19

Rails 3.x の gem で ApplicationController を拡張する巧妙な方法を思いつきました。

私の宝石lib/my_namespace/my_controller.rbには、次のものがありました。

class MyNamespace::MyController < ApplicationController

  before_filter :some_method
  after_filter :another_method

  def initialize
    # getting classname of the subclass to use for lookup of the associated model, etc.
    # and storing the model_class in an instance variable
    # ...
  end

  # define :some_method, :another_method, etc.
  # ...

private
  attr_accessor :subclass_defined_during_initialize # etc.

  # etc.
end

ただし、Gem が読み込まれると、app/controllers/application_controller.rbまだ読み込まれていないため、失敗します。

/path/to/rvm/gemset/gems/activesupport-3.2.6/lib/active_support/dependencies.rb:251:
in `require': cannot load such file -- my_gem_name/application_controller (LoadError)

回避策として、gem で ApplicationController を次のlib/gem_namespace/application_controller.rbように定義しました。

class ApplicationController < ActionController::Base
end

そこで定義したとしても、拡張したアプリケーションのコントローラーとapp/controllers/application_controller.rb拡張したコントローラーの両方が、で定義された ApplicationController を直接的または間接的に拡張するように、Rails 3 アプリケーションの で再定義されると想定しました。ApplicationControllerMyNamespace::MyControllerapp/controllers/application_controller.rb

しかし、gem をロードした後、拡張するコントローラーが でApplicationController定義されたメソッドにアクセスできないことに気付きましたapp/controllers/application_controller.rb。また、ApplicationHelper (app/helpers/application_helper.rb)モジュールは他のヘルパー モジュールによって読み込まれなくなりました。

and to を定義し、クラスの名前にアクセスして、関連するモデルのクラスを決定し、そのメソッド内で保存して使用できるようにApplicationControllerするために、gem のコントローラー内で拡張するにはどうすればよいですか?before_filterafter_filterinitialize

2012/10/22 更新:

これが私が思いついたものです:

lib/your_gem_name/railtie.rb

module YourGemsModuleName
  class Railtie < Rails::Railtie
    initializer "your_gem_name.action_controller" do
    ActiveSupport.on_load(:action_controller) do
      puts "Extending #{self} with YourGemsModuleName::Controller"
      # ActionController::Base gets a method that allows controllers to include the new behavior
      include YourGemsModuleName::Controller # ActiveSupport::Concern
    end
  end
end

とでlib/your_gem_name/controller.rb

module YourGemsModuleName
  module Controller
    extend ActiveSupport::Concern

    # note: don't specify included or ClassMethods if unused

    included do
      # anything you would want to do in every controller, for example: add a class attribute
      class_attribute :class_attribute_available_on_every_controller, instance_writer: false
    end

    module ClassMethods
      # notice: no self.method_name here, because this is being extended because ActiveSupport::Concern was extended
      def make_this_controller_fantastic
        before_filter :some_instance_method_available_on_every_controller # to be available on every controller
        after_filter :another_instance_method_available_on_every_controller # to be available on every controller
        include FantasticStuff
      end
    end

    # instance methods to go on every controller go here
    def some_instance_method_available_on_every_controller
      puts "a method available on every controller!"
    end

    def another_instance_method_available_on_every_controller
      puts "another method available on every controller!"
    end

    module FantasticStuff
      extend ActiveSupport::Concern

      # note: don't specify included or ClassMethods if unused

      included do
        class_attribute :class_attribute_only_available_on_fantastic_controllers, instance_writer: false
      end

      module ClassMethods
        # class methods available only if make_this_controller_fantastic is specified in the controller
        def some_fanastic_class_method
          put "a fantastic class method!"
        end
      end

      # instance methods available only if make_this_controller_fantastic is specified in the controller
      def some_fantastic_instance_method
        puts "a fantastic instance method!"
      end

      def another_fantastic_instance_method
        puts "another fantastic instance method!"
      end
    end
  end
end
4

4 に答える 4

9

この特定の種類の機能については、gem でモジュールを作成し、そのモジュールを Application Controller に含めることをお勧めします。

class ApplicationController < ActionController::Base
  include MyCoolModule
end

フィルターなどの前に追加するには (これをモジュールに追加します)

def self.included(base)
  base.send(:before_filter, my_method)
end

base.before_filter :my_method更新:どちらがよりクリーンかを実行できる場合があります。

于 2012-07-05T16:11:46.310 に答える
5

これは、サブクラスのクラスにアクセスしてインスタンス変数に格納し、前後のフィルターでアクセスする方法を示すGistです。これは include メソッドを使用します。

于 2012-07-05T23:37:44.577 に答える
2

真実ははるかに単純で柔軟です。

これに追加lib/engine.rbclass Engine < Rails::Engine; end

そして、単に使用します:

ActionController::Base.class_eval do

  include SomethingFromMineGemModule

  # or:
  def hello_from_gem
    'Hey people!'
  end

end
于 2016-10-24T17:30:13.623 に答える
0

初期化コールバックで ApplicationController を参照できました。

ApplicationController をサブクラス化/参照する gem コード:

class GemApplicationController < ApplicationController
  before_filter :method_to_call

  def method_to_call
    #your code here
  end
end

サブクラス化されたコントローラーを作成する gem コードのコールバック:

module GemName
  def self.load_gem_application_controller
    require "path/to/gem_application_controller"
  end
end

rails_app/config/initializers/gem_name.rb

GemName.load_gem_application_controller

次に、この機能を使用するコントローラーをサブクラス GemApplicationController にします。

class SpecialCaseController < GemApplicationController
  # this will inherit from the gem's controller, 
  # which inherits from the rails_app ApplicationController
end
于 2013-06-14T19:58:21.583 に答える