使用define_method覆盖Rails中的路径助手

时间:2018-05-23 18:14:16

标签: ruby-on-rails ruby

我需要在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)。

1 个答案:

答案 0 :(得分: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) %>

这使得路由更加清晰,因为它使得有一个自定义实现更加明显,而不是覆盖已知的实现。

这也可能是一种可能性(虽然未经测试,这应该像你现在的代码一样起作用&#34; path_helper.rb&#34;)

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