分享

Routing: Native Ruby Rewriting

 沁平 2008-03-30

1. An Introduction to Routes

This patch implements native Ruby rewriting for Rails, thus shifting the responsibility of URL parsing from the webserver to Rails itself. This has been a requested feature for two primary reasons.

  1. Not all webservers support rewriting. By moving this code to the framework Rails is able to function “out of the box” on almost all webservers.
  1. A rewriting implementation in Rails can also be used to generate custom URLs by linking it to the standard URL helpers such as url_for, link_to, and redirect_to.

Routes have been designed to be fully customizable by the average Ruby programmer. You’ll no longer have to be a htaccess wizard to have pretty URLs for your site.

Routes are defined in config/routes.rb. This file is a typical ruby source file which contains a document similar to

ActionController::Routing.draw do |map|
map.connect ‘:controller/:action/:id‘
end

Instead of wading through the tedious and obvious, let’s dive right in.

Example 1—Setting A Default Controller

Let’s say you’re one of those cool kids with a blog, and you’d like http://www./ to show your recent posts.

Case 1—BlogController#index

Lets say your BlogController’s index action is already setup to show the list of recent posts. To specify the default controller, we add :controller => ‘blog’ to the default route:

ActionController::Routing.draw do |map|
map.connect ‘:controller/:action/:id‘, :controller => ‘blog‘
end

As an additional perk, Routes will opt for the shortest path possible, so url_for :controller => ‘blog’, :action => ‘index’ will generate a url such as http://www./.

Case 2—BlogController#recent

Lets suppose that for whatever reason, your BlogController displays the recent posts with another action. We need to add a new Route that matches an empty path and points to BlogController’s recent action.

ActionController::Routing.draw do |map|
map.connect ‘‘, :controller => ‘blog‘, :action => ‘recent‘
map.connect ‘:controller/:action/:id‘
end

Notice that we put our new route before the default Rails route. Routes are recognized and generated in the order they are defined. By placing our custom route before the default Rails route, we can be assured that url_for :controller => ‘blog’, :action => ‘recent’ will generate a URL with an empty path, such as http://www./.

Note that if public/index.html exists, it will be served and Rails will not see the request. Be sure to delete the Congratulations page that ships with Rails.

Example 2—Setting up date-based URLs

Suppose you want to add a feature to your blog that let’s your visitors view posts by date. You’d like to allow them to browse by year, month, and day, and you already have an action which uses query or post parameters named year, month, and action.

The ideal URL for this looks like http://www./2005/02/14, with the month and day components being optional. Because you’re a sane person, you decide that the date order should be YYYY/MM/DD, rather than some arcane order such as MM/DD/YYYY.

Once again, we will want to add our custom route before the default Rails route. A first try at this might yield
  map.connect ‘:year/:month/:day‘, :controller => ‘blog‘, :action => ‘by_date‘
Although this will successfully match http://www./date/2005/02/14, it will fail to do so for tt>http://www./date/2005/02—we need to mark the :month and :day components as optional. To do so, we add :month => nil to our route:
  map.connect ‘date/:year/:month/:day‘, :controller => ‘blog‘, :action => ‘by_date‘,
:month => nil, :day => nil

Now our custom route will recognize URLs such as http://www./date/2005/02 or even http://www./date/2005. On the flip side, our custom route will also be used automatically when URLs are generated with the standard forms helpers such as link_to and url_for.

Now we run into another problem—our new route is too general, and it will catch all three-component URLs—such as posts/show/10. To combat this, we must tell place some requirements on our path components. Requirements are placed using a :requirements sub-hash like so:

  map.connect ‘date/:year/:month/:day‘, :controller => ‘blog‘, :action => ‘by_date‘,
:month => nil, :day => nil,
:requirements => {:year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/}

Example 3—Posts by Category

Lets move back to your BlogController. Suppose each of your posts belong to a category, and that we would like URLs to show items based on category, such as http://www./posts/rails. Lets assume that you also use the category “all” to mean show all categories. A quick route for this would be:

  map.connect ‘posts/:category‘, :controller => ‘blog‘, :action => ‘posts‘
The problem with this route is that your link_to statements will all have to include :category => ‘all’. It would be nicer if our templates could contain
<%= link_to ‘Posts‘, :controller => ‘blog‘, :action => ‘posts‘ %>

We can achieve this by giving the route a default value for :category:

  map.connect ‘posts/:category‘, :controller => ‘blog‘, :action => ‘posts‘, :category => ‘all‘

An added bonus is that the URL generated for the above link_to will omit the extraneous ‘all’ component, and be displayed as http://www./posts.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多