Chiroru's Diary

日々の学びをちょこちょこメモしていきます

DHH流 コントローラーを分割する

DHHについて

DHHはどのようにRailsのコントローラを書くのかを参考に、コントローラーの書き方を変更することにしました。

自分の作ったコントローラの状態を悔やむのは決まって、作ったコントローラの数が少なすぎた時です。多くの処理を任せようとしすぎてしまうんです。

これを実行するのに私が用いているヒューリスティクスはこうです。コントローラが元々持っているRESTアクションやデフォルトの5つの機能にはないメソッドを付け加えたいと思ったら、いつだって新しいコントローラを作る。それだけでいいのです。

ポイント

  • 名前空間を利用して分割する

  • コントローラーには基本CRUD機能しか持たせない

コントローラーを分割する

今回以下のようなUsersControllerを分割することにしました。

class UsersController < ApplicationController
  def index
  end

  def show
  end

  def following
  end

  def followers
  end
end


デフォルトアクション(CURD)以外のfollowingfollowersはこのように分割できます。↓

# controllers/users_controller.rb  
class UsersController < ApplicationController
  def index
  end

  def show
  end
end
# controllers/users/followings_controller.rb
class Users::FollowingsController < ApplicationController
  def index
  end
end
# controllers/users/followers_controller.rb
class Users::FollowersController < ApplicationController
  def index
  end
end

ルーティング

今回Users::FollowingsControllerのルーティングを考えるにあたり、先にFollowingsControllerだった場合について考えます。

FollowingsController

この時の階層はcontrollers/followingsで、ルーティングは以下のようになります。

resources users do
  resources followings
end

パスは、users/:id/followingsを認識します。

Users::FollowingsController

一方今回の階層はcontrollers/users/followingsです。その時ルーティングは以下のようになります。

resources users do
  resources followings, controller: 'users/followings'
end

パスは同じく、users/:id/followingsを認識します。

controllerオプション

controller: 'users/followings' では、使用するコントローラーを指定しています。
パスはusers/:id/followingsを認識しますが、今回コントローラーはusers/followingsになるので、そちらを指定しています。

参考:Rails のルーティング

名前空間について

名前空間は以下のような構成になっています。

class Hoge
  TEXT = ...
end

Hoge::TEXT

Zeitwerk::NameError

名前空間を利用する上で不適切な箇所に(controllers配下に)ファイルを配置していたため、このようなエラーに遭遇しました。

NameError: expected file /app/~~~ to define constant Controller, but didn't

Railsガイドには、以下のように書かれています。

Railsアプリケーションで使うファイル名は、そこで定義されている定数名と一致しなければなりません。ファイル名はディレクトリ名と合わせて名前空間として振る舞います。 たとえば、app/helpers/users_helper.rbファイルではUsersHelperを定義すべきですし、app/controllers/admin/payments_controller.rbではAdmin::PaymentsControllerを定義すべきです。 Railsは、ファイル名をString#camelizeで活用するようZeitwerkを設定します。たとえば、app/controllers/users_controller.rbは以下のためにUsersControllerという定数を定義します。 "users_controller".camelize # => UsersController

具体的には以下のようなことです。

api/users_controller.rb

Api::UsersController
API::UsersController <= こっちはダメ

またこのルールにそぐわない名前をつけたければ、inflectionsに指定する方法もあるようです。

カスタマイズする必要が生じた場合(略語を追加するなど)は、config/initializers/inflections.rbをチェック

参考:定数の自動読み込みと再読み込み (Zeitwerk)