使用 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)
直接来自此模块文件(位于插件文件夹中)。这将为请求正确加载,然后被控制器覆盖(我猜)。
完成此任务的方法是什么?
答案 0 :(得分:4)
常见的模式是在插件的Dispatcher.to_prepare
中使用init.rb
。这是必需的,因为在开发模式下(或者通常是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
答案 1 :(得分:0)
这种问题只发生在dev中,因为类是重新加载但不是gems。
所以在 config / environments / development.rb
中的send
块中添加config.to_prepare
方法
阅读有关初始化过程的Rails文档以获取更多详细信息。