【问题标题】:Best way to insert methods with rails rake task使用 rails rake 任务插入方法的最佳方法
【发布时间】:2013-10-22 01:09:46
【问题描述】:

我正在为 Rails 4 编写一个 gem,它需要向 ApplicationController 添加一些方法,我认为最好的方法是编写一个 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 行,我收到以下错误:

#

的未定义局部变量或方法 `foo'

【问题讨论】:

  • 您希望您的 gem 真正地编辑 application_controller.rb?在运行时通过重新打开 gem 中的类来扩展 ApplicationController 不是更好吗(或者这不适用于您的 gem 做什么?)您可以尝试从文件底部向上扫描以查找“结束”由'class ApplicationController'匹配——但我确信有些方法可能会被破坏。 (您将尝试自己的解析。)
  • 是的,我想直接编辑它。我可能误解了“在运行时扩展”的意思,但是如果应用程序中存在同名的控制器,Rails 会覆盖控制器,所以我不能只在引擎的控制器中编写方法。
  • 考虑一个类定义存在于一行的可能性。或者一些尴尬但有效的情况,其中 ApplicationController 是通过调用 Class.new() 定义的
  • 考虑调用 ApplicationController.define_method(...) 或类似的东西。或者考虑在运行时将自己的方法修补到“实时”类中。

标签: ruby-on-rails gem ruby-on-rails-4


【解决方案1】:

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

我创建了一个非常简单的 gem。我希望它添加一个功能,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

您可以在 on_load 块中调用 extend Rot13::ControllerOptIn 而不是 include Rot13::ControllerMethods,以在 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

【讨论】:

  • 我已经更新了我的问题以包括我的最新试验,在加载模块时,无论我将它们指定为实例方法还是类方法,这些方法似乎都不可用。
  • 使用控制器为您的呼叫添加前缀,例如controller.foo 如果您在视图中。
  • 错误中的类是线索--undefined local variable or method foo for #&lt;#:0x007ff350cd4ba0&gt;--如果对您的控制器调用 foo(并且不知何故不存在),那将是 #&lt;YourController:0x007ffabcdefabcdef&gt; 跨度>
  • 前缀也没有改变任何东西。该方法不绑定任何东西,它相当于 erb 中的&lt;%= foo %&gt;
  • 当您在视图的上下文中时,您实际上是在匿名类的实例中,而不是控制器的实例中。 (您可以访问控制器的实例变量的副本,这可能会产生误导。)尝试回到我在此处概述的方法(我保证有效!)并从您的控制器中调用 Rails.logger.debug(foo) 并观察开发记录。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-07
  • 2017-09-30
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多