📘

【Rails】コントローラコールバックのifオプションと条件分岐ベタ書きの違い【before_action】

2025/01/24に公開

はじめに

特定の条件下でbefore_actionを設定する際、起きたエラーと疑問に思ったことを整理します。

コントローラコールバック(controller callback)は、コントローラのアクションが実行される「直前(before)」、「直後(after)」、あるいは「直前と直後(around)」に実行されるメソッドです。

https://railsguides.jp/action_controller_overview.html#コントローラコールバック

起きたエラー

class BooksController < ApplicationController
  if some_condition?
    before_action: :foo
  end

  def index
  end

  private

  def some_condition?
  end

  def foo
  end
end

some_condition?下でのみ、各アクション実行前にfooを実行するようなコードを書きたかったのですが、以下のような関数未定義エラーが発生しました。

NameError (undefined local variable or method `some_condition?` for class BooksController)

このエラーは、以下のように記述することで解消できて、some_condition?trueの時のみfooをアクション直前に実行できるようになります。

before_action: :foo, if: :some_condition?

ここで、この二つの違いはどこから来ているのでしょうか。

評価のタイミング

結論としては、評価されるタイミングが異なることが主な違いのようです。

  • if: :some_condition?:コントローラのインスタンスメソッド (foo) を リクエスト毎(アクション実行直前) に呼び出すときに評価される
  • if some_condition? ... endActiveSupport::Callbacks の仕組みを用いて、クラス読み込みのときbefore_actionfooをコールバックチェーンに登録する際に評価される

コールバックチェーンへのメソッド登録→before_actionを実行→各アクション実行 という流れで処理が進むため、if some_condition? ... end評価時にコントローラのメソッドを参照できずエラーが起きていたようです。

class BooksController < ApplicationController
  if Rails.env.development?
    before_action :foo
  end
end

if some_condition? ... endが(一応)使える場合もあり、Rails起動時にロードされるRails.envを用いて、:fooをコールバックチェーンに登録させる/させないような条件分岐が書けることを手元で確認しました。(BooksController._process_action_callbacksで確認)

参考

https://github.com/rails/rails/blob/main/activesupport/lib/active_support/callbacks.rb#L737-L948

https://techracho.bpsinc.jp/baba/2013_08_06/12650

https://techracho.bpsinc.jp/baba/2013_08_19/12657

https://railsguides.jp/action_controller_overview.html#コントローラコールバック

Discussion