I want to display a selection list of MyModel subclasses in a view. It's not working yet, so for sanity checking, I included this in my view:
<%= MyModel.descendants %>
The first time I render this page after re-starting the server, it shows the list of descendants (there are six). All subsequent times, it shows up as an empty list []
.
FWIW, I have a require
statement in my initializers:
Dir[Rails.root.join("app/models/my_models/**/*.rb").to_s].each {|f| require f}
... and I've verified that they're getting required.
What the @($%& is going on?
When you use require, even if your my_model.rb
gets reloaded, the kernel won't require your subclasses .rb
files because they have already been loaded. You'd have to go through rails autoload.
Basically, on your first request, rails autoloads MyModel
from my_model.rb
, which then requires my_models/sub_model.rb
. The SubModel
class inherits MyModel
, which populates the descendants
array.
On your following requests, though, rails autoloads MyModel
again (hey, you're in dev mode), which then requires my_models/sub_model.rb
again. But this time, the kernel knows it had already loaded this file and won't load it again.
I ran into this problem one hour ago, which lead me to your post, and to find a solution. What we need is rails to autoload subclasses everytime your main class is called.
Here is a solution :
class MyModel
Dir[File.join(File.dirname(__FILE__),"my_models","*.rb")].each do |f|
MyModels.const_get(File.basename(f,'.rb').classify)
end
end
These lines could probably be put outside of the class. That should be enough (it is for me) if you only have files in my_models
and not in subdirectories. If you have some (for example MyModels::Car::Ford
, you may need to put the same kind of stuff in the submodules (in my_models/car.rb
).
I had the same problem. Solved it by adding a config/initializers/preload_models.rb
with:
Dir[Rails.root + 'app/models/*.rb'].map {|f| File.basename(f, '.*').camelize.constantize }
Hope that helps somebody.
I just enabled eager loading in each environment:
config.eager_load = true
This worked for me even when using namespaces for class names.