9

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?

fearless_fool
  • 33,645
  • 23
  • 135
  • 217

3 Answers3

9

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.

behe
  • 1,368
  • 1
  • 9
  • 5
  • 1
    This also still falls out of memory after a few minutes, when just loaded via initializer. – Todd Dec 01 '16 at 21:21
  • This seems to be working for me for namespaced models: `Dir[Rails.root + 'app/models/**/*.rb'].map { |f| File.path(f).to_s.sub(Rails.root.to_s + '/app/models/', '').chomp('.rb').camelize.constantize if !File.path(f).to_s.include?('/concerns/') }`, although I'm not sure if excluding concerns means models that should be loaded aren't loaded. It's not an issue in my current case. – eggroll Jan 28 '17 at 22:03
8

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 MyModelagain (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).

Wam
  • 810
  • 1
  • 8
  • 19
  • +1 for the lucid explanation of what is going on -- thank you. Unfortunately, my sub-classes have "acronym" names, like "PGEService" with filenames like 'pge_service.rb'. I'm not ready to rename my files to 'p_g_e_service.rb' -- at least not yet! – fearless_fool May 07 '11 at 04:30
  • Well, if your files are named PGEService.rb, you can just use the file name (without the `.classify`), but I don't see any easy solution if it's still `pge_service.rb`... Unless of course you do something like `s=File.basename(f,'.rb').split('_'); class_name = s.first.upcase + s.last.classify; MyModels.const_get(class_name)`. Yeah, a bit ugly, I know. – Wam May 08 '11 at 16:45
  • strictly speaking, you DID answer my question of "what's going on". So you deserve the checkmark. I have refined my question, but I'll post that as a separate question. Onwards! – fearless_fool May 25 '11 at 03:43
  • Actually I've changed a bit the code in my model, because I would get wrong stuff when loading Subclasses : If I load `SubModel` when `MyModel` hasn't been loaded, Rails find at the `class SubModel < Model` line that it needs to load `MyModel`, which in turn requires my submodel file ... infinite loop. The solution is to directly have the snippet above in the descendants method. – Wam May 26 '11 at 20:05
7

I just enabled eager loading in each environment:

config.eager_load = true

This worked for me even when using namespaces for class names.

mscriven
  • 409
  • 4
  • 10
  • I *think* this will be the winning answer. Can someone else verify that this works? – fearless_fool Jun 17 '15 at 04:37
  • 1
    This is a false positive, it will work for a few minutes, then fall out of memory. Whatever page youre using this feature with - just refresh enough, and eventually, the `descendants` or `subclasses` will start returning blank again. – Todd Dec 01 '16 at 21:22