从插件中包含控制器的方法
使用Rails 2.3.11 ,我正在为Redmine创建一个插件,为ApplicationController
添加方法 。
我在插件中创建了以下模块:
module ApplicationControllerPatch def self.included(base) # :nodoc: base.class_eval do rescue_from AnException, :with => :rescue_method def rescue_method(exception) ... end end end end
现在,如果我将此模块直接包含在application_controller.rb
文件中,如下所示:
class ApplicationController < ActionController::Base include ApplicationControllerPatch ... end
一切正常,但是我想通过从插件本身包含这个模块来避免编辑核心源代码。 到目前为止,如果我这样做:
ApplicationController.send(:include, ApplicationControllerPatch)
直接来自这个模块文件(位于插件文件夹中)。 这将为请求正确加载,然后由控制器覆盖(我猜)。
完成此任务的方法是什么?
一个常见的模式是在插件的init.rb
使用Dispatcher.to_prepare
。 这是必需的,因为在开发模式下(或者通常在config.cache_classes = false
)Rails会在每次请求获取更改之前重新加载所有类,而无需每次都完全重新启动应用程序服务器。
然而,这意味着您必须在重新加载类之后再次应用补丁,因为Rails无法知道稍后注入了哪些模块。 使用Dispatcher.to_prepare
可以实现这一点。 块中定义的代码在生产模式下执行一次,在开发模式下的每个请求之前执行,这使得它成为修补核心类的首选位置。
这种方法的优点是,您可以使插件自包含,并且不需要更改周围应用程序中的任何内容。
把它放在init.rb
,例如vendor/plugins/my_plugin/init.rb
require 'redmine' # Patches to the Redmine core. require 'dispatcher' Dispatcher.to_prepare do ApplicationController.send(:include, MyPlugin::ApplicationControllerPatch) unless ApplicationController.include?(RedmineSpentTimeColumn::Patches::IssuePatch) end Redmine::Plugin.register :my_plugin do name 'My Plugin' [...] end
您的补丁应始终在以插件命名的模块内进行命名空间,以免遇到定义相同模块名称的多个插件的问题。 然后将补丁放入lib/my_plugin/application_controller_patch.rb
。 这样,它将由Rails自动加载器自动拾取。
把它放到vendor/plugins/my_plugin/lib/my_plugin/application_controller_patch.rb
module MyPlugin module ApplicationControllerPatch def self.included(base) # :nodoc: base.class_eval do rescue_from AnException, :with => :rescue_method def rescue_method(exception) [...] end end end end end
这种问题只发生在开发中,因为类是重新加载而不是gem。
因此,在config / environments / development.rb中的config.to_prepare
块中添加send
方法
阅读有关初始化过程的Rails doc以获取更多详细信息