2013-10-22 38 views
1

我正在为Rails 4编写一个需要向ApplicationController添加一些方法的gem,我想最好的方法是编写一个rake任务来打开文件,插入我需要的方法,然后关闭它。为了让我的方法在类定义中,最好的方法是什么?用rails rake任务插入方法的最佳方法

class ApplicationController < ActionController::Base 

    def existing_methods 
    foo 
    end 

    # insert my methods here 

end 

编辑

尝试下面的建议后,但无济于事,我尝试以下this post,最终尝试以下操作:

的lib/my_engine.rb

require "my_engine/engine" 
require "my_engine/controller_methods" 

module MyEngine 
end 

lib/my_engine/controller_methods.rb

module MyEngine 
    module ControllerMethods 
    extend ActiveSupport::Concern 

    def foo 
     "foo" 
    end 

    end 
end 
ActiveRecord::Base.send(:include, MyEngine::ControllerMethods) 

当运行应用程序,我application.slim包括线= foo,我也得到了以下错误:

undefined local variable or method `foo' for #<#:0x007ff350cd4ba0>

+0

你想让你的宝石字面上编辑application_controller.rb?如果在运行时重新开放类(或者这不适用于你的gem做什么?),在运行时扩展ApplicationController不是更好吗?你可以尝试从文件底部向上扫描,寻找一个'end' 'class ApplicationController'匹配 - 但我确信有可能被破坏的方法。 (你会尝试你自己的解析。) – struthersneil

+0

是的,我正在寻找直接编辑它。我可能会误解你'在运行时扩展'的意思,但是如果应用程序中存在一个具有相同名称的控制器,那么Rails会覆盖这些控制器,所以我不能只将这些方法写在我的引擎控制器中。 –

+0

考虑在一行中存在的类定义的可能性。或者一些尴尬但有效的情况,其中ApplicationController是通过调用Class.new()来定义的 – struthersneil

回答

3

好吧,我们一致认为,改变你的主机应用程序的application_controller.rb不要走的路。让我们看看通过gem将方法添加到ApplicationController类(实际上是ActionController :: Base)的不同方法。

我创建了一个非常简单的宝石。我想要它添加一个功能,rot13,这意味着任何控制器将能够拨打rot13('something!')找回'fbzrguvat!'。 (在现实生活中,你要补充这String ...)

你可以扩展ActionController::Base,像这样:

class ActionController::Base 
    def rot13 str 
    a = 'a'.ord 
    z = 'z'.ord 
    str.unpack('c*').map { |x| (a..z).cover?(x) ? (((x - a) + 13) % 26) + a : x }.pack('c*') 
    end 
end 

现在在我的应用程序可以调用任何rot13('ibvyn!')控制器和voila!

添加模块并通过Railtie钩子将其包含在ActionController :: Base中更安全。所以让我们添加一个Railtie。

我添加lib/rot13/railtie.rb如下:

module Rot13 
    class Rot13Railtie < Rails::Railtie 
    initializer "rot_13_railtie.extend_action_controller" do 
     ActiveSupport.on_load :action_controller do 
     # At this point, self == ActionController::Base 
     include Rot13::ControllerMethods 
     end 
    end 
    end 
end 

现在lib/rot13.rb看起来是这样的:

require "rot13/version" 
require "rot13/railtie.rb" if defined? Rails 

module Rot13 
    module ControllerMethods 
    def rot13 str 
     a = 'a'.ord 
     z = 'z'.ord 
     str.unpack('c*').map { |x| (a..z).cover?(x) ? (((x - a) + 13) % 26) + a : x }.pack('c*') 
    end 
    end 
end 

这是适合大多数的目的。

假设您不希望您的rot13方法在ActionController::Base中定义并可供所有控制器使用 - 比方说,您希望您的gem用户在逐控制器的基础上“加入”。

class ApplicationController < ActionController::Base 
    with_rot_13 

    # and so on... 
end 

相反的include Rot13::ControllerMethods你可能称之为on_load块内extend Rot13::ControllerOptIn到的ActionController::Base类级别添加with_rot_13方法,然后定义一个ControllerOptIn模块如下:

module Rot13 
    module ControllerMethods 
    # ... 
    end 

    module ControllerOptIn 
    def with_rot_13 
     include ControllerMethods 
    end 
    end 
end 

这就是它!

编辑:刚刚解决的另一个问题“为什么不是我的视图中可见的方法是什么?” - 你的方法没有被定义为一个帮手,所以它在你的看法是不会自动无可见它后面添加与controller,例如%p= controller.rot13 'blah'。幸运的是,您可以通过拨打helper_method来定义它为助手,例如

module ControllerOptIn 
    def with_rot_13 
    include ControllerMethods 
    helper_method :rot13 
    end 
end 

现在你可以做到这一点(注:HAML):

%p= controller.rot13 'hello there!' 
%p= rot13 'well how are ya?' 

但它不是伟大的,有在这里指定helper_method :rot13。你能直接从Rot13::ControllerMethods挖掘必要的符号吗?当然,只要你确定它是你想要的:

module ControllerOptIn 
    def with_rot_13 
    include ControllerMethods 
    helper_method *ControllerMethods.instance_methods 
    end 
end 
+0

我已经更新了我的问题,包括我的最新试验,而模块正在加载,方法似乎不可用,无论我指定它们作为实例或类方法。 –

+0

在控制器前加上您的呼叫,例如'controller.foo',如果你在视图中。 – struthersneil

+0

错误中的类是#<#:0x007ff350cd4ba0> - 的线索 - 未定义的局部变量或方法foo - 如果对你的控制器调用foo,则会发生'#它不知何故) – struthersneil