Routing
I dreamed a thousand new paths. . . I woke and walked my old one.
—Chinese proverb
    没想到原作者还懂中国谚语,我来翻译翻译:
夜里想得千条路,明朝依旧卖豆腐 --中国谚语


Rails的路由系统检测进入的请求以及确定action的动作。好处不止于此。它也是Rails RESTful的基础。
Routing系统映射URL到actions。它是通过规则来起作用的,这些规则是你来指定的。

这些规则都写到这个文件里,config/routes.rb

routing 系统实际上只做两件事:
1.映射request到actions
2.为你的一些方法生成URLs,比如link_to, form_for等。
当你这么写:

<%= link_to “Items”, :controller => “items”, :action => “list” %>
routing系统会为link_to helper方法提供这个url:
很好很强大!

The Two Purposes of Routing
识别URLs是有用的:
[url]http://localhost:3000/myrecipes/apples[/url] 嗯,我们现在该做些什么?!
生成URLs是有用的:
<a href=”[url]http://localhost:3000/myrecipes/apples[/url]”>My Apple Recipes</a>
每次都得手工输入,这还有啥子乐趣嘛!

规则的写法(老版本):
map.connect ‘myrecipes/:ingredient’,
:controller => “recipes”,
:action => “show”

这个例子你可以看到:
一个静态字符串 (myrecipes)
一个通配URL组件(:ingredient)
范围参数   (:controller => “recipes”,:action => “show”)

The routes.rb File
ActionController::Routing::Routes.draw do |map|

# The priority is based upon order of creation
# First created gets highest priority.
# Sample of regular route:
# map.connect ‘products/:id’, :controller => ‘catalog’,
:action => ‘view’
The routes.rb File 63
# Keep in mind you can assign values other than
# :controller and :action
# Sample of named route:
# map.purchase ‘products/:id/purchase’, :controller => ‘catalog’,
:action => ‘purchase’
# This route can be invoked with purchase_url(:id => product.id)
# You can have the root of your site routed by hooking up ‘’
# -- just remember to delete public/index.html.
# map.connect ‘’, :controller => “welcome”
# Allow downloading Web Service WSDL as a file with an extension
# instead of a file named ‘wsdl’
map.connect ‘:controller/service.wsdl’, :action => ‘wsdl’
# Install the default route as the lowest priority.
map.connect ‘:controller/:action/:id.:format’
map.connect ‘:controller/:action/:id’

end

里面的map是ActionController::Routing::RouteSet::Mapper类的实例。

默认匹配规则:
:controller/:action/:id
auctions / show / 1

注意,路由的匹配规则就像下面这行代码:
case string
when /x/
puts “Matched ‘x’!”
when /./
puts “Matched any character!”
end
如果找到了匹配的结果,就会忽略下面的。所以要注意规则的次序。


注意到倒数第二行了吗?
map.connect ‘:controller/:action/:id.:format’
它对应类似的url:
这个params[:formate]影响着respond_to方法的调用:
respond_to方法允许你的action返回指定的format。
def show
@item = Item.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => @item.to_xml }
end
end

这里会返回html和xml格式的文本。
Resolving localhost... 127.0.0.1, ::1
Connecting to localhost|127.0.0.1|:3000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 295 [application/xml]
<item>
<created-at type=”datetime”>2007-02-16T04:33:00-05:00</created-at>
<description>Violin treatise</description>
<id type=”integer”>3</id>
<maker>Leopold Mozart</maker>
<medium>paper</medium>
<modified-at type=”datetime”></modified-at>
<year type=”integer”>1744</year>
</item>
命令行里执行wget来看得到的结果,可以看到的是xml文本类型返回。

respond_to and the HTTP-Accept Header
当你在请求里设置了HTTP-Accept header,就不需要这个.format部分了。
wget [url]http://localhost:3000/items/show/3[/url] -O - —header=”Accept:
text/xml”
Resolving localhost... 127.0.0.1, ::1
Connecting to localhost|127.0.0.1|:3000... connected.
HTTP request sent, awaiting response...
200 OK
Length: 295 [application/xml]
<item>
<created-at type=”datetime”>2007-02-16T04:33:00-05:00</created-at>
<description>Violin treatise</description>
<id type=”integer”>3</id>
<maker>Leopold Mozart</maker>
<medium>paper</medium>
<modified-at type=”datetime”></modified-at>
<year type=”integer”>1744</year>
</item>


The Empty Route
# You can have the root of your site routed by hooking up ‘’
# -- just remember to delete public/index.html.
# map.connect ‘’, :controller => “welcome”
当你请求[url]http://localhost:3000[/url]的时候,就会返回controller welcome的index页面。
在Rails2.0+版本里我们是使用这样的路由:
map.root :controller => “homepage”


Writing Custom Routes 定制路由
在你写你自己路由的时候,你需要考虑两件事:
在识别方面,意味着你的路由必须提供全面的信息- 是否是硬编码,是否要从URL接收一个值来选择哪个controller哪个action。
在生成URL方面,你需要保证你的硬编码参数和通配符是否提供了足够的值供route使用。


Using Regular Expressions in Routes

map.connect ‘:controller/show/:id’,:action => “show”, :requirements => { :id => /\d+/ }
可以用requirements来指定正则表达式限定你的url。

Default Parameters and the url_for Method
link_to ,  redirect_to 等方法实际上是在底层调用url_for方法。


Named Routes
具名路由和restful相关。

Creating a Named Route

map.help ‘help’,
:controller => “main”,
:action => “show_help”

当你创建具名路由的时候,你实际上也创建了至少两个方法,上面的例子里是 help_url 和 help_path。这两个方法不同点是:_url是绝对路径,_path是相对路径。

因此,当重定向的时候,需要指定绝对路径,那么就应该把_url版本的方法作为参数传给redirect_to方法。事实上,不管是redirect_to,permalinks还是其他,Rails之道是用_path来代替_url。

A Little More Sugar with Your Sugar?
如果你不想在url后面老是跟一个id,那么你可以在model文件里使用to_param方法来覆盖这个值:
def to_param
description.gsub(/\s/, “-”).gsub([^\W-], ‘’).downcase
end

这样你就可以得到类似的url:
/auction/3/item/cello-bow

不过你调用的时候就需要这样来用了:
上面例子里是model的description属性被传入,代替了原来的id,那么:
Item.find_by_munged_description(params[:id])
表面看上去是id,实际上传进去的是cello-bow这个属性值。

The Special Scope Method with_options
看这段路由:
map.help ‘/help’, :controller => “main”, :action => “help”
map.contact ‘/contact’, :controller => “main”, :action => “contact”
map.about ‘/about’, :controller => “main”, :action => “about”

我们可以用with_options来改写:
map.with_options :controller => “main” do |main|
main.help ‘/help’, :action => “help”
main.contact ‘/contact’, :action => “contact”
main.about ‘/about’, :action => “about”
end

注意:
你可能会考虑到一个性能问题,route识别和生成url,可能会比较慢,尤其是当有大量的自定义路由的时候,不过在每小时pv没达到上千的时候,不必担心这个问题。解决的办法是缓存这些url或者是转换成文本。

有个好消息就是,在Rails2.3版本, routing的性能已经提升了50%。