📘
【Rails】コントローラコールバックのifオプションと条件分岐ベタ書きの違い【before_action】
はじめに
特定の条件下でbefore_action
を設定する際、起きたエラーと疑問に思ったことを整理します。
コントローラコールバック(controller callback)は、コントローラのアクションが実行される「直前(before)」、「直後(after)」、あるいは「直前と直後(around)」に実行されるメソッドです。
起きたエラー
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? ... end
:ActiveSupport::Callbacks
の仕組みを用いて、クラス読み込みのときbefore_action
がfoo
をコールバックチェーンに登録する際に評価される
コールバックチェーンへのメソッド登録→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
で確認)
参考
Discussion