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?

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

Frederick Cheung wrote: