PIYO - Tech & Life -

Railsのroutes.rbでconstraintsを使う

Rails

ルーティングのマッチングの優先順位のせいか期待通りのコントローラーで処理できないことがあります。そんなときにconstraintsオプションでroutesのパラメータのフォーマットを制限できます。たまーに使います。

たまーにしか使わなくて、なんで書いてあるんだっけ?と忘れていたので、思い出せた今回メモがてらに書いておきますよ。

例だよ

例えばこんな感じのroutes.rbになっているとします。

  resources :messages, only: %i(index show)
  namespace :messages do
    resources :unreads, only: %i(index)
  end
DHHはどのようにRailsのコントローラを書くのか | POSTD
私たちの救世主DHH™は最近の Full Stack Radioのインタビュー で、 Basecamp の最新版で彼がどのようにRailsのコントローラを書いたかを説明しています。下記は、彼のすばら…

↑の例のようにnamespaceを切ると登場しうる書き方かなと思います。

で、3つのルーティングができます。

messages_path       	GET	/messages(.:format)	           messages#index
message_path	        GET	/messages/:id(.:format)        messages#show
messages_unreads_path	GET	/messages/unreads(.:format)    messages/unreads#index

1個めと2個めは割と問題ないのですが、3個目が曲者です。↑のルーティングの意図としてはhttp://localhost:3000/messages/unreadsなどとアクセスした場合にはunreads#index側にいってほしいはずです。

ですが、/messages/unreadsunreadsの部分が、'/messages/:id’の:idの部分とマッチしてしまい、messages#show側で処理しようとしてしまいます。デフォルトでは:idの箇所は数値でも文字列でもなんでもいいことになっているので、unreadsという文字列のパラメータが渡ってきたとして判断しちゃうんですね。

こうするよ

これを想定通りに動かすためにconstraintsを使います。今回は:idの部分が数値である場合にmessages#showにいってほしいので、その条件を書きます。

  resources :messages, only: %i(index show), constraints: {id: /\d+/} # <= この部分
  namespace :messages do
    resources :unreads, only: %i(index)
  end

constraintsには正規表現でパターンを指定できます。今回は数値にマッチする場合、としました。これ以外の/messages/hogehogeは他で処理しますので、今回のルーティングだと/messages/数値/messages/unreads以外はNo Routeになるわけですね。

constraintsについて詳しくは Rails のルーティング | Rails ガイド に記載があります。