Расширение контроллеров Rails 3 Engine в основном приложении - PullRequest
15 голосов
/ 18 февраля 2011

Я использую движок Rails в качестве драгоценного камня в своем приложении. Движок имеет PostsController с несколькими методами, и я хотел бы расширить логику контроллера в моем основном приложении, например добавить несколько методов. Если я просто создаю PostsController в главном приложении, то контроллер двигателя не загружается.

Существует решение, предложенное в вопросе Рельсовые двигатели, расширяющие функциональность на основе изменения ActiveSupport::Dependencies#require_or_load

Это единственный / правильный способ сделать это? Если да, куда мне поместить этот кусок кода?

EDIT1:

Это код , предложенный Андриусом для Rails 2.x

module ActiveSupport::Dependencies
  alias_method :require_or_load_without_multiple, :require_or_load
  def require_or_load(file_name, const_path = nil)
    if file_name.starts_with?(RAILS_ROOT + '/app')
      relative_name = file_name.gsub(RAILS_ROOT, '')
      @engine_paths ||= Rails::Initializer.new(Rails.configuration).plugin_loader.engines.collect {|plugin| plugin.directory }
      @engine_paths.each do |path|
        engine_file = File.join(path, relative_name)
        require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file)
      end
    end
    require_or_load_without_multiple(file_name, const_path)
  end
end

Ответы [ 7 ]

10 голосов
/ 08 октября 2011

По своей структуре классы в Rails :: Engine * предполагаются , которые должны быть ограничены движком. Таким образом, они не вносят странных ошибок, случайно топая весь код, загруженный в основное приложение или другими движками. Monkeypatching ActiveSupport :: Зависимости для смешивания движков по всем направлениям - это действительно плохой обходной путь.

Просто используйте Rails :: Railtie вместо этого. Они имеют одинаковую функциональность, но не ограничены так же, как движок. У вас есть доступ ко всему стеку приложений rails (включая движки). Это более хирургический подход.

module MyModule

  module SomeModelExtensions
    # Called when this module is included on the given class.
    def self.included(base)
      base.send(:include, InstanceMethods)
      base.extend(ClassMethods)
    end

    module ClassMethods
      def some_new_class_method
        # do stuff...
      end
    end

    module InstanceMethods
      def some_new_instance_method
        # do stuff...
      end
    end

  end

  module SomeControllerExtensions
    def self.included(base)
      base.send(:include, InstanceMethods)
      base.alias_method_chain :new, :my_module
    end

    module InstanceMethods
      # override the 'new' method
      def new_with_my_module
        # do stuff
      end
    end
  end

  class Railtie < ::Rails::Railtie

    # The block you pass to this method will run for every request in
    # development mode, but only once in production.
    config.to_prepare do
      SomeModel.send(:include, MyModule::SomeModelExtensions)
      SomeController.send(:include, MyModule::SomeControllerExtensions)
    end

  end

end

Что касается расположения файлов, то рельсы выглядят точно так же, как двигатели.

Дальнейшее чтение: Расширение Rails 3 с Railties

И если вы все еще в замешательстве, взгляните на этот git-проект, который имеет полную реализацию: https://github.com/jamezilla/bcms_pubcookie

7 голосов
/ 24 февраля 2011

Почему бы просто не наследовать класс контроллера Engine в вашем приложении (и указать ваши маршруты на новых дочерних контроллерах)? Звучит концептуально аналогично тому, как вы расширяете встроенные контроллеры Devise.

4 голосов
/ 19 февраля 2011

Метод 1

Вот что я вставил в свое приложение Rails 3 в application.rb после require 'rails/all' (дайте мне знать, если это плохое место, чтобы поставить его)

require 'active_support/dependencies'
module ActiveSupport::Dependencies
  alias_method :require_or_load_without_multiple, :require_or_load
  def require_or_load(file_name, const_path = nil)
    if file_name.starts_with?(Rails.root.to_s + '/app')
      relative_name = file_name.gsub(Rails.root.to_s, '')
      #@engine_paths ||= Rails::Application.railties.engines.collect{|engine| engine.config.root.to_s }
      #EDIT: above line gives deprecation notice in Rails 3 (although it works in Rails 2), causing error in test env.  Change to:
      @engine_paths ||= YourAppName::Application.railties.engines.collect{|engine| engine.config.root.to_s }
      @engine_paths.each do |path|
        engine_file = File.join(path, relative_name)
        require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file)
      end
    end
    require_or_load_without_multiple(file_name, const_path)
  end
end

Некоторое время это не сработало, вызывая

TypeError in PostsController#index

superclass mismatch for class PostsController

, но это было связано с опечаткой определения класса class PostsController < ActionController::Base, которая должна быть class PostsController < ApplicationController

Метод 2

Если вы не хотите делать это для всех контроллеров двигателя и т. Д., Вы можете загрузить контроллер двигателя до определения в главном приложении

require PostsEngine::Engine.config.root + 'app' + 'controllers' + 'posts_controller'

class PostsController < ApplicationController
  # extended methods
end
2 голосов
/ 02 сентября 2011

Я создал драгоценный камень на основе кода Андрюса и Андрея выше.Вместо того, чтобы копировать этот код, просто потребуйте гем mixable_engines.Работает только с рельсами 3 прямо сейчас.

https://github.com/asee/mixable_engines

https://rubygems.org/gems/mixable_engines

@ Андрей и @Artrius: Я внес вас в файл лицензии, дайте мне знать, если вы хотите указать свое настоящее имя иликакой-то другой кредит.

1 голос
/ 20 ноября 2014

Вы можете использовать метод Ruby send() для внедрения вашего кода в контроллер во время создания движка ...

# lib/cool_engine/engine.rb

module CoolEngine
  class Engine < ::Rails::Engine

    isolate_namespace CoolEngine

    initializer "cool_engine.load_helpers" do |app|
      # You can inject magic into all your controllers...
      ActionController::Base.send :include, CoolEngine::ActionControllerExtensions
      ActionController::Base.send :include, CoolEngine::FooBar

      # ...or add special sauce to models...
      ActiveRecord::Base.send :include, CoolEngine::ActiveRecordExtensions
      ActiveRecord::Base.send :include, CoolEngine::MoreStuff

      # ...even provide a base set of helpers
      ApplicationHelper.send :include, CoolEngine::Helpers
    end
  end
end

Этот метод избавляет вас от необходимости переопределять наследование контроллера в вашемосновное приложение.

1 голос
/ 18 февраля 2011

Если вы не хотите, чтобы активная поддержка исправлений изменяла порядок загрузки, как предложено в Механизмы Rails, расширяющие функциональность , вы можете использовать промежуточное программное обеспечение стойки для аутентификации.Если аутентификация выполняется как часть каждого действия контроллера, такой подход может сэкономить вам много кода и времени.

0 голосов
/ 20 марта 2012

@ cowboycoded метод 2 в сочетании с require_dependency и config.reload_plugins работал для меня на Rails 3.2.2 / Ruby 1.9.

Вот код: https://stackoverflow.com/a/9790497/22237

...