我正在开发一个使用引擎的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。在过去,gem的代码大多位于lib
目录中,因此只运行一次配置就足够了。
发动机如何改变事物
然而,Rails引擎在app
目录中有代码:控制器、模型等。这些文件在每次请求时都会在开发模式下重新加载。因此,像我上面的例子这样的配置就丢失了。
输入准备(_P)
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_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
块在每个请求上都被调用,但它似乎每隔三个请求左右就会被随机调用。我添加了块:
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
是使用ActiveSupport::Callbacks
实现的(即与before_save
等使用的相同)。多个to_prepare
块是可以的。
目前,Rails在读取应用程序初始化程序后似乎会在to_prepare_blocks
上进行迭代,因此添加到Rails.application.configuration.to_prepare
应该可以工作。您可能更喜欢使用ActionDispatch::Reloader.to_prepare
。
没有什么可以阻止您在应用程序/模型中的文件中执行初始化器代码。
例如
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的风向变化。