ruby-on-rails - 如何在 gem 中扩展 ApplicationController?

标签 ruby-on-rails ruby-on-rails-3 gem applicationcontroller load-order

我想我会想出一个巧妙的方法来在 Rails 3.x gem 中扩展 ApplicationController。

在我的 gem 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 的 lib/gem_namespace/application_controller.rb 中定义了 ApplicationController作为:
class ApplicationController < ActionController::Base
end

我假设即使我在那里定义了它,它也会在我的 Rails 3 应用程序的 app/controllers/application_controller.rb 中重新定义。 , 这样扩展 ApplicationController 的应用程序中的两个 Controller 和扩展 MyNamespace::MyController 的 Controller 将直接或间接扩展 app/controllers/application_controller.rb 中定义的 ApplicationController .

然而,我们注意到在加载 gem 之后,扩展 ApplicationController 的 Controller 无法访问 app/controllers/application_controller.rb 中定义的方法.此外,ApplicationHelper (app/helpers/application_helper.rb)模块不再被其他帮助模块加载。

如何延长 ApplicationController在我的 gem 的 Controller 中,用于定义 before_filterafter_filter使用initialize访问类的名称以确定关联模型的类,然后它可以在其方法中存储和使用?

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

最佳答案

对于这种特定类型的功能,我建议在您的 gem 中创建一个模块并将该模块包含在您的应用程序 Controller 中

class ApplicationController < ActionController::Base
  include MyCoolModule
end

添加前置过滤器等(将其添加到您的模块中)
def self.included(base)
  base.send(:before_filter, my_method)
end

更新:你也许可以做 base.before_filter :my_method哪个更干净。

关于ruby-on-rails - 如何在 gem 中扩展 ApplicationController?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11348332/

相关文章:

ruby-on-rails - Rails 控制台和 mongodb

ruby - 如何编写不与 block 环境隔离的DSL?

ruby-on-rails - 使用 Capistrano 部署 - 仅针对匹配的服务器运行

ruby-on-rails - Rails - Model.all.to_json 没有放置所有 Devise 属性

ruby-on-rails - 名称错误 : uninitialized constant in Rails app when testing with RSpec and Spork

ruby-on-rails-3 - Rails 3 中跨子域共享登录 session 的正确方法?

ruby-on-rails - "gem install rails"因 DNS 错误而失败

ruby-on-rails - 使用 Best In Place gem,如何允许就地编辑链接?

shell - zsh command not found : zeus, mailcatcher等?

ruby-on-rails - rails : Error running wkhtmltopdf -- (error while loading shared libraries)