mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Reinstate Object.subclasses_of and Class#descendents for plugin compat.
This reverts commits7d312e54ba
,5f981ff029
,f85f5dfc8f
,245bfafe33
, andec7c642f5f
This commit is contained in:
parent
fa2ff95d4b
commit
d1938953f4
5 changed files with 149 additions and 0 deletions
|
@ -1,3 +1,4 @@
|
|||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/core_ext/class/inheritable_attributes'
|
||||
require 'active_support/core_ext/class/delegating_attributes'
|
||||
require 'active_support/core_ext/class/subclasses'
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
class Class #:nodoc:
|
||||
# Returns an array with the names of the subclasses of +self+ as strings.
|
||||
#
|
||||
# Integer.subclasses # => ["Bignum", "Fixnum"]
|
||||
def subclasses
|
||||
Class.subclasses_of(self).map { |o| o.to_s }
|
||||
end
|
||||
|
||||
def reachable? #:nodoc:
|
||||
eval("defined?(::#{self}) && ::#{self}.equal?(self)")
|
||||
end
|
||||
|
||||
# Rubinius
|
||||
if defined?(Class.__subclasses__)
|
||||
def descendents
|
||||
subclasses = []
|
||||
__subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents }
|
||||
subclasses
|
||||
end
|
||||
else
|
||||
# MRI
|
||||
begin
|
||||
ObjectSpace.each_object(Class.new) {}
|
||||
|
||||
def descendents
|
||||
subclasses = []
|
||||
ObjectSpace.each_object(class << self; self; end) do |k|
|
||||
subclasses << k unless k == self
|
||||
end
|
||||
subclasses
|
||||
end
|
||||
# JRuby
|
||||
rescue StandardError
|
||||
def descendents
|
||||
subclasses = []
|
||||
ObjectSpace.each_object(Class) do |k|
|
||||
subclasses << k if k < self
|
||||
end
|
||||
subclasses.uniq!
|
||||
subclasses
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Exclude this class unless it's a subclass of our supers and is defined.
|
||||
# We check defined? in case we find a removed class that has yet to be
|
||||
# garbage collected. This also fails for anonymous classes -- please
|
||||
# submit a patch if you have a workaround.
|
||||
def self.subclasses_of(*superclasses) #:nodoc:
|
||||
subclasses = []
|
||||
superclasses.each do |klass|
|
||||
subclasses.concat klass.descendents.select {|k| k.name.blank? || k.reachable?}
|
||||
end
|
||||
subclasses
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
require 'active_support/core_ext/class/subclasses'
|
||||
|
||||
class Object
|
||||
# Exclude this class unless it's a subclass of our supers and is defined.
|
||||
# We check defined? in case we find a removed class that has yet to be
|
||||
# garbage collected. This also fails for anonymous classes -- please
|
||||
# submit a patch if you have a workaround.
|
||||
def subclasses_of(*superclasses) #:nodoc:
|
||||
Class.subclasses_of(*superclasses)
|
||||
end
|
||||
end
|
29
activesupport/test/core_ext/class_test.rb
Normal file
29
activesupport/test/core_ext/class_test.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
require 'abstract_unit'
|
||||
require 'active_support/core_ext/class'
|
||||
|
||||
class A
|
||||
end
|
||||
|
||||
module X
|
||||
class B
|
||||
end
|
||||
end
|
||||
|
||||
module Y
|
||||
module Z
|
||||
class C
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ClassTest < Test::Unit::TestCase
|
||||
def test_retrieving_subclasses
|
||||
@parent = eval("class D; end; D")
|
||||
@sub = eval("class E < D; end; E")
|
||||
@subofsub = eval("class F < E; end; F")
|
||||
assert_equal 2, @parent.subclasses.size
|
||||
assert_equal [@subofsub.to_s], @sub.subclasses
|
||||
assert_equal [], @subofsub.subclasses
|
||||
assert_equal [@sub.to_s, @subofsub.to_s].sort, @parent.subclasses.sort
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
require 'abstract_unit'
|
||||
require 'active_support/time'
|
||||
require 'active_support/core_ext/object'
|
||||
require 'active_support/core_ext/class/subclasses'
|
||||
|
||||
class ClassA; end
|
||||
class ClassB < ClassA; end
|
||||
|
@ -39,6 +40,55 @@ class Foo
|
|||
include Bar
|
||||
end
|
||||
|
||||
class ClassExtTest < Test::Unit::TestCase
|
||||
def test_subclasses_of_should_find_nested_classes
|
||||
assert Class.subclasses_of(ClassK).include?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_should_not_return_removed_classes
|
||||
# First create the removed class
|
||||
old_class = Nested.class_eval { remove_const :ClassL }
|
||||
new_class = Class.new(ClassK)
|
||||
Nested.const_set :ClassL, new_class
|
||||
assert_equal "Nested::ClassL", new_class.name # Sanity check
|
||||
|
||||
subclasses = Class.subclasses_of(ClassK)
|
||||
assert subclasses.include?(new_class)
|
||||
assert ! subclasses.include?(old_class)
|
||||
ensure
|
||||
Nested.const_set :ClassL, old_class unless defined?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_should_not_trigger_const_missing
|
||||
const_missing = false
|
||||
Nested.on_const_missing { const_missing = true }
|
||||
|
||||
subclasses = Class.subclasses_of ClassK
|
||||
assert !const_missing
|
||||
assert_equal [ Nested::ClassL ], subclasses
|
||||
|
||||
removed = Nested.class_eval { remove_const :ClassL } # keep it in memory
|
||||
subclasses = Class.subclasses_of ClassK
|
||||
assert !const_missing
|
||||
assert subclasses.empty?
|
||||
ensure
|
||||
Nested.const_set :ClassL, removed unless defined?(Nested::ClassL)
|
||||
end
|
||||
|
||||
def test_subclasses_of_with_multiple_roots
|
||||
classes = Class.subclasses_of(ClassI, ClassK)
|
||||
assert_equal %w(ClassJ Nested::ClassL), classes.collect(&:to_s).sort
|
||||
end
|
||||
|
||||
def test_subclasses_of_doesnt_find_anonymous_classes
|
||||
assert_equal [], Class.subclasses_of(Foo)
|
||||
bar = Class.new(Foo)
|
||||
assert_nothing_raised do
|
||||
assert_equal [bar], Class.subclasses_of(Foo)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ObjectTests < Test::Unit::TestCase
|
||||
class DuckTime
|
||||
def acts_like_time?
|
||||
|
|
Loading…
Reference in a new issue