0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ActiveSupport::Callbacksを読む

Posted at
  • 明日の朝までに読まないといけない

背景

  • とあるコントローラの処理が重い
  • 調査の結果、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=[]>
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?