Look Up All Descendants of a Class in Ruby

Look up all descendants of a class in Ruby

Here is an example:

class Parent
def self.descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end

class Child < Parent
end

class GrandChild < Child
end

puts Parent.descendants
puts Child.descendants

puts Parent.descendants gives you:

GrandChild
Child

puts Child.descendants gives you:

GrandChild

How to get all descendants of a class in Ruby using `self.inherited`?

class A
singleton_class.send(:attr_reader, :descendants)
@descendants = []
def self.inherited(subclass)
A.descendants << subclass
end
end

A.methods(false)
#=> [:inherited, :descendants, :descendants=]

class B < A; end
class C < B; end
class D < B; end
class E < C; end
class F < E; end

A.descendants
#=> [B, C, D, E, F]

Alternatively, you can use ObjectSpace#each_object to obtain A's descendants.

ObjectSpace.each_object(Class).select { |c| c < A }
#=> [F, E, D, C, B]

If you wish to obtain the ordering of

arr = [B, C, D, E, F]

you could write

(arr << A).each_with_object({}) { |c,h| h[c] =
arr.each_with_object([]) { |cc,a| a << cc if cc.superclass == c } }
#=> {B=>[C, D], C=>[E], D=>[], E=>[F], F=>[], A=>[B]}

How do I get a list of all subclasses without instantiating first in Ruby

This is an artefact of development mode only loading classes when first referenced: those files haven't been read by the interpreter yet - as far as ruby is concerned the classes genuinely do not exist yet

A workaround is to put

require_dependency "subclass_a"
require_dependency "subclass_b"
....

At the bottom the file for main class (outside the class definition)

How to check all classes under or using a class?

There's nothing built into the core Ruby language that will do what you want - you'll need to write your own. Here's an example method subclasses_of(class_name_here) (below) that will return a list of subclasses of a particular class for you:

class Mammal
end

class Human < Mammal
end

class Dog < Mammal
end

def subclasses_of input
ObjectSpace.each_object(Class).select { |klass| klass < input }
end

subclasses_of(Mammal)
#=> [Human, Dog]

Btw, there's an answer to this question here:

http://dzone.com/snippets/objectsubclasses

How to get all the descendants of an object in Ruby on Rails

I believe you're interpreting the ancestry gem incorrectly.
It looks like you expect Region to show up as a child of Country. Is that right?

Ancestry is useful for tree like structures (think folders -> sub folders -> child sub folders -> etc.)

Applying has_ancestry within your Country model would allow you to create countries within countries. But I don't believe that's what you're looking for.

Regardless, you can set up those relations by setting the "parent_id" attribute for a country when creating/updating it within the controller. Just set it to the ID of the parent country.
Same for regions if you want a tree structure of regions.

How can I get all defined classes?

p ObjectSpace.each_object(Class){|ob| p ob}

Ruby Object Model - ancestors of a class

The class A is an instance of Class, and you can see that via A.class

The class of an instance of A is A, and you access that via a = A.new; a.class

The method ancestors is showing the class hierarchy that an object of that class has (or would have) as its inheritance.

There are two parallel class hierarchy models going on in your example, and they only impinge on each other because Ruby represents its classes as objects for you to inspect and modify.

There is no fundamental reason to need A.class.ancestors and A.ancestors to intersect at all - except Ruby also has a deep class model with simple roots, so in practice that is what you'll see.

In fact I couldn't find any counter-example, even nil does this:

NilClass.ancestors
=> [NilClass, Object, Kernel, BasicObject]

NilClass.class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]

This one is more enlightening though:

BasicObject.ancestors
=> [BasicObject]

BasicObject.class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]

How do I get the parent's class name in Ruby

class A
end

class B < A
end

B.superclass # => A
B.superclass.name # => "A"

Loading class descendants in rails development

I found the following post: http://avinmathew.com/using-rails-descendants-method-in-development/

In short it says the following:

In enviroments/development.rb add the following:

config.eager_load_paths += Dir['path/to/files/*.rb']
ActionDispatch::Reloader.to_prepare do
Dir['path/to/files/*.rb'].each {|file| require_dependency file}
end

The first line adds the path that should be loaded when you start your app (or console)
and the rest tells rails to reload the classes on each request.

Nested singleton class method lookup

Much of this explanation is based on How Ruby Method Dispatch Works by James Coglan, a little of the Ruby Hacking Guide, and just a smidge of source.

To begin with a summary, the ancestry looks like this:

                                                           +----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>

---> Parent
~~~> Singleton class

Let's start from the beginning and build out. BasicObject is the root of everything - if you check BasicObject.superclass, you get nil. BasicObject is also an instance of Class. Yes, that gets circular, and there's a special case in the code to deal with it. When A is an instance of B, A.singleton_class is a child of B, so we get this:

                           Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>

Object inherits from BasicObject. When A inherits from B, A is a child of B and A.singleton_class is a child of B.singleton_class. Object also includes Kernel. When A includes B, B is inserted as the first ancestor of A (after A itself, but before A.superclass).

                           Class
^
|
BasicObject ~~~~~> #<Class:BasicObject
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>

Kernel is an instance of Module. It's the only instance of Module we'll see, and its singleton class doesn't appear in any ancestry chains, so I won't draw beyond it.

Now we get down to Foo, which inherits from Object (though you don't need to write < Object). We can already figure out what Foo and its singleton class are children of.

                           Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>

Now Class inherits from Module, and Module inherits from Object, so add Module and the appropriate singleton classes. Because Module < Object and Object < BasicObject and BasicObject.instance_of?(Class), this is where the drawing gets a little funky. Remember you just stop traversing upwards whenever you hit BasicObject.

                                                           +----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ |
| | |
| BasicObject ~~~~~> #<Class:BasicObject> |
| ^ ^ |
| | Kernel | |
| | ^ | |
| | | | +----------------------------------------+
| +-----+----+ | |
| | | v
+-------> Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>

Last step. Every instance of Class has a singleton_class (though it won't be instantiated until it's needed, or else you'd need more RAM). All of our singleton classes are instances of Class, so they have singleton classes. Watch out for this sentence: A class's singleton class's parent is the class's parent's singleton class. I don't know if there's a succinct way to state that as far as type systems go, and the Ruby source pretty much says it's just doing it for consistency in any case. So, when you ask for Foo.singleton_class.singleton_class, the language happily obliges you and propagates the necessary parents upward, leading finally to:

                                                           +----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>

If you start from any node in this graph and traverse depth-first, right to left (and stop at BasicObject, you get the node's ancestor chain, just like we wanted. And, we've built it up from some basic axioms, so we might just be able to trust it. Lacking trust, there are a couple interesting ways to verify the structure further.

Try looking at node.singleton_class.ancestors - node.ancestors for any node in the graph. This gives us the ancestors of the singleton class that are not the ancestors of the node itself, which eliminates some of the confusing redundancy in the list.

> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors
=> [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>,
#<Class:Class>, #<Class:Module>]

You can also verify any one parent with node.superclass.

> Foo.singleton_class.singleton_class.superclass
=> #<Class:#<Class:Object>>

And you can even verify that the object identity is all consistent, so there aren't anonymous classes popping up all over the place with no particular relationship to each other.

> def ancestor_ids(ancestors)
> ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")}
> end

> puts ancestor_ids(Foo.ancestors)
70165241815140 Foo
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject

> puts ancestor_ids(Foo.singleton_class.ancestors)
70165241815120 #<Class:Foo>
70165216039400 #<Class:Object>
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object # Same as Foo from here down
70165216040340 Kernel
70165216040540 BasicObject

> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors)
70165241980080 #<Class:#<Class:Foo>>
70165215986060 #<Class:#<Class:Object>>
70165215986040 #<Class:#<Class:BasicObject>>
70165216039440 #<Class:Class>
70165216039420 #<Class:Module>
70165216039400 #<Class:Object> # Same as Foo.singleton_class from here down
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject

And that, in a nutshell, is how you snipe a nerd.



Related Topics



Leave a reply



Submit