SI部の toga です。
Railsアプリケーションでは、事前処理を before_action
として定義し、コントローラー間で継承することができます。しかし、親クラスに定義した before_action
が意図せず実行されないという事象が先日発生しました。
本記事ではその原因と解決方法について紹介します。本記事の動作環境は Rails 8.0.2 です。
本来は実行されるはずの before_action
がスキップされ、後続の before_action
のみが実行されていることに気づきました。その結果、期待しない順序で処理が進行し、アプリケーションの動作に不整合が生じました。
原因を調査したところ、親クラスで定義された before_action
と同名の before_action
を子クラスでも再度宣言していたことが問題であることが判明しました。
Railsの before_action
は、同じコントローラー内で同一のメソッドに対する複数の宣言があると、最後の宣言が優先されるという挙動を持ちます。
同じコールバックを異なるオプションで複数回呼び出すと、最後に呼び出されたアクションコールバックの定義によって、それまでのコールバックの定義は上書きされます。
参考: https://railsguides.jp/action_controller_overview.html#before-action
class ExamplesController < ActionController::Base
before_action :do_something_1
before_action :do_something_2
before_action :do_something_1
def index; end
private
def do_something_1
puts "do_something_1"
end
def do_something_2
puts "do_something_2"
end
end
私が期待していた出力順序は以下です
do_something_1
do_something_2
do_something_1
しかし、実際の出力は以下のようになります
do_something_2
do_something_1
親クラスで定義された before_action
も子クラス側で上書きされるため、親クラスの before_action
が実行されないという状況が生じました。
以下のような構成のコントローラーを用いて、問題の再現と挙動の確認を行いました。
class ApplicationController < ActionController::Base
before_action :do_something_3
private
def do_something_3
puts "do_something_3"
end
end
class ConcreteController < ApplicationController
before_action :do_something_1
before_action :do_something_2
before_action :do_something_3
def index; end
private
def do_something_1
puts "do_something_1"
end
def do_something_2
puts "do_something_2"
end
end
期待していた出力順序は以下の通りです
do_something_3
do_something_1
do_something_2
do_something_3
しかし、実際の出力は以下のようになりました
do_something_1
do_something_2
do_something_3
親クラスの before_action
による do_something_3
は実行されておらず、子クラスで再宣言された do_something_3
のみが実行対象となっていました。
before_action
を定義した場合、子クラス側の定義が優先され、親クラスの before_action
は実行されなくなることを確認しました。before_action
の重複を静的解析で検知できるように、RuboCopのカスタムルールの導入も検討していきたいです。