How to find all the subclasses of a class?

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

Horacio Sanson wrote:

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

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

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

···

On 10/24/05, Horacio Sanson <hsanson@moegi.waseda.jp> wrote:

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

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

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

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

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

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

Sean O'Halpin wrote:

···

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

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

pit that's insanely cool - thanks!

-a

···

On Mon, 24 Oct 2005, 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

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

--

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

===============================================================================

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 :slight_smile:

Regards,
Pit