从数组或对象(而不是哈希)生成路由时,如何将“only_path:true”和“anchor:”等选项传递给“url_for”
Posted
技术标签:
【中文标题】从数组或对象(而不是哈希)生成路由时,如何将“only_path:true”和“anchor:”等选项传递给“url_for”【英文标题】:How to pass options like `only_path: true` and `anchor:` to `url_for` when generating a route from an array or object (instead of from a hash) 【发布时间】:2019-06-02 18:50:17 【问题描述】:我正在尝试更改一个函数——它目前只接受一个 URL(路径)string——使它更灵活,以便它也可以接受你可以接受的相同参数作为输入传递给url_for
。问题是,url_for
返回一个完整的 URL,包括协议/主机,我只想要路径部分...
url_for
有一个可爱的only_path: true
选项,可以跳过添加协议/主机。只要您将其作为单个哈希的一部分传递给url_for
,这将非常有效:
main > app.url_for(controller: 'users', action: 'index', only_path: true)
=> "/users"
但是当您将 array 或 模型对象 传递给 url_for
时,如何传递选项?
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'
你不能!显然,如果您要传递哈希以外的任何内容,则在语法上甚至不可能传递选项哈希,因为 its arity 是 0..1
并且它只需要一个参数:url_for(options = nil)
。
所以我的问题是,是否有一个 Rails 助手可以采用与 url_for
相同的选项(包括 anchor:
)但返回路径?
我想添加一个使用 URI.parse(path).path
的 this one (可能我现在要做的)不会太难...但这似乎效率低下、不优雅且不一致。
低效和不雅,因为它会生成一个包含额外不需要的信息的字符串,然后必须对其进行解析,将其转换为结构化数据结构,然后再将其转换返回到一个字符串没有不需要的信息。
不一致因为:
Rails 为每个 _url
路由助手包含一个 _path
变体。为什么它没有url_for
的内置“路径”变体(或者有它并且它被称为其他东西?)?
其他接受对象或数组的路由助手——例如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 的未来版本中,通过提交拉取请求)以允许主题(模型对象,数组或哈希)和要传入任意数量的可选options
?
可能最初的 url_for
是在 Ruby 有关键字参数之前编写的。但是现在,这样做非常简单:接受一个“对象”加上任意数量的可选关键字选项:
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
以使其尽可能向后兼容,同时还允许您使用数组 + 关键字选项调用它?
【问题讨论】:
【参考方案1】:这是一种潜在的解决方案,显示了我正在寻找的东西:
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
(与original Rails version的来源比较)
我还没有尝试针对这段代码运行 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"
如果您发现其中的任何错误或有任何改进或可以想出更简洁的方法来完成此操作,请告诉我。
请发布您可能有的任何改进版本作为答案!
【讨论】:
以上是关于从数组或对象(而不是哈希)生成路由时,如何将“only_path:true”和“anchor:”等选项传递给“url_for”的主要内容,如果未能解决你的问题,请参考以下文章