如何在gem中扩展ApplicationController?

时间:2012-07-05 16:08:44

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

我以为我想出了一个灵活的方法来扩展Rails 3.x gem中的ApplicationController。

在我的宝石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和控制器扩展了{{1}将直接或间接扩展MyNamespace::MyController中定义的ApplicationController。

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

如何在我的gem中的控制器中扩展(app/helpers/application_helper.rb),以便定义ApplicationControllerbefore_filter并使用after_filter来访问类的名称以确定相关模型的类,它可以在其方法中存储和使用?

更新2012/10/22

以下是我提出的建议:

initialize

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

4 个答案:

答案 0 :(得分:8)

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

class ApplicationController < ActionController::Base
  include MyCoolModule
end

要在过滤器之前添加等(将其添加到您的模块中)

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

更新:你可能只能做一个更清洁的base.before_filter :my_method

答案 1 :(得分:5)

Here is a Gist 它显示了如何访问子类的类并将其存储在实例变量中,并在前后过滤器中访问它。它使用include方法。

答案 2 :(得分:2)

真理更加简单灵活。

添加到lib/engine.rb这个:class Engine < Rails::Engine; end

然后简单地使用:

ActionController::Base.class_eval do

  include SomethingFromMineGemModule

  # or:
  def hello_from_gem
    'Hey people!'
  end

end

答案 3 :(得分:0)

我能够使用初始化程序回调引用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

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

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