从插件中包含控制器的方法

使用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以获取更多详细信息