天天看点

Rails每周一题(二): routes

Rails其实很好懂,可视的源码和大量的注释,只看你有没有心去一窥究竟。今天就来看看貌似神秘的routes吧。

一个命令

首先,介绍一个rake命令。对于不了解routes定义规则的,或许看到routes.rb文件有点迷糊。不要紧,如果你想看看一个url到底对应了哪个controller以及action,就用rake routes展开所有的奥秘吧。

几多规则

routes的定义规则其实不多,让我们来一一分析下吧。不过要保持耐心。

分析routes.rb文件,首先得搞清楚,它的优先级是从上到下,谁先匹配谁先得。

1. 默认规则

# Install the default routes as the lowest priority.
  # Note: These default routes make all actions in every controller accessible via GET requests. You should
  # consider removing the them or commenting them out if you're using named routes and resources.
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
           

  自定义的路由规则往往需要覆盖默认的路由规则,所以,rails的routes模板把默认路由规则放在文件的最下面,并且鼓励考虑用具名路由替换它们。

2. 无名规则

  大家看到前面的map.connect,那么这个方法到底做了什么?

# Create an unnamed route with the provided +path+ and +options+. See
  # ActionController::Routing for an introduction to routes.
  def connect(path, options = {})
     @set.add_route(path, options)
  end
           

  可以看到,connect方法为我们生成了一个无名路由规则。

  我们可以如此生成一个无名规则:

map.connect 'products/:id', :controller => 'catalog', :action => 'view'
           

3. 具名规则

  为什么要有具名规则,主要是因为rails可以为具名规则生成一些url helper。关于routes的helper,待会儿再详细描述,先来看看具名规则吧。

map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
           

  如此,我们已经生成了一个名字叫做purchase的具名规则。

  看看源代码

def named_route(name, path, options = {}) #:nodoc:
    @set.add_named_route(name, path, options)
end
           

4. 单资源REST风格路由规则: map.resource

   我们定义这样一个规则: map.resource :account

   它会帮我们定义怎样的一个路由规则呢? 答案是一个遵循REST风格的路由规则,非常完美。

# maps these actions in the Accounts controller:
  class AccountsController < ActionController::Base
      # GET new_account_url
       def new
         # return an HTML form for describing the new account
      end
    
      # POST account_url
      def create
        # create an account
      end
   
      # GET account_url
      def show
        # find and return the account
      end
    
     # GET edit_account_url
      def edit
        # return an HTML form for editing the account
      end
   
      # PUT account_url
      def update
        # find and update the account
      end
    
     # DELETE account_url
      def destroy
        # delete the account
      end
   end
           

    让我们用rake routes看看它为我们生成了怎样的路由。

    account POST                                     /account                            {:controller=>"accounts", :action=>"create"}

    formatted_account POST                 /account.:format               {:controller=>"accounts", :action=>"create"}

    new_account GET                             /account/new                    {:controller=>"accounts", :action=>"new"}

    formatted_new_account GET         /account/new.:format       {:controller=>"accounts", :action=>"new"}

    edit_account GET                             /account/edit                     {:controller=>"accounts", :action=>"edit"}

    formatted_edit_account GET          /account/edit.:format        {:controller=>"accounts", :action=>"edit"}

    GET                                                     /account                              {:controller=>"accounts", :action=>"show"}

    GET                                                     /account.:format                 {:controller=>"accounts", :action=>"show"}

    PUT                                                     /account                               {:controller=>"accounts", :action=>"update"}

    PUT                                                      /account.:format                 {:controller=>"accounts", :action=>"update"}

    DELETE                                             /account                               {:controller=>"accounts", :action=>"destroy"}

    DELETE                                             /account.:format                 {:controller=>"accounts", :action=>"destroy"}

5. 集合资源REST风格路由规则

    我们定义一个这样的规则: map.resources :messages

#   map.resources :messages
 
    # will map the following actions in the corresponding controller:
      class MessagesController < ActionController::Base
       # GET messages_url
       def index
         # return all messages
       end
    
      # GET new_message_url
       def new
         # return an HTML form for describing a new message
       end
    
       # POST messages_url
       def create
         # create a new message
       end
   
     # GET message_url(:id => 1)
       def show
         # find and return a specific message
       end
   
     # GET edit_message_url(:id => 1)
       def edit
         # return an HTML form for editing a specific message
       end
   
       # PUT message_url(:id => 1)
       def update
         # find and update a specific message
       end
    
       # DELETE message_url(:id => 1)
       def destroy
         # delete a specific message
       end
     end
           

     messages GET                          /messages                                {:controller=>"messages", :action=>"index"}

     formatted_messages GET       /messages.:format                   {:controller=>"messages", :action=>"index"}

     POST                                           /messages                                {:controller=>"messages", :action=>"create"}

     POST                                           /messages.:format                   {:controller=>"messages", :action=>"create"}

     new_user GET                           /messages/new                       {:controller=>"messages", :action=>"new"}

     formatted_new_user GET       /messages/new.:format          {:controller=>"messages", :action=>"new"}

     edit_user GET                             /messages/:id/edit                  {:controller=>"messages", :action=>"edit"}

     formatted_edit_user GET        /messages/:id/edit.:format      {:controller=>"messages", :action=>"edit"}

     user GET                                      /messages/:id                          {:controller=>"messages", :action=>"show"}

     formatted_user GET                   /messages/:id.:format             {:controller=>"messages", :action=>"show"}

      PUT                                              /messages/:id                           {:controller=>"messages", :action=>"update"}

      PUT                                             /messages/:id.:format              {:controller=>"messages", :action=>"update"}

     DELETE                                        /messages/:id                          {:controller=>"messages", :action=>"destroy"}

      DELETE                                     /messages/:id.:format            {:controller=>"messages", :action=>"destroy"}

6. 嵌套规则

    简单嵌套规则

map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller
           

    用rake routes看看生成的路由规则,即可一目了然。

     一个更加复杂的嵌套规则

map.resources :products do |products|
    products.resources :comments
    products.resources :sales, :collection => { :recent => :get }
end
           

7. root

# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
map.root :controller => 'home'  
           

     root路由用于映射根目录。

8. namespace

     我们还可以定义一个命名空间,如这样。

map.namespace :admin do |admin|
    # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb)
   admin.resources :products
end
           

7. path_prefix, name_prefix等其他options

    当想让不同的method访问同一个url时,对应到不同的controller或者action,可以如是:

map.signup '/signup', :controller => 'people', :action => 'new', :conditions => {:method => :get}
map.signup '/signup', :controller => 'people', :action => 'create', :conditions => {:method => :post}
           

    还是不再赘述了,这样的option很多,在不明白的时候,不如直接看源代码吧。

routes helpers

    当我们定义了一个具名路由或者REST风格路由时,router helper会为我们生成一些帮助方法。

    比如当我们定义了一个这样的路由时:

map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase'
           

     生成的helper方法有:

     purchase_url(:id => 1)  #  http://test.url/products/1/purchase

     purchase_path(:id => 1)  #  /products/1/purchase

     hash_for_purchase_url(:id => 1)   # {:controller=>"sessions", :action=>"new", :use_route=>:new_session, :only_path=>false}

     hash_for_purchase_path(:id => 1)  # {:controller=>"sessions", :action=>"new", :use_route=>:new_session, :only_path=>true}

delete & put

     对delete和put method,现在大多数浏览器都不能处理,所以以post方法代替,并附上:method参数。

<% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>  
           

保持简单

     最后有点感触,随着项目的进行,routes会变得越来越复杂,越来越难以看懂。所以,我们要遵循保持简单,尽量使用REST风格的原则。