🔥

before_action、どう使う? Fizzyのコードを読んで整理してみた

に公開
2025/12/25

はじめに

この記事は、Railsで開発をしていて before_action の使いどころに迷ったことがある方に向けて書いています。

最近Railsで開発をしていて、before_action の使い方についてコードレビューで指摘を受けました。
具体的には、リソース取得(@card = Card.find(params[:id]) のような処理)を before_action で書いていたところ、「アクションメソッドが増えて only オプションを使い始めると、どのアクションでどの変数が使えるか把握しづらくなる」という理由で、やめてほしいと言われました。

その通りだなと納得はしたものの、では before_action はどういうときに使うべきなのか、自分の中で明確な基準がないことに気づきました。

そこで使い方の参考を得るため、37signalsが公開しているOSS basecamp/fizzy のコードを読んでみました。

Fizzyは37signalsが公開しているKanban型のタスク管理ツールです。Railsの生みの親であるDHH氏が所属する会社だけあって、Railsらしいシンプルなコードが書かれています。そのソースコードは、明確で簡潔なRailsコードの書き方を学ぶ上で非常に参考になります。

この記事では、Fizzyのコードから学んだ before_action の使い方を紹介します。


パターン1:ApplicationController経由で全Controllerに適用

まず、ApplicationController を見てみます。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Authentication
  include Authorization
  # ...
end

認証処理は Authentication というConcernにまとめられています。

# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern

  included do
    before_action :require_account
    before_action :require_authentication
  end
  # ...
end

ポイント:

  • ApplicationController に include Authentication されている
  • すべてのControllerは ApplicationController を継承している
  • → 個別のControllerでは意識せずに、自動的に認証チェックが働く
# app/controllers/cards_controller.rb
class CardsController < ApplicationController
  # ApplicationControllerを継承しているので、
  # 自動的に require_authentication が実行される
end

共通の認証をbefore_actionで実装するのは、一番オーソドックスな使い方だと思います。自分もbefore_actionと聞いて最初に思い浮かぶのはこのパターンでした。


パターン2:個別のControllerで直接定義

次に、CardsController を見てみましょう。

# app/controllers/cards_controller.rb
class CardsController < ApplicationController
  before_action :set_board, only: %i[ create ]
  before_action :set_card, only: %i[ show edit update destroy ]
  before_action :ensure_permission_to_administer_card, only: %i[ destroy ]

  def show
  end

  def edit
  end

  def destroy
    @card.destroy!
    # ...
  end

  private

  def set_board
    @board = Current.user.boards.find params[:board_id]
  end

  def set_card
    @card = Current.user.accessible_cards.find_by!(number: params[:id])
  end

  def ensure_permission_to_administer_card
    head :forbidden unless Current.user.can_administer_card?(@card)
  end
end

ポイント:

  • set_card や set_board は、そのController固有の before_action
  • 他のControllerでは使わないので、Concernにはせず直接定義

リソース取得

before_action :set_card, only: %i[ show edit update destroy ]

def set_card
  @card = Current.user.accessible_cards.find_by!(number: params[:id])
end

複数のアクションで共通して必要なリソース取得をbefore_actionでまとめ、各アクションは本質的な処理に集中できるようにしています。

実は、これは冒頭で書いた「コードレビューで指摘されたパターン」そのものです。

指摘の通り、only オプションが増えると、どのアクションでどの変数が使えるか把握しづらくなる面はあると思います。それでもFizzyでは積極的に使われているのが印象的でした。

正解は一つではないのかもしれません。チームの方針やコードベースの規模によって、判断が分かれるところだと思います。

権限チェック

before_action :ensure_permission_to_administer_card, only: %i[ destroy ]

def ensure_permission_to_administer_card
  head :forbidden unless Current.user.can_administer_card?(@card)
end

アクション固有の権限チェックもbefore_actionで行われています。権限がなければここで処理を中断するため、アクション本体には権限チェックのコードが入りません。
これもわかりやすい使い方ですね。「条件を満たさなければ処理を中断する」というガードの役割で、before_actionとして自然だなと思います。


まとめ:before_action の使い分け

パターン 例 適用範囲 定義場所
Concern → ApplicationController require_authentication 全Controller Concern
Controller直接定義 set_card, ensure_permission_to_* そのControllerのみ 各Controller

Fizzyでは:

  • 認証・認可(全員に必要)→ Concern + ApplicationController
  • リソース取得・権限チェック(そのController固有)→ Controller直接定義

という使い分けがされています。

共通しているのは、「処理を続行してよいか?」の判断 または 「必要なリソースの準備」 という点です。

逆に、ビューの表示フラグ(@show_header = true など)や、特定のアクションでしか使わないデータの取得には使われていませんでした。

Fizzyのコードを読んで、before_action の使い方にもいろいろなスタイルがあることがわかりました。レビューで指摘されたパターンも、OSSでは普通に使われていて、結局はチームでの合意が大事なんだろうなと思います。


余談:Fizzyの設計思想

Fizzyのコードを読んでいて、コントローラーの実装が非常にシンプルなことに気づきました。

STYLE.md にも以下のように書かれています:

In general, we favor a vanilla Rails approach with thin controllers directly invoking a rich domain model.

このアプローチは「Vanilla Rails」と呼ばれているようです(参考:Vanilla Rails is plenty)。

その分、モデル層はこんな感じになっています

例えば Card モデル :

class Card < ApplicationRecord
  include Assignable, Attachments, Broadcastable, Closeable, Colored, Entropic, Eventable,
    Exportable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable,
    Readable, Searchable, Stallable, Statuses, Storage::Tracked, Taggable, Triageable, Watchable

コントローラーをシンプルに保ち、ビジネスロジックをモデル(とconcern)に集約するおもしろ設計だなと思いました。


参考

SMARTCAMP Engineer Blog

Discussion