routes.rbを部分ファイルに分割する方法(翻訳)

原文(How to split routes.rb into smaller parts? | Arkency Blog / February 27, 2015)です。英語の勉強にいいと思って、興味ある記事を日本語訳しています。間違いあれば教えてください。(#)は私の補足です。


Ruby on Railを使っているアプリケーションは、ルーティングエンジンとルーティングを定義するconfig/routes.rbファイルがあります。routes.rbは開発していると非常に大きくなることがよくあります。1行追加するたびに、routes.rbの保守が難しくなります。また、開発段階で特定のパスを探すことが、どんどん困難になります。現在、routes.rbが約5000LOC(# lines of code)含まれているアプリケーションで作業しています。かなり多いですよね。解決策はとてもシンプルです。routes.rbファイルをいくつかの小さいファイルに分割することです。

ファイルの読み込み順

リクエストが来ると、routes.rbファイルは「上から下に」順番に処理されます。最初に適切にマッチしたルーティングが見つかると、リクエストは適切なコントローラーに転送されます。ファイル内で一致するパスが見つからない場合、Railsは404エラーになります。(# 分割された)ファイルの読み込む順が並び替わる可能性があるため、(# ファイルを分割した場合)名前空間の優先順位を定義できなければなりません。

解決策

次の例はroutes.rbの一部です。

ActionController::Routing::Routes.draw do
  root to: "home#index"
  get "/about
  get "/login" => "application#login"


  namespace :api do
    #nested resources
  end

  namespace :admin do
    #nested resources
  end

  namespace :messages do
    #nested resources
  end

  namespace :orders do
    #nested resources
  end
end

いくつかのデフォルト(# namespaceの指定がないルーティングのこと?)の名前空間(/home, /about, /loginのルーティングを指定した場合 )とその他 4つの名前空間があります。これらの名前空間は、私たちのアプリケーションは存在するコンテキスト(# 人間が分かりやすい形にってこと。adminは管理者だったり)をうまく定義します。よって、それらの namespace はファイルを分割するための最適な候補です。そのため、api.rbadmin.rbmessages.rb, orders.rb を作成しました。通常は、この分割ファイルをconfig/routes/ディレクトリに置きます。次のステップは上記のファイルを読み込むことです。これを行うにはいくつかの方法があります。Rails 3 をベースにしたアプリケーションでは、アプリケーション設定からルートファイルを世も込むのが一般的な方法です。最終的にapplication.rbに次の行を追加します。

config.paths["config/routes"] += Dir[Rails.root.join('config/routes/*.rb’)]

ファイルの読込み順を制御したい場合は、次のようにしてください。

config.paths["config/routes"] = %w(
      config/routes/messages.rb
      config/routes/orders.rb
      config/routes/admin.rb
      config/routes/api.rb
      config/routes.rb
    ).map { |relative_path| Rails.root.join(relative_path) }

ただし、Rails 4 以降では上記の行を追加すると例外が投げられます。Rails 4 はRails::Engineに[config / routes] キーが提供されていません。Rails 3, 4 両方のバージョンで機能する別のオプションがあります。以下が別の解決方法です。

YourApplication::Application.routes.draw do

def draw(routes_name)
    instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end

  draw :messages
  draw :orders
  draw :api
  draw :admin

  root to: "home#index"
  get "/about
  get "/login" => "application#login" 

end

パスの読み込みはActionDispatch::Routingモジュールに新しいメソッドを追加することで可能になります。Rails 4 も最初は同様の解決策でしたが、削除されました。(# 前はRailsの機能として提供されていたが削除されてたということ。) Revert "Allow loading external route files from the router" · rails/rails@5e7d6bb · GitHub (# @dhh コメント「不透明になるのでRailsでは推奨したいものではないです。必要ならとても簡単なので自分で追加してください 」 )

結論

ファイルを分割することは、routes.rbファイルと日々の開発を改善するための非常に簡単な解決策です。本記事の解決策で、日々の開発が楽になります。


翻訳は以上です。こっからはワタシのひとり言

Railsがroutes.rbファイルの分割は不透明になるって言ってる、不透明ってなんだろう。
記述の重複(DRYじゃない)により小さく分割し共通化するのではなく、1ファイルの行数が増えたことにより分割することは、ただ全体像が追いづらい状態になるだけってことか。さすがにroutes.rbが5000行になったことないけど、namespaceごとにファイル分けるのはすごくいい。