I have a Rails app representing a card game. There are several different types of card, and a few expansions for the game, each providing more cards. So I'm using a single cards
table and STI, with each expansion having a models subdirectory and appropriately-named module. Like this:
app/models
- card.rb
+ base_game
- foo.rb
- class BaseGame::Foo < Card
- bar.rb
- class BaseGame::Bar < Card
+ exp_one
- thing.rb
- class ExpOne::Thing < Card
This works well.
I would like to be able to get a list of all the cards in each expansion. The constants
method of Module
provides a neat way of doing this -
class Card
def self.base_game_cards
BaseGame.constants.map {|c| ("BaseGame::" + c).constantize}.select {|c| c.instance_of?(Class) and c.superclass == Card}
end
end
...except that Rails only loads a given model definition when it's first referenced, so Card.base_game_cards
is usually []
.
I've tried working round this by forcing a preload of all the models, by adding a file into config/initializers:
preloader.rb:
# Preload all card models from all expansions. This allows us to dynamically
# determine card types by reflection.
Dir.glob("#{RAILS_ROOT}/app/models/*/*.rb").each do |file|
require file
end
But this doesn't seem to work either, and I can't work out why. By use of raise
and inpect
, I've determined that when I call Card.base_game_cards
from a controller, the result is still usually []
. Very occasionally it's correct just after server restart, but refreshing the page sends it back to []
. If I use script\console, the list is always correct. I've tried moving the block of requires to the top of the controller class definition, but that makes no difference either.
Now, I'm perfectly willing at this stage to go for another solution (probably just inflecting the list of filenames returned by glob
), but I'd at least like to know why this method isn't working. Can anyone shed some light (or - even better - make it work)?