从插件包括控制器的方法 [英] Including methods to a controller from a plugin

查看:92
本文介绍了从插件包括控制器的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用 Rails 2.3.11 ,我正在为Redmine创建一个插件,该插件ApplicationController添加方法.

Using Rails 2.3.11, I'm creating a plugin for Redmine that add methods to ApplicationController.

我在插件中创建了以下模块:

I've created the following module, in the plugin :

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文件中,就像这样:

Now, if I include this module directly into the application_controller.rb file, like this:

class ApplicationController < ActionController::Base
  include ApplicationControllerPatch

  ...
end

一切正常,但是我想通过从插件本身包含此模块来避免编辑. 到目前为止,如果我这样做:

Everything works just fine, however I would like to avoid editing the core source by including this module from the plugin itself. So far, if I do:

ApplicationController.send(:include, ApplicationControllerPatch)

直接从此模块文件(位于插件文件夹中)

.这将为请求正确加载,然后被控制器覆盖(我想).

directly from this module file (located in the plugin folder). This will load properly for the request and then it gets overwritten by the controller (I guess).

完成此操作的方式是什么?

推荐答案

常见的模式是在插件的init.rb中使用Dispatcher.to_prepare.之所以需要这样做,是因为在开发模式下(或者通常是config.cache_classes = false的情况下),Rails会在每次请求获取更改之前立即重新加载所有类,而无需每次都完全重新启动应用程序服务器.

A common pattern is to use Dispatcher.to_prepare inside your plugin's init.rb. This is required because in development mode (or generally if config.cache_classes = false) Rails reloads all classes right before each request to pick up changes without the need to completely restart the application server each time.

但是,这意味着您必须在重新加载类后再次应用补丁,因为Rails稍后无法知道注入了哪些模块.使用Dispatcher.to_prepare您可以精确地做到这一点.该块中定义的代码在生产模式下执行一次,在开发模式下的每个请求之前执行,这使其成为猴子补丁核心类的首要位置.

This however means the you have to apply your patch again after the class got reloaded as Rails can't know which modules got injected later on. Using Dispatcher.to_prepare you can achieve exactly that. The code defined in the block is executed once in production mode and before each request in development mode which makes it the premier place to monkey patch core classes.

这种方法的好处是您可以使插件自包含,而无需在周围的应用程序中进行任何更改.

The upside of this approach is that you can have your plugins self-contained and do not need to change anything in the surrounding application.

将此内容放入您的init.rb中,例如vendor/plugins/my_plugin/init.rb

Put this inside your init.rb, e.g. 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 Autoloader自动拾取.

Your patch should always be namespaced inside a module named after your plugin to not run into issues with multiple plugins defining the same module names. Then put the patch into lib/my_plugin/application_controller_patch.rb. That way, it will be picked up automatically by the Rails Autoloader.

将其放入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

这篇关于从插件包括控制器的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆