ruby 如何传递'only_path这样的选项:从数组或对象(而不是从哈希)生成路由时,将'锚:'设置为'url_for'

yyhrrdl8  于 2023-03-08  发布在  Ruby
关注(0)|答案(1)|浏览(93)

我试图修改一个函数--它目前只接受URL(path)* string *--使它更灵活,这样它也可以接受您传递给url_for的相同参数作为输入。问题是,url_for返回一个完整的URL,包括protocol/host,而我只需要路径部分...
url_for有一个可爱的only_path: true选项,可以跳过添加协议/主机,只要你把它作为一个散列的一部分传递给url_for,这个选项就能很好地工作:

main > app.url_for(controller: 'users', action: 'index', only_path: true)
=> "/users"

但是,当您向url_for传递 * array * 或 * model object * 时,如何传递选项呢?

main > app.url_for(user, only_path: true)
ArgumentError: wrong number of arguments (given 2, expected 0..1)
from /gems/actionpack-5.1.6/lib/action_dispatch/routing/url_for.rb:166:in `url_for'

main > app.url_for([user, :notification_preferences], only_path: true)
ArgumentError: wrong number of arguments (given 2, expected 0..1)
/actionpack-5.1.6/lib/action_dispatch/routing/url_for.rb:166:in `url_for'

显然,如果你传递的不是哈希值,那么从语法上来说,传递选项哈希值是不可能的,因为它的arity是0..1,而且它只接受一个参数:url_for(options = nil).
所以我的问题是,是否有一个Rails帮助器,它采用与url_for相同的选项(包括anchor:),但返回一个路径?
我想添加一个使用URI.parse(path).path(可能是我现在要做的)的this one并不太难......但这似乎效率低下、不优雅且不一致。

    • 低效不优雅**,因为它生成包含额外不需要的信息的字符串,然后必须解析它,将它转换为结构化数据结构,然后将它转换 * 回 * 没有 * 不需要的信息的字符串。
    • 不一致**原因:
  1. Rails为每个_url route helper都提供了一个_path变量,为什么它没有内置的url_for "path"变量(或者它有,但它被称为其他变量?)
    1.* Other * 接受对象或数组的路由助手(如polymorphic_path)也允许您传入选项:
    示例:
main > app.polymorphic_url [user, :notification_preferences], anchor: 'foo'
=> "http://example.com/users/4/notification_preferences#foo"

main > app.polymorphic_path [user, :notification_preferences], anchor: 'foo'
=> "/users/4/notification_preferences#foo"

main > app.polymorphic_path user, anchor: 'foo'
=> "/users/4#foo"

ActionDispatch::Routing::RouteSet实际上有一个path_for

# strategy for building urls to send to the client                        
  PATH    = ->(options) { ActionDispatch::Http::URL.path_for(options) }     
  UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }    

  def path_for(options, route_name = nil)                                   
    url_for(options, route_name, PATH)                                      
  end                                                                       

  # The +options+ argument must be a hash whose keys are *symbols*.         
  def url_for(options, route_name = nil, url_strategy = UNKNOWN)            
    options = default_url_options.merge options    
    ...
  • 显然,只是不是ActionDispatch::Routing::UrlFor。无论如何,ActionDispatch::Routing::RouteSet#path_for在内部埋得太深,对我没有帮助;我需要一个可以从控制器/视图调用的助手。
    那么,什么是一个好的解决方案,它是优雅的,与其他Rails路由助手一致,并且相对高效(没有URI.parse)?
    更好的是,内置url_for的方法签名不能简单地修改(在Rails的未来版本中,通过提交一个pull请求)以允许传入一个subject(模型对象、数组或散列)* 和 * 任意数量的可选options,这有什么原因吗?
    最初的url_for可能是在Ruby有关键字参数之前编写的,但是现在,要做到这一点已经很容易了:接受"object"加上任意数量的可选关键字选项:
def url_for(object = nil, **options)
  puts "object: #{object.inspect}"
  puts "options: #{options.inspect}"
end

main > url_for ['user', :notification_preferences], anchor: 'anchor'
object: ["user", :notification_preferences]
options: {:anchor=>"anchor"}
=> nil

main > url_for ['user', :notification_preferences], only_path: true
object: ["user", :notification_preferences]
options: {:only_path=>true}

有什么理由不能/不应该将url_for的方法签名更改为url_for(object = nil, **options)吗?
如何修改url_for/full_url_for,使其尽可能保持向后兼容,同时允许使用数组+关键字选项调用它?

zaqlnxep

zaqlnxep1#

这里有一个潜在的解决方案,显示了我正在寻找的东西:

def url_for(object = nil, **options)
    full_url_for(object, **options)
  end

  def full_url_for(object = nil, **options)
    path_or_url_for = ->(*args) {
      if options[:only_path]
        _routes.path_for(*args)
      else
        _routes.url_for( *args)
      end
    }
    polymorphic_path_or_url = ->(*args) {
      if options[:only_path]
        polymorphic_path(*args)
      else
        polymorphic_url( *args)
      end
    }

    case object
    when nil, Hash, ActionController::Parameters
      route_name = options.delete :use_route
      merged_url_options = options.to_h.symbolize_keys.reverse_merge!(url_options)
      path_or_url_for.(merged_url_options, route_name)
    when String
      object
    when Symbol
      HelperMethodBuilder.url.handle_string_call self, object
    when Array
      components = object.dup
      polymorphic_path_or_url.(components, components.extract_options!)
    when Class
      HelperMethodBuilder.url.handle_class_call self, object
    else
      HelperMethodBuilder.url.handle_model_call self, object
    end
  end

(与原始Rails版本的源代码比较)
我还没有试着对这段代码运行Rails测试套件,所以它很可能遗漏了一些东西,但到目前为止,它似乎对我的简单测试有效:

main > app.url_for(controller: 'users', action: 'index')
=> "http://example.com/users"

main > app.url_for(controller: 'users', action: 'index', only_path: true)
=> "/users"

main > app.url_for [user, :notification_preferences]
=> "http://example.com/users/4/notification_preferences"

main > app.url_for [user, :notification_preferences], only_path: true
=> "/users/4/notification_preferences"

让我知道,如果你发现任何错误,在这方面或有任何改进,或可以想到一个更清洁的方式来完成这一点。
请张贴任何改进的版本,你可能有作为答案!

相关问题