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/