17

我想我会想出一个在Rails 3.x gem中扩展ApplicationController的方法。如何将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 

但加载宝石时,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) 

作为一种解决办法,我已经确定ApplicationController中我的宝石的lib/gem_namespace/application_controller.rb为:

class ApplicationController < ActionController::Base 
end 

I假定即使我已经在那里定义了它,它将在我的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)模块不再由其他辅助模块加载。

我怎么能在我的宝石控制器内延长ApplicationController用于定义before_filterafter_filter并使用initialize访问类的名称,以确定相关模型的类,它可能然后存储及其方法中使用的目的是什么?

更新2012年10

这就是我想出了:

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 

回答

5

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

+0

太棒了!那么包括一个模块真的是最好的主意。非常感谢你的帮助! – 2012-07-06 12:48:29

+0

[Rails论坛中的相关对话](https://web.archive.org/web/20130216193936/http://railsforum.com/viewtopic.php?pid=153813) – 2012-07-06 13:15:55

8

对于这个规范C类功能我会建议建立在你的宝石模块和包括该模块在你的应用程序控制器

class ApplicationController < ActionController::Base 
    include MyCoolModule 
end 

之前滤波器增加等(添加到您的模块)

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

更新:你可能只能做base.before_filter :my_method这是更清洁。

+0

会包括一个模块真的在这种情况下工作吗?你可以扩展你如何得到子类的名称,因为没有子类,你将如何利用before_filter和after_filter?再看看这个问题,如果你能扩大这些问题,请澄清。否则,我会认为这不是一个选项。谢谢! – 2012-07-05 16:18:09

+0

刚才在问题的最后一行中澄清了我需要使用before_filter,after_filter和访问子类的名称。 – 2012-07-05 16:25:39

0

我能够引用具有初始化回调的ApplicationController。

创业板代码,子类/引用的ApplicationController:

class GemApplicationController < ApplicationController 
    before_filter :method_to_call 

    def method_to_call 
    #your code here 
    end 
end 

宝石码回调创建子类控制器:

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 
+0

我了解到最好使用模块。您只能从一个父类继承(直接),但可以根据需要包含/扩展模块。 – 2013-06-24 16:30:23

+1

谢谢加里。我最终还实现了一个模块。 – 2013-07-02 20:20:14

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 
相关问题