Railsガイドにきちんと目を通して新しい知識を得る - Rails のルーティング編 -

ドキュメントを読み込むのは大事、ということでRailsガイドを頭から読んでいく取り組みをしています。 各章ごとに、(Railsガイドにちゃんと書いてあるのに)知らなかった機能を雑にまとめていきます。

今回は、Rails のルーティングの章です。

railsguides.jp

浅いネスト

リンクはこちら

ルーティングで、リソースのネストを避ける方法としてidを必要とする(関連リソースのIDの特定が可能)アクションはネストから外すという方法が紹介されています。
同じリソースに対してルーティングを分けるというのは発想がなかったので、とても面白いと思いました。

resources :articles do
  resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

上記はshallow: trueをつけることでもっとシンプルに記載できます。
使うとしたらこっちの方が良さそうですね。

resources :articles, shallow: true do
  resources :comments
end

慣れの問題もあるかもですが、ネストを避けた結果分かりづらくなることもあると思うので、いろいろ意見を聞いてみたいなーと思いました。

concern

リンクはこちら

concernを使うことで共通化できる。知らなかったですが、使える場面ありそうですね。

concern :commentable do
  resources :comments
end
resources :books, concerns: :commentable

追加されたnewアクションへのルーティングを追加する

リンクはこちら

特定のアクションのルーティングに続ける形でルーティングの設定ができるみたいです。
ただし、コントローラのアクションは下記の例で言うとCommentsコントローラのpreviewアクションで処理されます。
また、newアクションがなくなるわけでもないです。

resources :comments do
  get 'preview', on: :new
end
Prefix Verb URI Pattern Controller#Action
preview_new_book GET /books/new/preview(.:format) books#preview
new_book GET /books/new(.:format) books#new

defaultsでデフォルトのパラメータを設定できる

リンクはこちら

defaultsを使うことでデフォルトのパラーメータを設定できます。
セキュリティ上の理由で上書き不可になっているとのこと。サーバ側で設定した値が上書き可能だと、色々とセキュリティ事故が起きやすくなってしまうから、ということでしょうか。
したがって、ユースケースとしては、値を渡すというよりRailsガイドの例のように、フォーマットを指定するようなユースケースの方がイメージできるなと思いました。

resources :books, defaults: { dummy: true }

ちなみに上記の状態で、/books?dummy=falseとリクエストを送っても、paramsの中身はdummy: trueでした。

同じURLで複数種類のHTTPリクエストを受け付ける

リンクはこちら

matchviaを使って同じURLでGETとPOSTを受け付ける、みたいなことができます。
あまり有効なユースケースが思いつかないですが、例えばリプレイス案件で既存のサイトのURL構造をとりあえず変えたくないときなど、どうしてもというときには使えるということを覚えておくと良いかもなと思いました。

match 'photos', to: 'photos#show', via: [:get, :post]

リクエスト内容に応じて制限を加える

リンクはこちら

例えば、以下のようにすると、http://admin.example.com/photosのように、adminというサブドメインにアクセスがあった場合にのみ、対応することになるみたいです。

get 'photos', to: 'photos#index', constraints: { subdomain: 'admin' }

matchesを使った高度な制限

リンクはこちら

例えば以下の例だと、constraintsに渡したインスタンスmatches?でtrueを返すので、/booksにアクセスすると、booksコントローラのnewアクションが反応します。

class Restrict
  def matches?(request)
    true
  end
end

Rails.application.routes.draw do
  get 'books', to: 'books#new', constraints: Restrict.new
end

ちなみにlogからは特に制限をしたからといって特別な履歴は出ていなかったです。

Started GET "/books/" for ...
Processing by BooksController#new as HTML
  ...(略)
Completed 200 OK

Rackアプリケーションにルーティングする

リンクはこちら

ルーターからすると、callに応答すれば、特にアクションであるかは気にしない、とのことです。
例えば、以下のようにすると、/rack_demoにアクセスした際に、Hello, World!というテキストが表示されました。

class MyRackApp
  def call(env)
    [200, { "Content-Type" => "text/plain" }, ["Hello World!"]]
  end
end

Rails.application.routes.draw do
  get 'rack_demo', to: MyRackApp.new
end

ちなみにlogは以下の1行だけでした。

Started GET "/rack_demo" for ...

resolve

リンクはこちら

例えば以下のようにすると、<%= link_to "Show this book", book %>のように書いている箇所のリンクがbook_urlではなく、dummy_urlを指すようになります。

ActionView::Template::Error (undefined method `dummy_url')

asオプションでヘルパーの命名をオーバーライド

リンクはこちら

asオプションを使うことで、ヘルパーの命名をオーバーライドすることができます。
URLが長くなったときに短縮するとか、URLを変更するときにviewファイルの変更を一旦後回しにしたいときなどに使えそうかな?と思いました。
ただし、命名の衝突には注意が必要ですね。

path_namesでnewやeditのオーバーライド

リンクはこちら

ユーザーに見せるURLだけをnewやeditから変えたいときに使えそうです。
これも例えばサービスをRailsで作り替えたいときなどに、URL構成を変更せず、かつなるべくアクションはRails標準を使いたいときなどに使えそうですね。

resources :books, path_names: { new: 'make', edit: 'change' }

少し混乱しそうではありますが、ヘルパー名もnewやeditを使うことになります。

$ rails routes
new_book GET    /books/make(.:format)  books#new

inflectionsで単数系、複数形の名前をオーバライド

リンクはこちら

inflectionsで、単数系、複数形の名前を自由に調整できます。

例えば、Personモデルをscaffoldで作成すると、コントローラの名前などはPeopleになります。

$ rails g scaffold Person

...
create    app/controllers/people_controller.rb

しかし、config/initializers/inflections.rbを以下のようにすると、Personsのコントローラを作成できました。

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.irregular 'person', 'persons'
end
$ rails g scaffold Person

...
create    app/controllers/persons_controller.rb

paramでルーティングのパラメータをオーバーライド

リンクはこちら

ルーティングのidの部分を他の値にすることができるようです。
重複しないことが必須になると思うので難しいですが、何か明確な識別子があるなら、それを表示したいケースなどもありそうですね。

ルーティングのファイルを分割

リンクはこちら

ルーティングのファイルはdrawを使うことで分割先のファイルを読み込むように指定できるようです。
Railsガイドには「本当に必要になるまでは分割しないこと」とあり、その理由としてscopeやnamespace`などで分割する方法がすでにあるから、と言うことでしたが、個人的には(ファイル分割は使ったことないですが)管理者か一般ユーザーかどうかでファイルを分けるくらいでもむしろ明確になっていいんじゃないの?と思ったりもしました。