Включение методов в контроллер из плагина

Используя 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)

непосредственно из этого файла модуля (находится в папке плагина). Это будет правильно загружаться для запроса, а затем оно будет перезаписано контроллером (я думаю).

Как это сделать?


person Sébastien Grosjean - ZenCocoon    schedule 28.10.2011    source источник


Ответы (2)


Распространенным шаблоном является использование 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
person Holger Just    schedule 29.10.2011

Такая проблема возникает только в dev, потому что перезагружаются классы, но не драгоценные камни.

Поэтому добавьте свой метод send в блок config.to_prepare в файле config/environments/development.rb.

Прочтите документацию Rails, касающуюся процесса инициализации, для получения дополнительной информации.

person apneadiving    schedule 29.10.2011
comment
Извините, не увидел ваш ответ, прежде чем опубликовать свой собственный ответ. Трюк с редактированием development.rb — хороший совет. Спасибо. - person Sébastien Grosjean - ZenCocoon; 29.10.2011
comment
Enfin, je devrais plutot dire: de rien! - person apneadiving; 29.10.2011
comment
En effet, en Français ca me convient bien aussi ;-) Merci pour ton aide. - person Sébastien Grosjean - ZenCocoon; 29.10.2011
comment
Это хороший способ, однако способ, описанный Хольгером Джастом, еще лучше, так как не требует никаких изменений в настройках ядра Redmine. - person Sébastien Grosjean - ZenCocoon; 31.10.2011
comment
Нет проблем, странно, я получил -1, хотя - person apneadiving; 31.10.2011
comment
не совсем уверен, когда вам следует правильно -1, я думаю, это заслуживает того, чтобы остаться. Сделаю через 2 дня, пока не могу. - person Sébastien Grosjean - ZenCocoon; 02.11.2011