Search code examples
ruby-on-railsrubydependenciesactivesupportsti

Rails: Why don't the child classes get loaded when using class method to return attributes about child classes?


Hello I've been searching for a solution for this for a while. Using Rails 2.3.5

I have a parent class with several child classes and for the sake of not having a file that's 1500 lines long I have the child classes kept in a subdirectory of the app/models directory.

Up until recently when I viewed this post: here

I couldn't even get the child classes to load

Now I want access each child in a manner using the self.inherited class method like this:

class Project < ActiveRecord::Base

  CHILDREN = []
  def self.inherited(child)
    super
    CHILDREN << child
    puts "CHILDREN.inspect: #{CHILDREN.inspect}"
  end
  def self.valid_child_types
    CHILDREN.collect{ |child| child.project_type}
  end
end

Temporarily, I put some debug statements to get a better picture of how things are getting loaded. I fired up the console and noticed this behavior:

>> Project
require_or_load /Users/frankdrebin/Sites/cerp/app/models/project.rb
loading /Users/frankdrebin/Sites/cerp/app/models/project
require_or_load /Users/frankdrebin/Sites/cerp/app/models/status.rb
loading /Users/frankdrebin/Sites/cerp/app/models/status

=> Project(id: integer, url: string, deadline: date, state: string, type: string,  priority: integer, status_id: integer)
>> Project::CHILDREN
=> []
>> ArticleProject
require_or_load /Users/frankdrebin/Sites/cerp/app/models/projects/article_project.rb
loading /Users/frankdrebin/Sites/cerp/app/models/projects/article_project
CHILDREN.inspect: [ArticleProject(id: integer, url: string, deadline: date, state:  string, type: string, priority: integer, status_id: integer)]
require_or_load /Users/frankdrebin/Sites/cerp/vendor/gems/state_machine-  0.7.3/lib/state_machine.rb
loading /Users/frankdrebin/Sites/cerp/vendor/gems/state_machine-0.7.3/lib/state_machine

=> ArticleProject(id: integer, url: string, deadline: date, state: string, type: string,   priority: integer, status_id: integer)
>> Project::CHILDREN
=> [ArticleProject(id: integer, url: string, deadline: date, state: string, type: string,  priority: integer, status_id: integer)]
>> 

I am sure there are less elegant solutions to this, such as putting the Child Classes all back into one gigantic file but I'd like to avoid this if at all possible.

Thanks


Solution

  • You have all sorts of problems:

    1. CHILDREN is a constant in Ruby because it starts with a capital letter, you don't want that.
    2. Next if you change it to children it would then be a local variable but you need an instance variable for the instance of the definition of the parent class so you need to use @children.
    3. You need to make the @children availible at the class level (which is probably why you were trying the CHILDREN thing).

    Here's how you would do it:

    class Parent
    
      @children = []
    
      # Make the attr_reader for the class not an instance of the class
      class << self
        attr_reader :children
      end
    
      def self.inherited(child)
        puts "Parent inherited by child: #{child.inspect}"
        @children << child
        super
      end
    end
    
    class Child1 < Parent
    end
    puts "Child1 class created"
    
    class Child2 < Parent
    end
    puts "Child2 class created"
    
    c1 = Child1.new
    c2 = Child2.new
    
    puts "Parent.children: #{Parent.children}"
    

    Output:

    Parent inherited by child: Child1
    Child1 class created
    Parent inherited by child: Child2
    Child2 class created
    Parent.children: [Child1, Child2]