*_path与*_url的由来
区别
path是相对路径,url是绝对路径
*_path are for views because ahrefs are implicitly linked to the current URL. So it'd be a waste of bytes to repeat it over and over.
In the controller, though, *_url is needed for redirect_to because the HTTP specification mandates that the Location: header in 3xx redirects is a complete URL.
model为复数的会对应到action=>index, 为单数时需要传递id参数并对应到action=>show
例如,如对于user而言:
users_url # => http://localhost:3000/users
users_path # => /users
user_url(1) # => http://localhost:3000/users/1
user_path(1) # => /users/1
url 的名字 REST方法 对应的url的表达式 对应的controller#action
----------------------------------------------------------------------------------
travels_path GET /travels(.:format) travels#index
new_travel_path GET /travels/new(.:format) travels#new
edit_travel_path GET /travels/:id/edit(.:format) travels#edit
travel_path GET /travels/:id(.:format) travels#show
travel_path PATCH/PUT /travels/:id(.:format) travels#update
travel_path POST /travels/:id(.:format) travels#create
travel_path DELETE /travels/:id(.:format) travels#destroy
在route_set中被初始化,然后在view或controller中才能被调用。(是method)
何时生成
在WelcomeController的index方法中检查:welcome_index_path的source_location,得:
[3] pry(#)> method(:welcome_index_path).source_location
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb", 283]
查看如下:
def define_url_helper(mod, route, name, opts, route_key, url_strategy)
helper = UrlHelper.create(route, opts, route_key, url_strategy)
mod.module_eval do
define_method(name) do |*args|
last = args.last
options = case last
when Hash
args.pop
when ActionController::Parameters
if last.permitted?
args.pop.to_h
else
raise ArgumentError, ActionDispatch::Routing::INSECURE_URL_PARAMETERS_MESSAGE
end
end
helper.call self, args, options
end
end
end
在define_method前加入调试语句
binding.pry if /welcome_index_path/ =~ name
重新rails server,结果就立即进入了pry。查看调用栈
[7] pry(#)> _bs_
=> [#<binding:70310498294340 #<module:0x007fe4dfcbf3b0="">.block in define_url_helper /home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:283>,
#<binding:70310498342680 actiondispatch::routing::routeset::namedroutecollection#define_url_helper="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" route_set.rb:282="">,
#<binding:70310498390220 actiondispatch::routing::routeset::namedroutecollection#add="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" route_set.rb:114="">,
#<binding:70310498454920 actiondispatch::routing::routeset#add_route="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" route_set.rb:516="">,
#<binding:70310498501300 actiondispatch::routing::mapper#add_route="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:1657="">,
#<binding:70310498550160 actiondispatch::routing::mapper#decomposed_match="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:1628="">,
#<binding:70310498607320 actiondispatch::routing::mapper#block="" in="" map_match="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:1927="">,
#<binding:70310498704400 actiondispatch::routing::mapper#map_match="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:1910="">,
#<binding:70310498753600 actiondispatch::routing::mapper#match="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:1599="">,
#<binding:70310498810300 actiondispatch::routing::mapper#map_method="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:719="">,
#<binding:70310498859480 actiondispatch::routing::mapper#get="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" mapper.rb:680="">,
#<binding:70310498907560 actiondispatch::routing::mapper#block="" in="" <top="" (required)=""> /home/z/test_rails/dapo/config/routes.rb:4>,
#<binding:70310498964660 actiondispatch::routing::routeset#eval_block="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" route_set.rb:390="">,
#<binding:70310499014220 actiondispatch::routing::routeset#draw="" home="" z="" .rbenv="" versions="" 2.4.0="" lib="" ruby="" gems="" 2.4.0="" gems="" actionpack-5.0.1="" lib="" action_dispatch="" routing="" route_set.rb:372="">,
#<binding:70310499094380 object#<top="" (required)=""> /home/z/test_rails/dapo/config/routes.rb:1>,
#...</binding:70310499094380></binding:70310499014220></binding:70310498964660></binding:70310498907560></binding:70310498859480></binding:70310498810300></binding:70310498753600></binding:70310498704400></binding:70310498607320></binding:70310498550160></binding:70310498501300></binding:70310498454920></binding:70310498390220></binding:70310498342680></binding:70310498294340>
可见源自在config/routes.rb
怎样返回url
对*_url进行trace
[2] pry(#)> binding.trace_tree{welcome_index_url}
WelcomeController#block in index (pry):2
└─WelcomeController#block (2 levels) in define_url_helper $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:286
└─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#call $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:171
├─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#optimize_routes_generation? $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:191
│ └─#..
├─WelcomeController#url_options $GemPath0/gems/actionpack-5.0.1/lib/action_controller/metal/url_for.rb:28
│ └─#...
├─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#optimized_helper $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:183
│ └─#...
└─ActionDispatch::Routing::RouteSet.block in $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:306
└─ActionDispatch::Http::URL.url_for $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/http/url.rb:48
└─#...
可见使用的是OptimizedUrlHelper(未研究),最后用route_set.rb:306的block,里面调了个url_for来返回url
而*_path也类似,用的是route_set.rb:305的block,里面调了个path_for
[5] pry(#)> binding.trace_tree{welcome_index_path}
WelcomeController#block in index (pry):5
├─WelcomeController#block (2 levels) in define_url_helper $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:286
└─WelcomeController#block (2 levels) in define_url_helper $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:286
└─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#call $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:171
├─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#optimize_routes_generation? $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:191
│ └─#...
├─WelcomeController#url_options $GemPath0/gems/actionpack-5.0.1/lib/action_controller/metal/url_for.rb:28
│ └─#...
├─ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper::OptimizedUrlHelper#optimized_helper $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:183
│ └─#...
└─ActionDispatch::Routing::RouteSet.block in $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/routing/route_set.rb:305
└─ActionDispatch::Http::URL.path_for $GemPath0/gems/actionpack-5.0.1/lib/action_dispatch/http/url.rb:68
这两个block就是
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
*_url那个叫做UNKNOWN,看上去有点奇怪,搜一下哪里用到或是否有其他名字指向他
$ actionpack-5.0.1 git:(master) grep UNKNOWN -rn *
lib/action_dispatch/routing/route_set.rb:115: define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
lib/action_dispatch/routing/route_set.rb:303: UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
lib/action_dispatch/routing/route_set.rb:685: def url_for(options, route_name = nil, url_strategy = UNKNOWN)
唯一与config/routes.rb调用栈能联系起来的,就是ActionDispatch::Routing::RouteSet::NamedRouteCollection#add了
def add(name, route)
key = name.to_sym
path_name = :"#{name}_path"
url_name = :"#{name}_url"
if routes.key? key
@path_helpers_module.send :undef_method, path_name
@url_helpers_module.send :undef_method, url_name
end
routes[key] = route
define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
@path_helpers << path_name
@url_helpers << url_name
end
而这里的@path_helpers_module和@url_helpers_module,可以猜到,就是会mixin到controller的匿名module
[9] pry(#)> self.class.ancestors
=> [WelcomeController,
#,
ApplicationController,
#,
#,
#,
ActionController::Base,
Turbolinks::Redirection,
Turbolinks::Controller,
ActiveRecord::Railties::ControllerRuntime,
ActionDispatch::Routing::RouteSet::MountedHelpers,
ActionController::ParamsWrapper,
#...
[13] pry(#)> self.class.ancestors[4].instance_methods(false)
=> [:rails_info_properties_path, :rails_info_routes_path, :rails_info_path, :rails_mailers_path, :welcome_index_path, :welcome_go_to_index_path]
[14] pry(#)> self.class.ancestors[5].instance_methods(false)
=> [:rails_info_properties_url, :rails_info_routes_url, :rails_info_url, :rails_mailers_url, :welcome_index_url, :welcome_go_to_index_url]