首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在gem中扩展ApplicationController?

如何在gem中扩展ApplicationController?
EN

Stack Overflow用户
提问于 2012-07-05 16:08:43
回答 4查看 12.2K关注 0票数 19

我想我会想出一种巧妙的方法在Rails 3.xgem中扩展ApplicationController。

在我的gem的lib/my_namespace/my_controller.rb中,我有:

代码语言:javascript
复制
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还没有被加载,所以它失败了:

代码语言:javascript
复制
/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的ApplicationController的lib/gem_namespace/application_controller.rb中定义了以下内容:

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-07-05 23:37:44

这是一个吉斯特,它演示如何访问子类的类并将其存储在实例变量中,并在“前”和“后”筛选器中访问它。它使用包含方法。

票数 5
EN

Stack Overflow用户

发布于 2012-07-05 16:11:46

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

代码语言:javascript
复制
class ApplicationController < ActionController::Base
  include MyCoolModule
end

添加前面的过滤器等(将此添加到模块中)

代码语言:javascript
复制
def self.included(base)
  base.send(:before_filter, my_method)
end

更新:您可能只需要做一个更干净的base.before_filter :my_method

票数 9
EN

Stack Overflow用户

发布于 2016-10-24 17:30:13

真理要简单得多,也要灵活得多。

将此添加到lib/engine.rb中:class Engine < Rails::Engine; end

然后简单地使用:

代码语言:javascript
复制
ActionController::Base.class_eval do

  include SomethingFromMineGemModule

  # or:
  def hello_from_gem
    'Hey people!'
  end

end
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11348332

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档