如何在 gem 中扩展 ApplicationController? [英] How can I extend ApplicationController in a gem?

查看:12
本文介绍了如何在 gem 中扩展 ApplicationController?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想我会想出一个巧妙的方法来在 Rails 3.x gem 中扩展 ApplicationController.

I thought I'd come up with a slick way to extend ApplicationController in a Rails 3.x gem.

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

In my gem's lib/my_namespace/my_controller.rb, I had:

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还没有加载,所以失败了:

but when the Gem is loaded, app/controllers/application_controller.rb is not yet loaded, so it fails:

/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 定义为:

As a workaround, I had defined ApplicationController in my gem's lib/gem_namespace/application_controller.rb as:

class ApplicationController < ActionController::Base
end

我假设即使我在那里定义了它,它也会在我的 Rails 3 应用程序的 app/controllers/application_controller.rb 中重新定义,这样应用程序中的两个控制器都扩展了 ApplicationController 和扩展 MyNamespace::MyController 的控制器将直接或间接扩展 app/controllers/application_controller.rb 中定义的 ApplicationController.

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.

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

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 以定义 before_filterafter_filter 并使用 初始化以访问类的名称以确定关联模型的类,然后它可以在其方法中存储和使用?

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?

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

推荐答案

这是一个要点这显示了如何访问子类的类并将其存储在实例变量中并在前后过滤器中访问它.它使用包含方法.

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.

这篇关于如何在 gem 中扩展 ApplicationController?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆