从插件包含控制器的方法

时间:2011-10-28 23:57:20

标签: ruby-on-rails ruby plugins redmine

使用 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)

直接来自此模块文件(位于插件文件夹中)。这将为请求正确加载,然后被控制器覆盖(我猜)。

完成此任务的方法是什么?

2 个答案:

答案 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文档以获取更多详细信息。