簡體   English   中英

在主應用程序中擴展Rails 3引擎的控制器

[英]Extending controllers of a Rails 3 Engine in the main app

我在我的應用程序中使用Rails引擎作為gem。 引擎有PostsController ,有很多方法,我想在我的主應用程序中擴展控制器邏輯,例如添加一些方法。 如果我只是在主應用程序中創建PostsController ,則不會加載引擎的控制器。

有一個解決方案提出了Rails引擎,它基於改變ActiveSupport::Dependencies#require_or_load擴展功能

這是唯一/正確的方法嗎? 如果是的話,我在哪里放這段代碼?

EDIT1:

這是Andrius為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

按照設計,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

就文件布局而言,鐵路看起來與發動機完全一樣。

進一步閱讀: 使用Railties擴展Rails 3

如果您仍然感到困惑,請看看這個具有完整實現的git項目: https//github.com/jamezilla/bcms_pubcookie

為什么不直接從應用程序中的Engine控制器類繼承(並將路由指向新的子控制器)? 聲音在概念上類似於擴展內置Devise控制器的方式。

方法1

這是我在require 'rails/all'之后在application.rb放入我的Rails 3應用application.rb (讓我知道它是不是放置它的地方)

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

我已經根據上面Andrius和Andrei的代碼創建了一個gem。 而不是復制該代碼,只需要mixable_engines gem。 僅適用於rails 3。

https://github.com/asee/mixable_engines

https://rubygems.org/gems/mixable_engines

@Andrei和@Artrius:我已經在許可證文件中記下了您,如果您想要真實姓名或其他信用,請告訴我。

您可以使用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

此方法使您不必重新定義主應用程序中的控制器繼承。

如果您不希望補丁活動支持按照Rails引擎擴展功能中的建議更改加載順序,則可以使用機架中間件進行身份驗證。 如果身份驗證是作為每個控制器操作的一部分完成的,則此方法可能會節省大量代碼和時間。

@cowboycoded方法2與require_dependencyconfig.reload_plugins一起在Rails 3.2.2 / Ruby 1.9上為我工作。

這是代碼: https//stackoverflow.com/a/9790497/22237

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM