【问题标题】:Overriding path helpers in Rails using define_method在 Rails 中使用 define_method 覆盖路径助手
【发布时间】:2018-05-23 18:14:16
【问题描述】:

我需要在 Ruby on Rails 中重写很多路径辅助方法并从它们中调用 super。我的标准方法是:
path_helper.rb

def business_path(business)
  if params[:city_id] == 2
    moscow_business_path(business)
  else
    super
  end
end

但是我有很多这些方法,所以我想像这样动态定义它们:

  %i[root businesses business ...].each do |path_method|
    method_name = "#{path_method}_path"
    old_method = method(method_name)
    define_method(method_name) do |*args|
      if params[:city_id] == 2
        public_send("moscow_#{method_name}")
      else
        old_method.call(*args)
      end
    end
  end

但我收到此错误:

 /home/leemour/Ruby/burobiz/app/helpers/path_helper.rb:31:in `method': undefined method `root_path' for class `Module' (NameError) 
 from /home/leemour/Ruby/burobiz/app/helpers/path_helper.rb:31:in `block in <module:PathHelper>'
 from /home/leemour/Ruby/burobiz/app/helpers/path_helper.rb:29:in `each' 
 from /home/leemour/Ruby/burobiz/app/helpers/path_helper.rb:29:in `<module:PathHelper>' 
 from /home/leemour/Ruby/burobiz/app/helpers/path_helper.rb:1:in `<top (required)>' 
 from /home/leemour/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/activesupport-5.1.3/lib/active_support/dependencies.rb:476:in `load'

我猜还没有包含辅助模块,所以没有原始路径辅助方法可以用method(method_name) 捕获。然后我想我必须使用self.included 钩子,但我想不通。如何调整此代码以使其正常工作? (我不想使用 eval)。

【问题讨论】:

标签: ruby-on-rails ruby


【解决方案1】:

您没有尝试使用Kernel.method,而是尝试使用Rails.application.routes.url_helpers 方法?

%i[...].each do |path_method|
  define_method("#{path_method}_path") do |*args|
    if params[:city_id] == 2
      public_send("moscow_#{path_method}_path", *args)
    else
      Rails.application.routes.url_helpers.public_send("#{path_method}_path", *arsg)
    end
  end
end

【讨论】:

  • 谢谢,这行得通。但是,我也想知道在包含模块后如何定义方法,所以我有一个通用的解决方案,而不是使用url_helpers 进行破解。你能帮忙吗?
  • public_send 与 url_helpers 一起使用,以保持一致性。
  • @leemour:FWIW,我认为 your 的方式是这里的 hacky 方式。 ¯\_(ツ)_/¯ url_helpers 的使用在控制器上下文之外很常见(比如模型或类似的东西)。
  • @leemour:我自己没有尝试过,但我有一种很好的感觉,如果你对 url 助手对象(在初始化程序或其他东西中)进行猴子补丁,你会实现你想要的。
  • 同时发送 args,因为现在它们正在被丢弃,所以像 business_path(@business) 这样的东西会失败。
【解决方案2】:

你也可以把所有的电话都打包

def path_name_for(path_name,*args) 
  path = "#{path_name}_path"
  path.prepend("moscow_") if params[:city] == 2
  public_send(path,*args)
end 

那么在你看来,只需调用

<%= link_to 'Business Path', path_name_for(:business, @business) %>

这使路由对我来说更清晰一些,因为它更明显地表明存在自定义实现而不是覆盖已知实现。

这也可能是一种可能性(尽管未经测试,这应该像“path_helper.rb”中的现有代码一样起作用)

module PathHelper
  module MoscowRedirect
    def self.prepended(base) 
      %i[...].each do |path_name|
        define_method("#{path_name}_path") do |*args|
          params[:city] == 2 ? public_send("moscow_#{__method__}",*args) : super(*args)
        end
      end
    end
  end
  self.prepend(MoscowRedirect)
end 

【讨论】:

  • 我在整个项目中都有路径助手,所以替换它们不是一个理想的解决方案
  • @leemour 很好,我还添加了一个新的实现
  • 一切都是平等的,这也是我会采取的方法。干净多了。
猜你喜欢
  • 2013-02-27
  • 2012-04-02
  • 2015-08-27
  • 2014-07-02
  • 2014-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
相关资源
最近更新 更多