如何防止初始化程序配置在开发模式中丢失?
我正在使用一个使用引擎的Rails应用程序。 我正在使用初始化程序来配置我的引擎控制器之一,以便它在主机应用程序中触发操作。 代码看起来像这样:
# config/initializers/my_engine.rb MyEngine::SomeController.after_filter proc { # Do something in the host app }, :only => :update
这在生产中工作正常,但在开发模式下,proc仅在第一个请求时调用。 这是因为类被重新加载并且此配置丢失,因为它存储在类变量中。 (例如, MyEngine::SomeController
从它所在的文件重新加载,并且因为after_filter
没有在那里声明,所以不会重新添加它。)
一些Rails背景
在开发模式下,Rails使用以下加载策略:
-
app
目录中的代码会在每个请求上重新加载,前提是您正在主动更改它。 - 当应用程序启动时,
lib
目录中的代码以及config/initializer
文件将被加载一次 。
初始化程序文件通常用于配置gem。 在过去,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
application.rb
失败,以保持组织有序。
因此,对于我的引擎的app
目录中的任何类配置,我想将该代码放在config/initializers
下的文件中。
这是我的问题。
- 我不清楚如何在初始化文件中将
config
纳入范围。 我在想它会是Rails.application.config
。 是对的吗? - 我可以添加多个
to_prepare
块吗? 我担心多次调用它会覆盖以前的块。
更新
正如@Frederick Cheung所提到的, Rails.application.config.to_prepare
可以在config/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
代码在开发过程中间歇性地工作。
您可以根据需要添加to_prepare
块 – 当您执行config.to_prepare
,Rails正在执行(在railties中的configuration.rb
中)
def to_prepare(&blk) to_prepare_blocks << blk if blk end
然后迭代这些块将它们交给ActionDispatch::Reloader
to_prepare
,其中to_prepare
是使用ActiveSupport::Callbacks
(即与before_save
相同的东西,依此类推)。 多个to_prepare
块都可以。
目前看起来Rails在读取应用程序初始化程序后会迭代to_prepare_blocks
,因此添加到Rails.application.configuration.to_prepare
应该可以正常工作。 您可能更喜欢使用ActionDispatch::Reloader.to_prepare
。
没有什么可以阻止你在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的转移风。