Merge pull request #43951 from Shopify/ruby-head-handle-descendants-revert

Ruby 3.1: Handle `Class#subclasses` existing without `Class#descendants`
This commit is contained in:
Jean Boussier 2021-12-21 11:59:28 +01:00 committed by GitHub
commit 58c0b76982
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 22 deletions

View File

@ -1,3 +1,8 @@
* Fix `Class#descendants` and `DescendantsTracker#descendants` compatibility with Ruby 3.1.
[The native `Class#descendants` was reverted prior to Ruby 3.1 release](https://bugs.ruby-lang.org/issues/14394#note-33),
but `Class#subclasses` was kept, breaking the feature detection.
*Jean Boussier*
Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/activesupport/CHANGELOG.md) for previous changes.

View File

@ -3,24 +3,32 @@
require "active_support/ruby_features"
class Class
# Returns an array with all classes that are < than its receiver.
#
# class C; end
# C.descendants # => []
#
# class B < C; end
# C.descendants # => [B]
#
# class A < B; end
# C.descendants # => [B, A]
#
# class D < C; end
# C.descendants # => [B, A, D]
def descendants
ObjectSpace.each_object(singleton_class).reject do |k|
k.singleton_class? || k == self
unless ActiveSupport::RubyFeatures::CLASS_DESCENDANTS
if ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
def descendants
subclasses.concat(subclasses.flat_map(&:descendants))
end
else
# Returns an array with all classes that are < than its receiver.
#
# class C; end
# C.descendants # => []
#
# class B < C; end
# C.descendants # => [B]
#
# class A < B; end
# C.descendants # => [B, A]
#
# class D < C; end
# C.descendants # => [B, A, D]
def descendants
ObjectSpace.each_object(singleton_class).reject do |k|
k.singleton_class? || k == self
end
end
end
end unless ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
end
# Returns an array with the direct children of +self+.
#

View File

@ -51,7 +51,7 @@ module ActiveSupport
unless @clear_disabled
@clear_disabled = true
remove_method(:subclasses)
remove_method(:descendants)
remove_method(:descendants) if RubyFeatures::CLASS_DESCENDANTS
@@excluded_descendants = nil
end
end
@ -86,10 +86,16 @@ module ActiveSupport
subclasses
end
def descendants
descendants = super
descendants.reject! { |d| @@excluded_descendants[d] }
descendants
if RubyFeatures::CLASS_DESCENDANTS
def descendants
descendants = super
descendants.reject! { |d| @@excluded_descendants[d] }
descendants
end
else
def descendants
children.concat(children.flat_map(&:descendants))
end
end
def direct_descendants

View File

@ -3,5 +3,6 @@
module ActiveSupport
module RubyFeatures # :nodoc:
CLASS_SUBCLASSES = Class.method_defined?(:subclasses) # RUBY_VERSION >= "3.1"
CLASS_DESCENDANTS = Class.method_defined?(:descendants)
end
end