Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to find all the subclasses of a class?

2,841 views
Skip to first unread message

Horacio Sanson

unread,
Oct 24, 2005, 7:05:43 AM10/24/05
to

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


Robert Klemme

unread,
Oct 24, 2005, 7:28:03 AM10/24/05
to

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
>> subclasses
=> [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.

>> class Foo
>> def self.inherited(cl)
>> p cl
>> end
>> end
=> nil
>> class Bar < Foo
>> end
Bar
=> nil

Kind regards

robert

Sean O'Halpin

unread,
Oct 24, 2005, 7:40:06 AM10/24/05
to

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 = {}
ObjectSpace.each_object do |obj|
if Class == obj.class
if obj.ancestors.include? self
class_hash[obj] = true
end
end
end
class_hash.keys
end
end

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

Regards,

Sean


Sean O'Halpin

unread,
Oct 24, 2005, 7:48:47 AM10/24/05
to
Incorporating Robert's suggestions, this is a bit better:

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

Regards,

Sean


Gene Tani

unread,
Oct 24, 2005, 8:11:31 AM10/24/05
to
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

Kroeger, Simon (ext)

unread,
Oct 24, 2005, 8:30:43 AM10/24/05
to

> -----Original Message-----
> From: Sean O'Halpin [mailto:sean.o...@gmail.com]
> Sent: Monday, October 24, 2005 1:49 PM
> To: ruby-talk ML
> Subject: Re: How to find all the subclasses of a class?
>
> Incorporating Robert's suggestions, this is a bit better:
>
> class Module
> def subclasses
> classes = []
> ObjectSpace.each_object(Module) do |m|
> classes << m if m.ancestors.include? self
> end
> classes
> end
> end
>
> Regards,
>
> Sean

prettyfied:

require 'enumerator'
class Module
def subclasses
ObjectSpace.to_enum(:each_object, Module).select do |m|
m.ancestors.include?(self)
end
end
end

cheers

Simon


Daniel Schierbeck

unread,
Oct 24, 2005, 8:33:00 AM10/24/05
to
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

Pit Capitain

unread,
Oct 24, 2005, 8:50:37 AM10/24/05
to
Robert Klemme schrieb:

> 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


Robert Klemme

unread,
Oct 24, 2005, 9:58:00 AM10/24/05
to
Pit Capitain wrote:
> Robert Klemme schrieb:
>> 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

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

Kind regards

robert

Ara.T.Howard

unread,
Oct 24, 2005, 10:10:00 AM10/24/05
to

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
===============================================================================

nobu....@softhome.net

unread,
Oct 24, 2005, 11:13:59 AM10/24/05
to
Hi,

At Mon, 24 Oct 2005 20:05:43 +0900,
Horacio Sanson wrote in [ruby-talk:162270]:


> 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

FYI, http://www.rubyist.net/~nobu/ruby/factory.rb

--
Nobu Nakada


Pit Capitain

unread,
Oct 24, 2005, 12:47:06 PM10/24/05
to
Robert Klemme schrieb:

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

Same for me. I didn't know this either until today :-)

Regards,
Pit


Horacio Sanson

unread,
Oct 24, 2005, 11:28:33 PM10/24/05
to

Thanks for all the answers, this solution made my day....

Horacio

Monday 24 October 2005 21:30、Kroeger, Simon (ext) さんは書きました:
> m.ancestors.include?(self)

0 new messages