1

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

class ApplicationController < ActionController::Base

  def existing_methods
    foo
  end

  # insert my methods here

end

编辑

在尝试了以下建议后,但无济于事,我尝试关注此帖子并最终尝试以下操作:

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,我收到以下错误:

#<#:0x007ff350cd4ba0> 的未定义局部变量或方法“foo”

4

1 回答 1

3

好的,我们一致认为更改主机应用程序的 application_controller.rb 不是可行的方法。让我们看看通过 gem 向 ApplicationController 类(实际上是 ActionController::Base)添加方法的不同方法。

我创建了一个非常简单的宝石。我希望它添加一个函数 rot13,这意味着任何控制器都可以调用rot13('something!')get back '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您可以extend Rot13::ControllerOptIn在该块中调用以在类级别on_load添加一个方法,然后定义一个模块,如下所示:with_rot_13ActionController::BaseControllerOptIn

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
于 2013-10-22T10:51:58.523 回答