How to find all the subclasses of a class?

H

Horacio Sanson

I want to be able to list and instatiate all subclasses of a class to create a
Factory type pattern, I want to be able to do something like


subclasses = Array.new

## Magic code that puts all subclasses of a class in
## the subclasses array
subclasses = get_subclasses( .... )

subclasses.each { |subclass|
return subclass.new if condition == true
end


sorry I am very new to Ruby and have no idea on how to do this.
any help is appreciated.


Horacio
 
R

Robert Klemme

Horacio said:
I want to be able to list and instatiate all subclasses of a class to
create a Factory type pattern, I want to be able to do something like


subclasses = Array.new

## Magic code that puts all subclasses of a class in
## the subclasses array
subclasses = get_subclasses( .... )

subclasses.each { |subclass|
return subclass.new if condition == true
end


sorry I am very new to Ruby and have no idea on how to do this.
any help is appreciated.

There are two possible approaches:

1. Find them when you need them via ObjectSpace:
cl = Enumerable => Enumerable
subclasses = [] => []
ObjectSpace.each_object(Module) {|m| subclasses << m if
m.ancestors.include? cl}
=> 371=> [Struct::Tms, Dir, File, IO, Range, Struct, Hash, Array, String,
Enumerable]

2. Record them when they are created.

This is typically done by a variant that uses Class#inherited.
Bar
=> nil

Kind regards

robert
 
S

Sean O'Halpin

I want to be able to list and instatiate all subclasses of a class to cre= ate a
Factory type pattern, I want to be able to do something like

subclasses =3D Array.new

## Magic code that puts all subclasses of a class in
## the subclasses array
subclasses =3D get_subclasses( .... )

subclasses.each { |subclass|
return subclass.new if condition =3D=3D true
end


sorry I am very new to Ruby and have no idea on how to do this.
any help is appreciated.


Horacio

Something like this should get you started (though don't expect it to
be efficient - it basically queries every object in the system to find
the subclasses):

class A
end

class B < A
end

class C < B
end

class Class
def subclasses
class_hash =3D {}
ObjectSpace.each_object do |obj|
if Class =3D=3D obj.class
if obj.ancestors.include? self
class_hash[obj] =3D true
end
end
end
class_hash.keys
end
end

p A.subclasses
#=3D> [C, B, A]

Regards,

Sean
 
S

Sean O'Halpin

Incorporating Robert's suggestions, this is a bit better:

class Module
def subclasses
classes =3D []
ObjectSpace.each_object(Module) do |m|
classes << m if m.ancestors.include? self
end
classes
end
end

Regards,

Sean
 
G

Gene Tani

FWIW last summer we made a list of the 10 methods you can use to test
inheritance / mixed-in or extendedness:

Object#kind_of?, #is_a?, #instance_of?, #type (deprecated), #class

Module#ancestors, #included_modules

Class#inherited, #superclass

MyClassname === myobj
 
D

Daniel Schierbeck

This will save an array of subclasses in a class attribute named
`subclasses'. It may not be the most efficient way of doing it, but it
works.

class Class
def inherited(subclass)
if superclass.respond_to? :inherited
superclass.inherited(subclass)
end

@subclasses ||= []
@subclasses << subclass
end

def subclasses
@subclasses
end
end

class SuperClass; end

class A < SuperClass; end
class B < SuperClass; end
class C < SuperClass; end
class D < A; end

puts SuperClass.subclasses.join(", ") # A, B, C, D

If you only need one superclass, this is more efficient:

class MyClass
def self.inherited(subclass)
if superclass.respond_to? :inherited
superclass.inherited(subclass)
end

@subclasses ||= []
@subclasses << subclass
end

def self.subclasses
@subclasses
end
end


Cheers,
Daniel
 
P

Pit Capitain

Robert said:
1. Find them when you need them via ObjectSpace:

ObjectSpace.each_object(Module) {|m| subclasses << m if m.ancestors.include? cl}

If you want to find subclasses of a *class* instead of a module, you can
pass the singleton class of the base class:

require "enumerator"

def get_subclasses(klass)
ObjectSpace.enum_for:)each_object, class << klass; self; end).to_a
end

Horacio, if you need help to decipher this code, feel free to ask.

Regards,
Pit
 
R

Robert Klemme

Pit said:
If you want to find subclasses of a *class* instead of a module, you
can pass the singleton class of the base class:

require "enumerator"

def get_subclasses(klass)
ObjectSpace.enum_for:)each_object, class << klass; self;
end).to_a end

Cool! Didn't know this. Learn something new every day... Thanks!

Kind regards

robert
 
A

Ara.T.Howard

If you want to find subclasses of a *class* instead of a module, you can pass
the singleton class of the base class:

require "enumerator"

def get_subclasses(klass)
ObjectSpace.enum_for:)each_object, class << klass; self; end).to_a
end

Horacio, if you need help to decipher this code, feel free to ask.

pit that's insanely cool - thanks!

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| anything that contradicts experience and logic should be abandoned.
| -- h.h. the 14th dalai lama
===============================================================================
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top