Как я могу расширить ApplicationController в геме?

Я думал, что придумал изящный способ расширить ApplicationController в геме Rails 3.x.

В моей жемчужине 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)

В качестве обходного пути я определил ApplicationController в моем геме lib/gem_namespace/application_controller.rbкак:

class ApplicationController < ActionController::Base
end

Я предположил, что даже несмотря на то, что я определил его там, он будет переопределен в app/controllers/application_controller.rbмоего приложения Rails 3, так что оба контроллера в приложении, которое расширило ApplicationController, и контроллеры, которые расширили MyNamespace::MyController, будут прямо или косвенно расширять ApplicationController. определено в app/controllers/application_controller.rb.

Однако мы заметили, что после загрузки гема контроллеры, расширяющие ApplicationController, не могли получить доступ к методам, определенным в app/controllers/application_controller.rb. Кроме того,ApplicationHelper(app/helpers/application_helper.rb)модуль больше не загружался другими вспомогательными модулями.

Как я могу расширить ApplicationControllerвнутри контроллера в моем драгоценном камне с целью определения before_filterи after_filterи использовать initializeдля доступа к имени класса, чтобы определить класс связанной модели, который он мог бы затем хранить и использовать в своих методах?

Обновление 22.10.2012:

Вот что у меня получилось:

В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
18
задан fontno 14 July 2013 в 20:13
поделиться