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