- 明日の朝までに読まないといけない
背景
- とあるコントローラの処理が重い
- 調査の結果、before_actionとかafter_actionとかで指定されているfilterに原因があることが分かった
- そこで、それぞれのCallbackの実行にかかる時間を書き出したい
- そのために、該当するControllerのrun_callbacksメソッドを黒魔術で上書きする
ActiveSupport::Callbacksについて
- ApplicationControllerやActiveRecordがこれをincludeして、run_callbacksなどのメソッドを使用可能になる。
今回読むコード
activesupport/lib/active_support/callbacks.rb
# Runs the callbacks for the given event.
#
# Calls the before and around callbacks in the order they were set, yields
# the block (if given one), and then runs the after callbacks in reverse
# order.
#
# If the callback chain was halted, returns +false+. Otherwise returns the
# result of the block, +nil+ if no callbacks have been set, or +true+
# if callbacks have been set but no block is given.
#
# run_callbacks :save do
# save
# end
#
#--
#
# As this method is used in many places, and often wraps large portions of
# user code, it has an additional design goal of minimizing its impact on
# the visible call stack. An exception from inside a :before or :after
# callback can be as noisy as it likes -- but when control has passed
# smoothly through and into the supplied block, we want as little evidence
# as possible that we were here.
def run_callbacks(kind)
callbacks = __callbacks[kind.to_sym]
if callbacks.empty?
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
next_sequence = callbacks.compile
invoke_sequence = Proc.new do
skipped = nil
while true
current = next_sequence
current.invoke_before(env)
if current.final?
env.value = !env.halted && (!block_given? || yield)
elsif current.skip?(env)
(skipped ||= []) << current
next_sequence = next_sequence.nested
next
else
next_sequence = next_sequence.nested
begin
target, block, method, *arguments = current.expand_call_template(env, invoke_sequence)
target.send(method, *arguments, &block)
ensure
next_sequence = current
end
end
current.invoke_after(env)
skipped.pop.invoke_after(env) while skipped && skipped.first
break env.value
end
end
# Common case: no 'around' callbacks defined
if next_sequence.final?
next_sequence.invoke_before(env)
env.value = !env.halted && (!block_given? || yield)
next_sequence.invoke_after(env)
env.value
else
invoke_sequence.call
end
end
end
binding.pryで実験
[9] pry(#<Api::V2::AppleInAppPurchaseController>)> callbacks.class
=> ActiveSupport::Callbacks::CallbackChain
[10] pry(#<Api::V2::AppleInAppPurchaseController>)> callbacks.compile.class
=> ActiveSupport::Callbacks::CallbackSequence
callbacks
=> #<ActiveSupport::Callbacks::CallbackChain:0x00007f907ffdd998
@callbacks=
#<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064416350
@after=[],
@before=[],
@call_template=
#<ActiveSupport::Callbacks::CallTemplate:0x00007f9064416378
@arguments=[],
@method_name=:set_controller_action,
@override_block=nil,
@override_target=nil>,
@nested=
#<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064417ca0
@after=
[#<Proc:0x00007f90644175e8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:232>,
#<Proc:0x00007f9064416c60@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>,
#<Proc:0x00007f9064416a08@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>],
@before=
[#<Proc:0x00007f9064416468@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f90644165f8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064416850@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064416e68@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:175>,
#<Proc:0x00007f9064417188@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064417368@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f90644178e0@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>],
@call_template=nil,
@nested=nil,
@user_conditions=nil>,
@user_conditions=[]>,
@chain=
[#<ActiveSupport::Callbacks::Callback:0x00007f90643c1738
@chain_config=
{:scope=>[:kind],
:terminator=>
#<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>,
:skip_after_callbacks_if_terminated=>true},
@filter=:set_controller_action,
@if=[],
@key=:set_controller_action,
@kind=:around,
@name=:process_action,
@unless=[]>,
#<ActiveSupport::Callbacks::Callback:0x00007f90643add28
@chain_config=
{:scope=>[:kind],
:terminator=>
#<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>,
:skip_after_callbacks_if_terminated=>true},
@filter=:init_i18n_debugger,
@if=[],
@key=:init_i18n_debugger,
@kind=:before,
@name=:process_action,
@unless=[]>,
#<ActiveSupport::Callbacks::Callback:0x00007f9072cfd938
@chain_config=
{:scope=>[:kind],
:terminator=>
#<Proc:0x00007f9072ce44d8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:34 (lambda)>,
:skip_after_callbacks_if_terminated=>true},
@filter=
#<Proc:0x00007f9072cfdb90@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/config-2.2.1/lib/config/integrations/rails/railtie.rb:28>,
callback_chains
=> #<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064416350
@after=[],
@before=[],
@call_template=
#<ActiveSupport::Callbacks::CallTemplate:0x00007f9064416378
@arguments=[],
@method_name=:set_controller_action,
@override_block=nil,
@override_target=nil>,
@nested=
#<ActiveSupport::Callbacks::CallbackSequence:0x00007f9064417ca0
@after=
[#<Proc:0x00007f90644175e8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:232>,
#<Proc:0x00007f9064416c60@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>,
#<Proc:0x00007f9064416a08@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:247>],
@before=
[#<Proc:0x00007f9064416468@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f90644165f8@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064416850@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064416e68@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:175>,
#<Proc:0x00007f9064417188@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f9064417368@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>,
#<Proc:0x00007f90644178e0@/Users/daiki-kudo/.rbenv/gems/2.6.0/gems/activesupport-6.0.3.2/lib/active_support/callbacks.rb:194>],
@call_template=nil,
@nested=nil,
@user_conditions=nil>,
@user_conditions=[]>