mirror of https://github.com/rails/rails
Ruby 3.1: Handle `Class#subclasses` existing without `Class#descendants`
Since `#subclasses` was introduced a bit after `#descendants`, the feature testing code assumed that if the former was present, the later would be too. However it was decided to revert `#descendants` for now, but to keep `#subclasses` https://bugs.ruby-lang.org/issues/14394#note-33 Since this was totally unexpected, the `#descendants` core_ext and `DescendantsTracker#descendants` are broken on Active Support 7.0.0 / Ruby 3.1.0-dev. We now test the existence of both methods to handle this new situation. Since for now it's planned for `#descendants` to make it back in Ruby 3.2, we keep checking for it.
This commit is contained in:
parent
2ea5268cd2
commit
68e9df0257
|
@ -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.
|
||||
|
|
|
@ -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+.
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue