如何在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_filter
和after_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.rb
: class 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