ルーティング

ルーティングって、要するにURLからどのアクションを実行するかを決めることね!

RESTfulインタフェースの定義

  • Railsは、RESTfulが基本ですね!
    • RESTfulとは、HTTPプロトコルの基本である、GET/POST/PUT/DELETEで、データの取得/操作を行う、って考え方だね。
    • なんだけど、現状の大抵のブラウザじゃ、GET/POSTしか実装されてないんだよなー
    • という訳で、Railsでは疑似的に、全てのHTTPプロトコルを実装しているってのが、なんとも微妙…。
    • ただ、考え方としては、HTTPの基本に従った方が単純でいいってのは良く判る。
  • で、その定義は、config/routes.rbに書く訳だ。
    • まあ、既にScaffoldingで出てるけど、こんな感じで書けば、
       resources :members
      
    • こんな感じで、ルーティングされる訳だ。確認は「rake routes」でね!
              members GET    /members(.:format)                     {:action=>"index", :controller=>"members"}
                      POST   /members(.:format)                     {:action=>"create", :controller=>"members"}
           new_member GET    /members/new(.:format)                 {:action=>"new", :controller=>"members"}
          edit_member GET    /members/:id/edit(.:format)            {:action=>"edit", :controller=>"members"}
               member GET    /members/:id(.:format)                 {:action=>"show", :controller=>"members"}
                      PUT    /members/:id(.:format)                 {:action=>"update", :controller=>"members"}
                      DELETE /members/:id(.:format)                 {:action=>"destroy", :controller=>"members"}
      
      • この辺のURLは、form_for/form_tag/link_to等のビューヘルパーでは自動生成されるから、ぜひ使うべき!
      • ちなみに、ビューヘルパーは、***_pathは相対パスを、***_urlはURL全体を返すらしい。
    • で、テーブルのルーティングをイメージしてる複数データの定義の[resources]だけど、単一リソースの場合は[resource]があるよ
      • こんな感じで書けば、
         resource :config
        
      • こうルーティングされる、と
                 config POST   /config(.:format)                      {:action=>"create", :controller=>"configs"}
             new_config GET    /config/new(.:format)                  {:action=>"new", :controller=>"configs"}
            edit_config GET    /config/edit(.:format)                 {:action=>"edit", :controller=>"configs"}
                        GET    /config(.:format)                      {:action=>"show", :controller=>"configs"}
                        PUT    /config(.:format)                      {:action=>"update", :controller=>"configs"}
                        DELETE /config(.:format)                      {:action=>"destroy", :controller=>"configs"}
        
      • [resources]との違いは、indexとidが無い事ね。

RESTfulインタフェースのカスタマイズ

  • まあ、resourcesで簡単にアクセスできるのはいいけど、当然もっと細かい事をしたくなるよね!
  • ルートパラメータで簡単に制約を掛けるには、resourcesの[:constraints]で正規表現を書けばOK!
    • こんな感じで書けば、
       resources :books, :constraints => {:id => /[0-9]{1,2}/}
      
    • [http://localhost:3000/books/aaa]とかにアクセスすると、「Not Found」じゃなくて、「Unknown action」ってエラーになる
      • うーん、本だと「Routing Error」だけど、なんか違うのかな?
    • あー、複数のresources に一括で設定するには、こんな記述でできるんだねー
       constraints(:id => /[0-9]{1,2}/) do
         resources :books
         resources :reviews
       end
      
  • 正規表現だけじゃ足りない場合には、自分で制約クラスを作って、それを使うことも可!
    • こんな感じで制約用クラス(models/TimeConstraint.rb)を作って、matches?メソッドを書いて、
      class TimeConstraint
       def matches?(request)
         corrent = Time.now
         corrent.year == 2012 && corrent.month == 2
       end
      end
      
      • この場合、2012年1月のみOK、みたいな感じね!
    • リソース定義に、こんな感じで書くと、
      require 'TimeConstraint'
       …中略…
       resources :users, :constraints => TimeConstraint.new
      
    • これで、イケルはずなんだが…、なんか知らんが全くmatches?が効かん…。
      • なんかキャッシュでもされてるかと思って、WEBrick再起動&ブラウザ再起動したけど、同じだ…
      • matches?に[return false]を書いても一緒なんで、なんか変だな?後で調べるか…。
  • コントローラやURLヘルパーを変更するには、:controllers/:asを使って変更する
    • んー、本質的にこれ、使うのまずい気がするんだが…。まあ、こう書けばいいらしい。
       resources :users, :controller => 'members' # これでMembersControllerにマッピング
       resources :reviews, :as => 'comments'      # これでcomments_pathとかのヘルパーを使える
      
  • コントローラーがいっぱい同じレベルに作られたら大変なんで、namespaceとかscopeが用意されてるよ
    • まずは、コントローラを作って見る
      rails g controller Admin::Books
      
    • で、作ったコントローラで、namespaceを使ってRESTfulインタフェースをroutes.rbに定義
       namespace 'admin' do
         resources :books
       end
      
    • これで、/admin/booksとかがルーティングされる、と。
      • ただ、これだとViewが無いからURLにアクセスしちゃうとエラーになるんで、rake routesで確認するだけね。
    • で、単にコントローラの場所を動かすだけで、URLとかは変えたくない!って場合は、scopeを使うんだねー
       scope :module => 'admin' do
         resources :books
       end
      
      • 確かに、こっちだとrake routesで確認すると、下みたいな違いがあるんだねー
        admin_books GET    /admin/books(.:format)                 {:action=>"index", :controller=>"admin/books"} 
              books GET    /books(.:format)                       {:action=>"index", :controller=>"admin/books"}
        
  • 自前のGETメソッドの追加とかは、collection/memberブロックで定義できるよー
    • こんな感じで書くと、
       resources :reviews do
         collection do
           get 'unapproval' # GETに[reviews/unapproval]を追加
         end
         member do
           get 'draft'      # GETに[reviews/draft]を追加
         end
       end
      
    • ルーティングにこんな感じで追加される、と
      unapproval_reviews GET    /reviews/unapproval(.:format)          {:action=>"unapproval", :controller=>"reviews"}
            draft_review GET    /reviews/:id/draft(.:format)           {:action=>"draft", :controller=>"reviews"}
      
      • collection はindexみたいな複数オブジェクトで、member はshowみたいな単数オブジェクトの形式だね!
      • ブロック内が1つだけなら、:onを使って、こんな感じにも書けるんだねー
         resources :reviews do
           get 'unapproval', :on => :collection
           get 'draft', :on => :member
         end
        
    • とはいえ、これ、いっぱい登録しなきゃならん状況って、そもそもおかしいから注意!
  • 無効化するなら、:only/:exceptオプションで。変更なら、:path_namesで!
    • まあ、よくある「絶対削除しない」とかを書く場合だよねー
    • こんな感じで、書くと、
       resources :users, :except => ['show','destroy']
      
    • ルーティングから、showとdestroyが削除される、と。
      • まあ、:onlyなら逆に必要なモノだけ書く訳だ。
    • で、無効じゃなくて、別のURLに変えたい場合は、:path_namesで定義する、と。こんな感じで書けば、
       resources :users, :path_names => { :new => 'insert', :edit => 'revise'}
      
    • こんな感じのルーティングになる、と。
               new_user GET    /users/insert(.:format)                {:action=>"new", :controller=>"users"}
              edit_user GET    /users/:id/revise(.:format)            {:action=>"edit", :controller=>"users"}
      
  • へー、resources って、ネストもできるんだね。
    • こんな感じで書くと、
       resources :books do
         resources :reviews
       end
      
    • ルーティングにこんなのが追加される、と
           book_reviews GET    /books/:book_id/reviews(.:format)          {:action=>"index", :controller=>"reviews"}
                        POST   /books/:book_id/reviews(.:format)          {:action=>"create", :controller=>"reviews"}
        new_book_review GET    /books/:book_id/reviews/new(.:format)      {:action=>"new", :controller=>"reviews"}
       edit_book_review GET    /books/:book_id/reviews/:id/edit(.:format) {:action=>"edit", :controller=>"reviews"}
            book_review GET    /books/:book_id/reviews/:id(.:format)      {:action=>"show", :controller=>"reviews"}
                        PUT    /books/:book_id/reviews/:id(.:format)      {:action=>"update", :controller=>"reviews"}
                        DELETE /books/:book_id/reviews/:id(.:format)      {:action=>"destroy", :controller=>"reviews"}
      
      • 当然、今までのbooksのルーティングもあるさ。

RESTfulじゃないルーティング

  • これ、基本はやらないほうがいいけど、一応、できるってことで
  • まあ、config/routes.rbにmatchメソッドで書けばいいだけみたいだけどね。
    • こんな感じで書けば、
       match '/blogs/:user_id' => 'blogs#index'
      
    • こんなルーティングが追加されるんだねー
       /blogs/:user_id(.:format)                  {:controller=>"blogs", :action=>"index"}
      
    • この他、デフォルト値設定や、HTTPメソッドの制限、正規表現での制約、リダイレクト設定なんかもできるんだ。
    • ちなみに、以前のRailsのデフォルト設定は、こんな感じね!
       match ':controller(/:action(/:id(.:format)))'
      
      • そういや、ここ、初めの方でコメントアウトしたっけ。
  • あー、rootも設定できるのねー
    • で、まさにこんな風に[root]を書いて、
       root :to => 'books#index'
      
    • デフォルトで作成されてる[public/index.html]を別名に変えてから、[http://localhost:3000/]にアクセスすれば、自動で[/books#index]にルーティングされる、と。
  • ルーティングは、順番が大事!
    • 特にrootは全体に影響するから、最後に書こう!

-
最終更新:2012年01月27日 07:33