Search code examples
ruby-on-railsruby-on-rails-3ruby-1.9.3

rails3 loads my base class but doesn't evaluate my sub-classes when code changes


Quite complex, so I'll try to do it as simple as I can.

I have a rails application, and have a problem when I work in developement mode and change my code when the server is running.
I have the following models (with only the relevant code, if you think you need more - tell me and I'll add):

---- file : app/models/entity.rb ----
class Entity < ActiveRecord::Base

  @@str_to_ent = {}

  def self.with_type(given_type)
    given_type = given_type.to_sym
    if @@str_to_ent[given_type] and self!=@@str_to_ent[given_type]
      raise "Type #{given_type} was already associated by #{@@str_to_ent[given_type}"
    end
    @@str_to_ent[given_type] = self
  end

  def self.by_type(type)
    @@str_to_ent[type.to_sym]
  end

  # rest of code
end
Dir["#{File.dirname(__FILE__)}/*.rb"].each {|file| puts "requiring #{file}"; require file }


---- file : app/models/ip_entity.rb ----
class IpEntity < Entity
  with_type :ip

  # rest of code
end

---- file : app/models/phone_entity.rb ----
class PhoneEntity < Entity
  with_type :phone

  # rest of code
end

I want entities to be assigned with a type (string) for further lookups (in one of the controllers I need the map between the textual representation of an entity and the entity class itself).
Obviously I need all sub classes entities to be loaded, that's why I added the last line on entity.rb that loads all others. (also tried using an initializer, but it didn't help as it's not reloaded when the code changes).

My problem goes as follows:

  1. I start the server. From looking at some debug prints, I can see that all the other classes were loaded, and when I look at the @@str_to_ent map - it's initialized.
  2. I call the controller. everything is OK. I keep calling it with various arguments - everything is fine.
  3. I change any of the files (add a space line)
  4. From now on I'll get an error message saying that no entity was associated with the given type (Entity.by_type returned nil on a type that was previously initialized).

I do understand that when the code changes, rails (in development mode) re-evaluates the classes files. I could also see that in my debug prints. It's requiring the entity.rb class and therefore overrides @@str_to_ent with {}, but then although it requires 'ip_entity.rb', and 'phone_entity.rb', it seems that it doesn't actually evaluates them since Entity.with_type isn't called again and the @@str_to_ent hash is still empty. That's what causing the problem.

The only way I can overcome this, is to restart the server, but that makes the whole debugging process very annoying...

Can anyone help with a solution? What's the best-practice when I need to "configure" my classes somehow (pretty similar to "attr_accessible", or "validates_presence_of") but I need the actions of these methods to stay with me even when rails reload the class. (I don't have a problem with re-evaluating the classes, my problem is that it re-evaluates the base and skips the sub-classes).

Thanks,
Zach


Solution

  • If I correctly understand what you are trying to achieve is called Single Table Inheritance. It is Rails default behavior when implementing inheritance on models.

    Search this documentation for Single Table Inheritance.

    UPDATE

    I think using require_dependency should allow you to reload subclasses as well :

    class Entity
      ...
    end
    require_dependency 'ip_entity'
    require_dependency 'phone_entity'