I thought I'd come up with a slick way to extend ApplicationController in a Rails 3.x gem.
我想我可以想出一个在Rails 3中扩展应用程序控制器的巧妙方法。x的宝石。
In my gem's lib/my_namespace/my_controller.rb
, I had:
在我的宝石的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
but when the Gem is loaded, app/controllers/application_controller.rb
is not yet loaded, so it fails:
但是当Gem加载时,app/controller /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)
As a workaround, I had defined ApplicationController in my gem's lib/gem_namespace/application_controller.rb
as:
作为一个解决方案,我在gem的lib/gem_namespace/application_controller中定义了ApplicationController。rb:
class ApplicationController < ActionController::Base
end
I assumed that even though I had defined it there, it would be redefined in my Rails 3 application's app/controllers/application_controller.rb
, such that both controllers in the application that extended ApplicationController
and controllers that extended MyNamespace::MyController
would directly or indirectly extend the ApplicationController defined in app/controllers/application_controller.rb
.
我假设即使我在那里定义了它,它也会在我的Rails 3应用程序的app/controller /application_controller中重新定义。rb,使应用程序中扩展ApplicationController的控制器和扩展MyNamespace:::MyController的控制器都可以直接或间接地扩展app/controllers/application_controller.rb中定义的ApplicationController。
However, we noticed that after loading the gem, controllers that extend ApplicationController
were unable to access methods defined in app/controllers/application_controller.rb
. Also, the ApplicationHelper
(app/helpers/application_helper.rb)
module was no longer being loaded by other helper modules.
但是,我们注意到在加载gem之后,扩展ApplicationController的控制器无法访问应用程序/控制器/application_controller.rb中定义的方法。另外,ApplicationHelper (app/helper /application_helper.rb)模块不再被其他helper模块加载。
How can I extend ApplicationController
within the controller in my gem for the purpose of defining a before_filter
and after_filter
to and use initialize
to access the class's name to determine the associated model's class that it could then store and use within its methods?
如何在gem中的控制器中扩展ApplicationController以定义before_filter和after_filter以访问类的名称并使用initialize来确定相关模型的类,然后在其方法中存储和使用该类?
Update 2012/10/22:
更新2012/10/22:
Here's what I came up with:
以下是我的想法:
In lib/your_gem_name/railtie.rb
:
在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
and in lib/your_gem_name/controller.rb
:
在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 个解决方案
#1
5
Here is a Gist that shows how to access the class of the subclass and store it in an instance variable and access it in the before and after filters. It uses the include method.
这里有一个要点,说明如何访问子类的类并将其存储在实例变量中,并在过滤器之前和之后访问它。它使用include方法。
#2
8
For this specific kind of functionality I would recommend creating a module in your gem and include that module in your Application Controller
对于这种特定的功能,我建议在gem中创建一个模块,并在应用程序控制器中包含该模块
class ApplicationController < ActionController::Base
include MyCoolModule
end
To add before filters, etc (add this to your module)
要添加过滤器等等(将此添加到模块中)
def self.included(base)
base.send(:before_filter, my_method)
end
Update: you may be able to just do base.before_filter :my_method
which is cleaner.
更新:你可以做基地。before_filter:my_method,更简洁。
#3
2
Truth is much much simpler and flexible.
真理要简单得多,也灵活得多。
Add to lib/engine.rb
this: class Engine < Rails::Engine; end
添加到lib /引擎。rb: class Engine < Rails:::Engine;结束
And then simply use:
然后简单地使用:
ActionController::Base.class_eval do
include SomethingFromMineGemModule
# or:
def hello_from_gem
'Hey people!'
end
end
#4
0
I was able to reference ApplicationController with an initializer callback.
我可以使用初始化器回调引用ApplicationController。
gem code that subclasses/references ApplicationController:
子类/引用应用程序控制器的gem代码:
class GemApplicationController < ApplicationController
before_filter :method_to_call
def method_to_call
#your code here
end
end
gem code callback to create subclassed controller:
gem代码回调来创建子类控制器:
module GemName
def self.load_gem_application_controller
require "path/to/gem_application_controller"
end
end
rails_app/config/initializers/gem_name.rb
rails_app / config /初始化/ gem_name.rb
GemName.load_gem_application_controller
Then have controllers that use this functionality subclass GemApplicationController
然后有使用这个功能的控制器子类GemApplicationController
class SpecialCaseController < GemApplicationController
# this will inherit from the gem's controller,
# which inherits from the rails_app ApplicationController
end
#1
5
Here is a Gist that shows how to access the class of the subclass and store it in an instance variable and access it in the before and after filters. It uses the include method.
这里有一个要点,说明如何访问子类的类并将其存储在实例变量中,并在过滤器之前和之后访问它。它使用include方法。
#2
8
For this specific kind of functionality I would recommend creating a module in your gem and include that module in your Application Controller
对于这种特定的功能,我建议在gem中创建一个模块,并在应用程序控制器中包含该模块
class ApplicationController < ActionController::Base
include MyCoolModule
end
To add before filters, etc (add this to your module)
要添加过滤器等等(将此添加到模块中)
def self.included(base)
base.send(:before_filter, my_method)
end
Update: you may be able to just do base.before_filter :my_method
which is cleaner.
更新:你可以做基地。before_filter:my_method,更简洁。
#3
2
Truth is much much simpler and flexible.
真理要简单得多,也灵活得多。
Add to lib/engine.rb
this: class Engine < Rails::Engine; end
添加到lib /引擎。rb: class Engine < Rails:::Engine;结束
And then simply use:
然后简单地使用:
ActionController::Base.class_eval do
include SomethingFromMineGemModule
# or:
def hello_from_gem
'Hey people!'
end
end
#4
0
I was able to reference ApplicationController with an initializer callback.
我可以使用初始化器回调引用ApplicationController。
gem code that subclasses/references ApplicationController:
子类/引用应用程序控制器的gem代码:
class GemApplicationController < ApplicationController
before_filter :method_to_call
def method_to_call
#your code here
end
end
gem code callback to create subclassed controller:
gem代码回调来创建子类控制器:
module GemName
def self.load_gem_application_controller
require "path/to/gem_application_controller"
end
end
rails_app/config/initializers/gem_name.rb
rails_app / config /初始化/ gem_name.rb
GemName.load_gem_application_controller
Then have controllers that use this functionality subclass GemApplicationController
然后有使用这个功能的控制器子类GemApplicationController
class SpecialCaseController < GemApplicationController
# this will inherit from the gem's controller,
# which inherits from the rails_app ApplicationController
end