Find every subclass of an object

Hi,

I have this class:

class MoldItem < ActiveRecord::Base
self.abstract_class = true
end

And then I have a (currenly unspecified) number of classes such as
these:

class StringMoldItem < MoldItem
end
class LinkMoldItem < MoldItem
end

I would like to add a class method to MoldItem that will return all of
its subclasses. In the above example, this method would return the
Array [StringMoldItem, LinkMoldItem].

I have been able to write this method in several ways that are
successful using script/console. But as soon as I try to use the
method within my browser (i.e. calling it in a controller) I get
unexpected results.

Option 1:
def self.subklasses(direct = false)
#ensure files are loaded by rails
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require
file }

classes = []
if direct
  ObjectSpace.each_object(Class) do |c|
    next unless c.superclass == self
    classes << c
  end
else
  ObjectSpace.each_object(Class) do |c|
    next unless c.ancestors.include?(self) and (c != self)
    classes << c
  end
end
classes

end

Option 2:
def self.subklasses
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require
file }
@@subclasses[MoldItem]
end

Option 3:

def self.inherited other
super if defined? super
ensure
( @subclasses ||= [] ).push(other).uniq!
end

def self.subklasses
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require
file }
@subclasses ||= []
@subclasses.inject( [] ) do |list, subclass|
list.push(subclass, *subclass.subklasses)
end
end

Results: when I run MoldItem.subklasses using script/console, I get
the correct result for all three options. When I run it from a
controller (by visiting a page of my website), the first request after
starting mongrel works. However, every subsequent request will return
[] or nil (depending on the option).

I think the problem is a conflict between this line:
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require file }
(which is meant to ensure that the subclasses such as StringMoldItem
and LinkMoldItem exist) and rails’ fancy way of requiring source files
(would that be ActiveSupport::Dependencies#load_missing_constant?).

Do you have any suggestions for how I can dynamically return a list of
the subclasses of MoldItem?

On 14 Sep 2008, at 21:05, DyingToLearn wrote:

I think the problem is a conflict between this line:
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require file }
(which is meant to ensure that the subclasses such as StringMoldItem
and LinkMoldItem exist) and rails’ fancy way of requiring source files
(would that be ActiveSupport::Dependencies#load_missing_constant?).

Probably. Using require_dependency instead require might help.

Fred

Thanks!

I just needed to change this:
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file| require file }

to this:
Dir.glob(RAILS_ROOT + ‘/app/models/*.rb’).each { |file|
require_dependency file }

Now I just need to think of some way to not require reading the entire
directory contents every time the method is called :slight_smile:

Paul