Rails: STI, inheritance and reload! bug in development environment

Created on 2 Jan 2013  ·  3Comments  ·  Source: rails/rails

I'm reopening issue #868 as it it still present today.

I've used ruby-1.9.3-p362 and rails 3.2.9

The only solution is to require child classes or to use config.cache_classes = true

> Support.all
 => [] 
> Employee
 => Employee(id: integer, type: string) 
> Administrator
 => Administrator(id: integer, type: string) 
> Support.all
 => [#<Administrator id: 1, type: "Administrator">, #<Employee id: 2, type: "Employee">] 
activerecord

Most helpful comment

This is known limitation of using STI in development mode with lazy loading of classes. You need to use require_dependency to make Rails aware of those subclasses, e.g:

# app/models/support.rb
class Support < User
end

require_dependency 'administrator'
require_dependency 'employee'

If you have a lot of these subclasses then you can use Dir[] and a special folder in app to load them all, e.g:

# app/models/user.rb
class User < ActiveRecord::Base
end

Dir["#{Rails.root}/app/users/*.rb"].each do |file|
  require_dependency file
end

Or a third alternative is to hard code the list in Model.descendants, e.g:

# app/models/user.rb
class User < ActiveRecord::Base
  def self.descendants
    [Support, Administrator, Employee]
  end
end

I haven't used the last one personally but it should work.

All 3 comments

I've reproduce it on rails 4

Rails 4.0.0.beta
Ruby 1.9.3p194

1.9.3p194 :001 > Administrator.create

1.9.3p194 :003 > Employee.create

1.9.3p194 :008 > Support.all
  Support Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."type" IN ('Support', 'Administrator', 'Employee')
 => #<ActiveRecord::Relation [#<Administrator id: 1, type: "Administrator">, #<Employee id: 2, type: "Employee">]> 

1.9.3p194 :009 > reload!
Reloading...
 => true 

1.9.3p194 :010 > Support.all
  Support Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."type" IN ('Support')
 => #<ActiveRecord::Relation []> 

This is known limitation of using STI in development mode with lazy loading of classes. You need to use require_dependency to make Rails aware of those subclasses, e.g:

# app/models/support.rb
class Support < User
end

require_dependency 'administrator'
require_dependency 'employee'

If you have a lot of these subclasses then you can use Dir[] and a special folder in app to load them all, e.g:

# app/models/user.rb
class User < ActiveRecord::Base
end

Dir["#{Rails.root}/app/users/*.rb"].each do |file|
  require_dependency file
end

Or a third alternative is to hard code the list in Model.descendants, e.g:

# app/models/user.rb
class User < ActiveRecord::Base
  def self.descendants
    [Support, Administrator, Employee]
  end
end

I haven't used the last one personally but it should work.

I have this kind of problem and can't get it working. My models are A, B (and more) < C, D (and more) < B.
After a code change, I get "uninitialized constant B". Tried require_dependency in a couple of ways, but don't really know 1) which class(es) to require (B?), and 2) where to do it.
These models are associated with other models in several ways, this might have an impact on the load order too (e.g. A belongs_to X, X has_many Bs and Cs)

Was this page helpful?
0 / 5 - 0 ratings