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

我正在使用一个使用引擎的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的转移风。