Rails中的反射问题

时间:2010-08-02 16:09:45

标签: ruby-on-rails ruby reflection lazy-loading

我有一个代表纸牌游戏的Rails应用程序。有几种不同类型的卡,以及游戏的一些扩展,每个都提供更多的卡。所以我使用了一个cards表和STI,每个扩展都有一个models子目录和适当命名的模块。像这样:

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

这很有效。

我希望能够获得每个扩展中所有卡的列表。 constants Module方法提供了一种巧妙的方法 -

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

...除了Rails只在第一次引用时加载给定的模型定义,因此Card.base_game_cards通常是[]

我尝试通过在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

但这似乎也不起作用,我无法弄明白为什么。通过使用raiseinpect,我确定当我从控制器调用Card.base_game_cards时,结果通常仍为[]。服务器重启后,偶尔会出现错误,但刷新页面会将其发送回[]。如果我使用script \ console,列表总是正确的。我已经尝试将需求块移动到控制器类定义的顶部,但这也没有区别。

现在,我非常愿意在这个阶段寻找另一个解决方案(可能只是改变glob返回的文件名列表),但我至少想知道为什么这个方法不起作用。任何人都可以解决一些问题(或者 - 甚至更好 - 让它发挥作用)?

1 个答案:

答案 0 :(得分:1)

是的,不幸的是,active_support是“按需”而不是“预先要求”,所以你可能不得不使用你的预加载器。不幸的是,如果你的应用程序处于开发模式,它可能会设置为“为每个请求重新加载类”,因此你必须禁用rails的这个方便功能,或者确保在需要时重新加载这些文件。

-r