如何防止初始化程序配置在开发模式中丢失?

时间:2012-01-17 13:06:36

标签: ruby-on-rails ruby-on-rails-3.1

我正在使用一个使用引擎的Rails应用程序。我正在使用初始化程序来配置我的引擎的一个控制器,以便它在主机应用程序中触发一个动作。代码看起来像这样:

# config/initializers/my_engine.rb
MyEngine::SomeController.after_filter proc {
  # Do something in the host app
}, :only => :update

这在生产中工作正常,但在开发模式下,只在第一个请求时调用proc。这是因为类被重新加载并且此配置丢失,因为它存储在类变量中。 (例如,MyEngine::SomeController从其所在的文件重新加载,并且由于after_filter未在此处声明,因此不会重新添加。{/ p>

一些Rails背景

在开发模式下,Rails使用以下加载策略:

  • 假设您正在积极更改{li>每个请求,app目录中的代码会重新加载。
  • 当应用程序启动时,lib目录中的代码以及config/initializer文件将加载

初始化文件通常用于配置gems。在过去,gems主要在lib目录中有代码,因此运行一次配置就足够了。

引擎如何改变事物

但是,Rails引擎在app目录中有代码:控制器,模型等。这些文件在每个请求的开发模式下重新加载。因此,上面的示例配置将丢失。

输入to_prepare

Rails专门提供config.to_prepare来解决这个问题:它在生产中运行一次,在开发中的每个请求上运行。

例如,我们在application.rb中有这个,它工作正常:

config.to_prepare do
  # set up class variables (after_filters, etc)
end

但是,如果我必须将所有引擎的配置都放在application.rb中,那么config/initializers就会使事情变得井然有序。

因此,对于我的引擎'app目录中的任何类配置,我想将该代码放在config/initializers下的文件中。

以下是我的问题。

  • 我不清楚如何将config放入初始化文件的范围内。我想它会是Rails.application.config。是吗?
  • 我可以添加多个to_prepare块吗?我担心多次调用它会覆盖以前的块。

更新

正如@Frederick Cheung所说,Rails.application.config.to_prepareconfig/initializer文件中有效,并且可以根据需要在各种文件中使用尽可能多的文件;每个调用将其块附加到数组,因此不会覆盖任何内容。

此问题的解决方案是:

# config/initializers/my_engine.rb
Rails.application.config.to_prepare do
  MyEngine::SomeController.after_filter proc {
    # Do something in the host app
  }, :only => :update
end

有一点似乎仍然很奇怪:我希望在开发模式下在每个请求上调用to_prepare块,但是它似乎每隔3个请求随机调用一次。我添加了块:

Rails.application.config.to_prepare do
  Rails.logger.info "Running the prepare block!"
end

...重新启动了我的应用,并刷新了九次页面。我只在第1次,第5次,第7次和第9次请求中看到了该消息。我不确定是什么解释了这种行为,但它确实解释了为什么我的代码没有 to_prepare在开发过程中间歇性地工作。

2 个答案:

答案 0 :(得分:10)

您可以根据需要添加任意数量的to_prepare块 - 当您执行config.to_prepare时,Rails正在执行(在configuration.rb的铁路中)

def to_prepare(&blk)
  to_prepare_blocks << blk if blk
end

然后迭代这些块,将它们交给ActionDispatch::Reloader,其中to_prepare使用ActiveSupport::Callbacks实现(即用于before_save的相同内容,依此类推)。多个to_prepare块很好。

目前看起来Rails在阅读应用程序初始化程序后会迭代to_prepare_blocks,因此添加到Rails.application.configuration.to_prepare应该可行。您可能更喜欢使用ActionDispatch::Reloader.to_prepare

答案 1 :(得分:1)

没有什么可以阻止你在app / models中的文件中执行初始化代码。

例如

class MyClass
  def self.run_me_when_the_class_is_loaded
  end
end

MyClass.run_me_when_the_class_is_loaded

MyClass.run_me ...将在加载类时运行....这就是我们想要的,对吧?

不确定它是否是Rails方式....但它非常简单,并且不依赖于Rails的转变。