たばりばりスタイル

たばりばりスタイル

バリバリバリ⚡︎

RailsでControllerに標準のCRUDなアクション以外生やしたくない

ここでいう標準な CRUD とは下記のアクション群 (scaffold で生成されるアクション)

  • index
  • show
  • new
  • edit
  • create
  • update
  • destroy

標準のCRUDなアクション以外生やし始めると、徐々にコントローラがカオス化する気がしませんか?

生やしたい派の意見としては共通ロジックをコントローラで使いまわせる、ルーティングからコードの配置がわかりやすい (?)、などなど

でも生やした結果、最終的にコントローラがカオス化してるケースを何件も見てきました。。。 (アクションが数十個、全体で数百行に成長したコントローラ。。。

個人的には、よくある検索用の search や完了ページ的な finish (complite) みたいなアクションでさえ別コントローラに切り出すでよくないかと思ってしまう。それくらい強いルールがあったほうが秩序が保たれるんじゃないかと。。。

実際のイメージですが、Tweet (つぶやき) 用のコントローラを用意したい場合で考えてみます。

class TweetsController
  # GET /tweets
  def index # 一覧ページ
  end

  # GET /tweets/new
  def new #  投稿ページ
  end

  # POST /tweets
  def create # 投稿処理
  end

  # GET /tweets/search
  def search # 検索用ページ
  end

  # GET /tweets/finish
  def  finish # 投稿完了用ページ
  end
end

このコントローラを

class TweetsController
  # GET /tweets
  def index # 一覧ページ
  end

  # GET /tweets/new
  def new #  投稿ページ
  end

  # POST /tweets
  def create # 投稿処理
  end
end

class Tweet::SearchesController
  # GET /tweet/search
  def show # show なのは単一ルーティングで表現を想定しているため
  end
end

class Tweet::FinishesController
  # GET /tweet/finish or /tweets/:id/finish
  def show # show なのは単一ルーティングで表現を想定しているため
  end
end

# routes.rb
Rails.application.routes.draw do
  resources :tweets, only: [:index, :new, :create] do
    scope module: :tweet do
      resource :finish, only: :show
    end
  end

  namespace :tweet do
    resource :search, only: :show
  end
end

このような形で実装する。 上記 routes.rb の記述で作成されるルーティングは下記。

Helper Path Controller#Action
tweet_finish_path GET /tweets/:tweet_id/finish(.:format) tweet/finishes#show
tweets_path GET /tweets(.:format) tweets#index
POST /tweets(.:format) tweets#create
new_tweet_path GET /tweets/new(.:format) tweets#new
tweet_search_path GET /tweet/search(.:format) tweet/searches#show

こういう感じで routes.rb で resource(s) ベースでコントローラを作ることをルール化すれば、標準 CRUD なアクションのみ対応でき、実装に統一感もでます。標準 CRUD なアクションのみになるので、7 つ以上のルーティングをひとつのコントローラで制御することがなくなり、シンプルになると思います。

また、コントローラを分割することで、分割されたコントローラ間で利用したい共通ロジックが出てくる場合もあると思います。そうなると、外 (例えば Concern など) に切り出す必要がでるため、コントローラのコード量が減りさらに薄くなります。

外に切り出しやコントローラの分割でファイル数自体は増えるでしょうが、ルーティングの生成されるパスからファイルも推測しやすいですし、大きな問題ではないと思います。

正直コントローラは薄ければ薄いほど嬉しいと思っていて、アクションが数十個、全体で数百行あるようなコントローラは読んでられません。ただでさえ Fat 化させやすいレイヤーだと思うので、個人的には厳しいルールで縛ってなるべく全体の見通しがよくしたいなと思っています。

Rubocop でそういう cop は現時点で存在しないようなので、時間があるときにカスタム cop を作れたらいいなと思っています。

以上です。