Class#descendants?

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

I found this: Class#descendants – qerub.se

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.

Jason L. wrote:

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.

On 12.08.2009 21:30, Jason L. wrote:

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

I found this: Class#descendants – qerub.se

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

Joel VanderWerf wrote:

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

Jason L. wrote:

Joel VanderWerf wrote:

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

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.)

Robert K. wrote:

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.

Robert K. wrote:

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:ineach_object’
from (irb):85
from :0

Is there a reason superclass is returning nil on cl?

On 12.08.2009 21:57, Jason L. wrote:

irb(main):087:1> end
NoMethodError: undefined method ancestors' for nil:NilClass from (irb):86 from (irb):85:ineach_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

Tony A. wrote:

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:

gist:166800 · GitHub

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

Oi, good catch.

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

On Wed, Aug 12, 2009 at 1:39 PM, Joel VanderWerf
[email protected]wrote:

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:

Workarounds for ActiveSupport included, yay!

On Wed, Aug 12, 2009 at 2:39 PM, Joel
VanderWerf[email protected] wrote:

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

On Thu, Sep 3, 2009 at 9:59 AM, Charles Oliver N.
[email protected]wrote:

~/projects/jruby âž” 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.

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

On Thu, Sep 3, 2009 at 12:16 AM, Charles Oliver
Nutter[email protected] wrote:

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 âž” 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

Tony A. wrote:

Oi, good catch.

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

Yes, if you’re willing to accept the monkeypatch on Class:

http://pastie.org/604847

On Thu, Sep 3, 2009 at 1:30 PM, Duck Typist
[email protected]wrote:

Yes, if you’re willing to accept the monkeypatch on Class:

http://pastie.org/604847

But this covers even fewer cases than the non-ObjectSpace example I gave
earlier:

Tony A. wrote:

On Thu, Sep 3, 2009 at 1:30 PM, Duck Typist
[email protected]wrote:

Yes, if you’re willing to accept the monkeypatch on Class:

http://pastie.org/604847

But this covers even fewer cases than the non-ObjectSpace example I gave
earlier:

gist:166800 · GitHub

Sorry, my bad. Guess so.

On Thu, Sep 3, 2009 at 3:30 PM, Duck
Typist[email protected] wrote:

Tony A. wrote:

Oi, good catch.

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

Yes, if you’re willing to accept the monkeypatch on Class:

http://pastie.org/604847

Try that code with p Object.descendants

Your inherited hook is too late.


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale