Class#descendants?

J

Jason Lillywhite

Ruby can do Class#ancestors but not Class#descendants.

I found this: http://termos.vemod.net/class-descendants

But it only works after you define a new inheritance. Is there a way to
find descendants of a class that is already in place? For example:

Numeric.descendants
=> [Integer, Float, Complex]

Thank you.
 
J

Joel VanderWerf

Jason said:
Ruby can do Class#ancestors but not Class#descendants.

I found this: http://termos.vemod.net/class-descendants

But it only works after you define a new inheritance. Is there a way to
find descendants of a class that is already in place? For example:

Numeric.descendants
=> [Integer, Float, Complex]

Thank you.

irb(main):001:0> ObjectSpace.each_object(Class) {|cl| p cl if cl < Numeric}
Bignum
Float
Fixnum
Integer

But keep in mind: it's inefficient (so cache if you can), and it may not
be standard across rubies (jruby may not have it IIRC). It's useful for
interactive poking around though.
 
R

Robert Klemme

Ruby can do Class#ancestors but not Class#descendants.

I found this: http://termos.vemod.net/class-descendants

But it only works after you define a new inheritance. Is there a way to
find descendants of a class that is already in place? For example:

Numeric.descendants
=> [Integer, Float, Complex]

Let me first state that I believe this is intentional: while sub classes
need knowledge of their parent classes the opposite is not true. In
fact, that would create a kind of circular dependency.

Having said that, this is what you could do

require 'set'
h = Hash.new {|h,k| h[k] = Set.new}

ObjectSpace.each_object(Class) do |cl|
cl.superclass.ancestors.each {|scl| h[scl] << cl}
end

p h[String]

You should keep in mind though that this is by no means static. New
classes can come in existence at any time.

Kind regards

robert
 
J

Jason Lillywhite

Joel said:
irb(main):001:0> ObjectSpace.each_object(Class) {|cl| p cl if cl <
Numeric}

Thank you. Why does it return 387 after I do this in IRB?

like this:
irb(main):069:0> ObjectSpace.each_object(Class) {|cl| p cl if cl < Date}
DateTime
=> 387
 
J

Jason Lillywhite

Robert said:
You should keep in mind though that this is by no means static. New
classes can come in existence at any time.

This is great. Thank you.

I really just want to play around for educational purposes to get a feel
for the Ruby class hierarchy.
 
J

Jason Lillywhite

Robert said:
ObjectSpace.each_object(Class) do |cl|
cl.superclass.ancestors.each {|scl| h[scl] << cl}
end

I'm getting this error:

irb(main):085:0> ObjectSpace.each_object(Class) do |cl|
irb(main):086:1* cl.superclass.ancestors.each {|scl| h[scl] << cl}
irb(main):087:1> end
NoMethodError: undefined method `ancestors' for nil:NilClass
from (irb):86
from (irb):85:in `each_object'
from (irb):85
from :0

Is there a reason superclass is returning nil on cl?
 
J

Joel VanderWerf

Jason said:
Thank you. Why does it return 387 after I do this in IRB?

like this:
irb(main):069:0> ObjectSpace.each_object(Class) {|cl| p cl if cl < Date}
DateTime
=> 387

It's the number of instances of Class (since that was the argument). So
another example is:

irb(main):002:0> ObjectSpace.each_object(String) {}
=> 4613

(Yep, the empty block is necessary.)
 
R

Robert Klemme

Robert said:
ObjectSpace.each_object(Class) do |cl|
cl.superclass.ancestors.each {|scl| h[scl] << cl}
end

I'm getting this error:

irb(main):085:0> ObjectSpace.each_object(Class) do |cl|
irb(main):086:1* cl.superclass.ancestors.each {|scl| h[scl] << cl}
irb(main):087:1> end
NoMethodError: undefined method `ancestors' for nil:NilClass
from (irb):86
from (irb):85:in `each_object'
from (irb):85
from :0

Is there a reason superclass is returning nil on cl?

Oops, I inserted "superclass" after doing the test. This is likely the
case of Object. If you remove it it works as well. Sorry, for the hassle.

Kind regards

robert
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

irb(main):001:0> ObjectSpace.each_object(Class) {|cl| p cl if cl < Numeric}

For what it's worth, here's my implementation, which doesn't use
ObjectSpace. It's a bit less trivial and I wonder if it could be made
better:

http://gist.github.com/166800

Workarounds for ActiveSupport included, yay!
 
J

Joel VanderWerf

Tony said:
For what it's worth, here's my implementation, which doesn't use
ObjectSpace. It's a bit less trivial and I wonder if it could be made
better:

http://gist.github.com/166800

Workarounds for ActiveSupport included, yay!

Not all classes are constants...

my_numbers = Class.new(Numeric)

p Numeric.descendants
a = []
ObjectSpace.each_object(Class) {|cl| a << cl if cl <= Numeric}
p a
 
T

Tony Arcieri

[Note: parts of this message were removed to make it a legal post.]

Oi, good catch.

So the question remains... can this be done without ObjectSpace?

Tony said:
For what it's worth, here's my implementation, which doesn't use
ObjectSpace. It's a bit less trivial and I wonder if it could be made
better:

http://gist.github.com/166800

Workarounds for ActiveSupport included, yay!

Not all classes are constants...

my_numbers = Class.new(Numeric)

p Numeric.descendants
a = []
ObjectSpace.each_object(Class) {|cl| a << cl if cl <= Numeric}
p a
 
C

Charles Oliver Nutter

irb(main):001:0> ObjectSpace.each_object(Class) {|cl| p cl if cl < Numeric}
Bignum
Float
Fixnum
Integer

But keep in mind: it's inefficient (so cache if you can), and it may not be
standard across rubies (jruby may not have it IIRC). It's useful for
interactive poking around though.

This is actually the one each_object case we do support with general
ObjectSpace support turned off, since we maintain a weak reference
from parent to child classes (so we just walk everything from Object
down; it's for our method-caching logic).

- Charlie
 
C

Charles Oliver Nutter

This is actually the one each_object case we do support with general
ObjectSpace support turned off, since we maintain a weak reference
from parent to child classes (so we just walk everything from Object
down; it's for our method-caching logic).

Oh, I suppose I should also mention this:

~/projects/jruby =E2=9E=94 jruby -rjruby/core_ext -e "p Numeric.subclasses(=
true)"
[Precision, Precision, Integer, Bignum, Fixnum, Float]

It doesn't filter out the "included wrapper" from included modules,
but it's there. I suggested it as an official addition to Ruby, but
nobody seemed interested in the idea at the time.

- Charlie
 
T

Tony Arcieri

~/projects/jruby =E2=9E=94 jruby -rjruby/core_ext -e "p Numeric.subclasse= s(true)"
[Precision, Precision, Integer, Bignum, Fixnum, Float]

It doesn't filter out the "included wrapper" from included modules,
but it's there. I suggested it as an official addition to Ruby, but
nobody seemed interested in the idea at the time.

My two cents: I would love to see this as an official addition to Ruby.

--=20
Tony Arcieri
Medioh/Nagravision
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top