aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2010-02-09 18:03:14 -0800
committerJeremy Kemper <jeremy@bitsweat.net>2010-02-09 18:20:57 -0800
commitd1938953f416ac945b591251567d51e30ee2a6a4 (patch)
tree9f0b99f6d01a6e955a1a2a7a42ef4b68cee52478 /activesupport
parentfa2ff95d4b783751f42378447331385c469e4775 (diff)
downloadrails-d1938953f416ac945b591251567d51e30ee2a6a4.tar.gz
rails-d1938953f416ac945b591251567d51e30ee2a6a4.tar.bz2
rails-d1938953f416ac945b591251567d51e30ee2a6a4.zip
Reinstate Object.subclasses_of and Class#descendents for plugin compat.
This reverts commits 7d312e54bad9c39634c137caec07dfc8df471650, 5f981ff0294ba45aa44ad15aa063970b29aeec44, f85f5dfc8ffefff174b695c6363211d342f77a57, 245bfafe335ff883f7a096eab95ac22fe2848679, and ec7c642f5fe60afc857aa64f1a9b4c2be56f9d70
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/lib/active_support/core_ext/class.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/class/subclasses.rb58
-rw-r--r--activesupport/lib/active_support/core_ext/object/extending.rb11
-rw-r--r--activesupport/test/core_ext/class_test.rb29
-rw-r--r--activesupport/test/core_ext/object_and_class_ext_test.rb50
5 files changed, 149 insertions, 0 deletions
diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb
index 62df7d8b82..f2ca9c7cc9 100644
--- a/activesupport/lib/active_support/core_ext/class.rb
+++ b/activesupport/lib/active_support/core_ext/class.rb
@@ -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'
diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb
new file mode 100644
index 0000000000..c166ce8079
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb
@@ -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
diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb
new file mode 100644
index 0000000000..c4c37b6a2a
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/extending.rb
@@ -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
diff --git a/activesupport/test/core_ext/class_test.rb b/activesupport/test/core_ext/class_test.rb
new file mode 100644
index 0000000000..b7f3dd9930
--- /dev/null
+++ b/activesupport/test/core_ext/class_test.rb
@@ -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
diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb
index 0b2a9c418e..f31e7774e9 100644
--- a/activesupport/test/core_ext/object_and_class_ext_test.rb
+++ b/activesupport/test/core_ext/object_and_class_ext_test.rb
@@ -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?