如何在gem中扩展ApplicationController?

我想我会想出一个灵活的方法来扩展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的应用程序中的两个控制器和扩展MyNamespace::MyController控制器将直接或间接扩展app/controllers/application_controller.rb定义的ApplicationController。

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

我如何在我的gem中的控制器中扩展ApplicationController ,以便定义一个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 

这是一个Gist ,它展示了如何访问子类的类并将其存储在实例变量中,并在前后filter中访问它。 它使用include方法。

对于这种特定类型的function,我建议在gem中创建一个模块,并在Application Controller中包含该模块

 class ApplicationController < ActionController::Base include MyCoolModule end 

要在filter之前添加等(将其添加到您的模块)

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

更新:您可能只需要执行base.before_filter :my_method更干净。

真理更加简单灵活。

添加到lib/engine.rbclass Engine < Rails::Engine; end class Engine < Rails::Engine; end

然后简单地使用:

 ActionController::Base.class_eval do include SomethingFromMineGemModule # or: def hello_from_gem 'Hey people!' end end 

我能够使用初始化程序回调引用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 /配置/初始化/ gem_name.rb

 GemName.load_gem_application_controller 

然后让控制器使用此function子类GemApplicationController

 class SpecialCaseController < GemApplicationController # this will inherit from the gem's controller, # which inherits from the rails_app ApplicationController end