Ateam Lifestyle x cyma Advent Calendar 2018 の15日目は、株式会社エイチームライフスタイルのWebエンジニア @kytiken が担当します。
before_actionと言っているのは、controllerに before_action :メソッド名 と書いたら、アクションが実行される直前に指定したメソッドを実行してくれるアレのことです。
今回の記事では before_action がどうやって動いているのかを、railsのソースコードを読むことによって、理解を深めようという趣旨で書いていきます。
バージョン
rails5.2
コードリーディング
実際にbefore_actionをメソッドを定義している場所を見てみます。
[:before, :after, :around].each do |callback|
define_method "#{callback}_action" do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, callback, name, options)
end
end
before_actionを定義している部分をかいつまんで抜き出しました。
define_methodで動的にメソッドを作っています。
before_actionを実行すると _insert_callbacks を実行して、そのブロックの中で set_callback しているとわかります。
この2つのメソッドがそれぞれ何をしているかを調べていきます。
_insert_callbacks
def _insert_callbacks(callbacks, block = nil)
options = callbacks.extract_options!
_normalize_callback_options(options)
callbacks.push(block) if block
callbacks.each do |callback|
yield callback, options
end
end
このメソッドは引数として渡された callbacks をeachでグルグル回して、 options と一緒に yield にわたすということをしています。
また第二引数としてblockが渡されているとしたら、それをcallbacksに追加して、最終的には yield に渡されます。
set_callback
このメソッドは任意のメソッドに対して、コールバックを設定できるメソッドとなります。
このメソッドの使い方は、こちらのAPIドキュメントに詳しく書いてあります。
class Audit
def before(caller)
puts 'Audit: before is called'
end
def before_save(caller)
puts 'Audit: before_save is called'
end
end
class Account
include ActiveSupport::Callbacks
define_callbacks :save
set_callback :save, :before, Audit.new
def save
run_callbacks :save do
puts 'save in main'
end
end
end
apiドキュメントより引用
set_callback を使ってコールバックするためには、 define_callbacks をしておく必要があるのと、 run_callbacks を実行するようにしておく必要があります。
before_actionの定義
_insert_callbacks と set_callback のことがわかったところで、もう一回 before_action を見てみます。
[:before, :after, :around].each do |callback|
define_method "#{callback}_action" do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, callback, name, options)
end
end
さて、before_actionの定義で、set_callbackは下記のように使われていました。
set_callback(:process_action, callback, name, options)
process_action メソッドにコールバックをつけていることがわかります。
process_action
この process_action というメソッドは、アクションが実行されるタイミングで実行されるのですが、このモジュールの中で、 run_callbacks を実行するように上書きしています。
def process_action(*args)
run_callbacks(:process_action) do
super
end
end
ついでにいうと、callbackが使えるように define_callbacks もやっています。
define_callbacks :process_action,
terminator: ->(controller, result_lambda) { result_lambda.call; controller.performed? },
skip_after_callbacks_if_terminated: true
まとめ
-
process_actionが実行されるタイミングでrun_callbackが実行される。 -
before_actionはprocess_actionにset_callbackするもの。
Ateam Lifestyle x cyma Advent Calendar 2018 の16日目は @kyntk さん に書いてもらう予定です。お楽しみに!
エイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/