Skip to content
This repository
Browse code

Reinstate Object.subclasses_of and Class#descendents for plugin compat.

This reverts commits 7d312e5, 5f981ff, f85f5df, 245bfaf, and ec7c642
  • Loading branch information...
commit d1938953f416ac945b591251567d51e30ee2a6a4 1 parent fa2ff95
Jeremy Kemper jeremy authored
1  activesupport/lib/active_support/core_ext/class.rb
... ... @@ -1,3 +1,4 @@
1 1 require 'active_support/core_ext/class/attribute_accessors'
2 2 require 'active_support/core_ext/class/inheritable_attributes'
3 3 require 'active_support/core_ext/class/delegating_attributes'
  4 +require 'active_support/core_ext/class/subclasses'
58 activesupport/lib/active_support/core_ext/class/subclasses.rb
... ... @@ -0,0 +1,58 @@
  1 +require 'active_support/core_ext/object/blank'
  2 +
  3 +class Class #:nodoc:
  4 + # Returns an array with the names of the subclasses of +self+ as strings.
  5 + #
  6 + # Integer.subclasses # => ["Bignum", "Fixnum"]
  7 + def subclasses
  8 + Class.subclasses_of(self).map { |o| o.to_s }
  9 + end
  10 +
  11 + def reachable? #:nodoc:
  12 + eval("defined?(::#{self}) && ::#{self}.equal?(self)")
  13 + end
  14 +
  15 + # Rubinius
  16 + if defined?(Class.__subclasses__)
  17 + def descendents
  18 + subclasses = []
  19 + __subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents }
  20 + subclasses
  21 + end
  22 + else
  23 + # MRI
  24 + begin
  25 + ObjectSpace.each_object(Class.new) {}
  26 +
  27 + def descendents
  28 + subclasses = []
  29 + ObjectSpace.each_object(class << self; self; end) do |k|
  30 + subclasses << k unless k == self
  31 + end
  32 + subclasses
  33 + end
  34 + # JRuby
  35 + rescue StandardError
  36 + def descendents
  37 + subclasses = []
  38 + ObjectSpace.each_object(Class) do |k|
  39 + subclasses << k if k < self
  40 + end
  41 + subclasses.uniq!
  42 + subclasses
  43 + end
  44 + end
  45 + end
  46 +
  47 + # Exclude this class unless it's a subclass of our supers and is defined.
  48 + # We check defined? in case we find a removed class that has yet to be
  49 + # garbage collected. This also fails for anonymous classes -- please
  50 + # submit a patch if you have a workaround.
  51 + def self.subclasses_of(*superclasses) #:nodoc:
  52 + subclasses = []
  53 + superclasses.each do |klass|
  54 + subclasses.concat klass.descendents.select {|k| k.name.blank? || k.reachable?}
  55 + end
  56 + subclasses
  57 + end
  58 +end
11 activesupport/lib/active_support/core_ext/object/extending.rb
... ... @@ -0,0 +1,11 @@
  1 +require 'active_support/core_ext/class/subclasses'
  2 +
  3 +class Object
  4 + # Exclude this class unless it's a subclass of our supers and is defined.
  5 + # We check defined? in case we find a removed class that has yet to be
  6 + # garbage collected. This also fails for anonymous classes -- please
  7 + # submit a patch if you have a workaround.
  8 + def subclasses_of(*superclasses) #:nodoc:
  9 + Class.subclasses_of(*superclasses)
  10 + end
  11 +end
29 activesupport/test/core_ext/class_test.rb
... ... @@ -0,0 +1,29 @@
  1 +require 'abstract_unit'
  2 +require 'active_support/core_ext/class'
  3 +
  4 +class A
  5 +end
  6 +
  7 +module X
  8 + class B
  9 + end
  10 +end
  11 +
  12 +module Y
  13 + module Z
  14 + class C
  15 + end
  16 + end
  17 +end
  18 +
  19 +class ClassTest < Test::Unit::TestCase
  20 + def test_retrieving_subclasses
  21 + @parent = eval("class D; end; D")
  22 + @sub = eval("class E < D; end; E")
  23 + @subofsub = eval("class F < E; end; F")
  24 + assert_equal 2, @parent.subclasses.size
  25 + assert_equal [@subofsub.to_s], @sub.subclasses
  26 + assert_equal [], @subofsub.subclasses
  27 + assert_equal [@sub.to_s, @subofsub.to_s].sort, @parent.subclasses.sort
  28 + end
  29 +end
50 activesupport/test/core_ext/object_and_class_ext_test.rb
... ... @@ -1,6 +1,7 @@
1 1 require 'abstract_unit'
2 2 require 'active_support/time'
3 3 require 'active_support/core_ext/object'
  4 +require 'active_support/core_ext/class/subclasses'
4 5
5 6 class ClassA; end
6 7 class ClassB < ClassA; end
@@ -39,6 +40,55 @@ class Foo
39 40 include Bar
40 41 end
41 42
  43 +class ClassExtTest < Test::Unit::TestCase
  44 + def test_subclasses_of_should_find_nested_classes
  45 + assert Class.subclasses_of(ClassK).include?(Nested::ClassL)
  46 + end
  47 +
  48 + def test_subclasses_of_should_not_return_removed_classes
  49 + # First create the removed class
  50 + old_class = Nested.class_eval { remove_const :ClassL }
  51 + new_class = Class.new(ClassK)
  52 + Nested.const_set :ClassL, new_class
  53 + assert_equal "Nested::ClassL", new_class.name # Sanity check
  54 +
  55 + subclasses = Class.subclasses_of(ClassK)
  56 + assert subclasses.include?(new_class)
  57 + assert ! subclasses.include?(old_class)
  58 + ensure
  59 + Nested.const_set :ClassL, old_class unless defined?(Nested::ClassL)
  60 + end
  61 +
  62 + def test_subclasses_of_should_not_trigger_const_missing
  63 + const_missing = false
  64 + Nested.on_const_missing { const_missing = true }
  65 +
  66 + subclasses = Class.subclasses_of ClassK
  67 + assert !const_missing
  68 + assert_equal [ Nested::ClassL ], subclasses
  69 +
  70 + removed = Nested.class_eval { remove_const :ClassL } # keep it in memory
  71 + subclasses = Class.subclasses_of ClassK
  72 + assert !const_missing
  73 + assert subclasses.empty?
  74 + ensure
  75 + Nested.const_set :ClassL, removed unless defined?(Nested::ClassL)
  76 + end
  77 +
  78 + def test_subclasses_of_with_multiple_roots
  79 + classes = Class.subclasses_of(ClassI, ClassK)
  80 + assert_equal %w(ClassJ Nested::ClassL), classes.collect(&:to_s).sort
  81 + end
  82 +
  83 + def test_subclasses_of_doesnt_find_anonymous_classes
  84 + assert_equal [], Class.subclasses_of(Foo)
  85 + bar = Class.new(Foo)
  86 + assert_nothing_raised do
  87 + assert_equal [bar], Class.subclasses_of(Foo)
  88 + end
  89 + end
  90 +end
  91 +
42 92 class ObjectTests < Test::Unit::TestCase
43 93 class DuckTime
44 94 def acts_like_time?

2 comments on commit d193895

Joshua Peek
Collaborator

Why not put deprecation notices on those methods?

Jeremy Kemper
Owner

Just reverting since it broke some plugins and the 3.0 upgrade should be smoother.

Please sign in to comment.
Something went wrong with that request. Please try again.