class Verifly::DependentCallbacks::Invoker

Simple service to invoke callback groups @example simple invokation

def invoke_callbacks
  Invoker.new(group) do |invoker|
    invoker.around {}
    invoker.run(self)
  end
end

@see DependentCallbacks#export_callbacks_to @!attribute flat_sequence

@return [[Callback]] tsorted callbacks

@!attribute context

@return [[Object]] invokation context. It would be passed to all applicators

@!attribute inner_block

@return [Proc] block in the middle of middleware

@!visibility private @!attribute break_if_proc

@note does not affect 'after' callbacks
@return [Proc] if this block evaluate to truthy, sequence would be halted.

@!attribute binding_

@return [#instance_exec] binding_ to evaluate on

Attributes

binding_[RW]
break_if_proc[RW]
context[RW]
flat_sequence[RW]
inner_block[RW]

Public Class Methods

new(callback_group) { |self| ... } click to toggle source

@param callback_group [CallbackGroup] @yield self if block given

# File lib/verifly/dependent_callbacks/invoker.rb, line 33
def initialize(callback_group)
  self.flat_sequence = callback_group.sequence
  self.context = []
  self.break_if_proc = proc { false }
  yield(self) if block_given?
end

Public Instance Methods

around(&block) click to toggle source

@yield in the middle of middleware, setting inner_block attribute @see inner_block

# File lib/verifly/dependent_callbacks/invoker.rb, line 42
def around(&block)
  self.inner_block = block
end
break_if(&block) click to toggle source

@yield between callbacks halting chain if evaluated to true @see break_if_proc

# File lib/verifly/dependent_callbacks/invoker.rb, line 48
def break_if(&block)
  self.break_if_proc = block
end
run(binding_) click to toggle source

Sets binding_, reduces callbacks into big proc and evaluates it @param binding_ [#instance_exec] binding_ to be evaluated on @return inner_block call result

# File lib/verifly/dependent_callbacks/invoker.rb, line 55
def run(binding_)
  self.binding_ = binding_
  result = nil
  block_with_result_extractor = -> { result = inner_block&.call }

  log!(:info, "Started chain processing")

  sequence =
    flat_sequence.reverse_each.reduce(block_with_result_extractor) do |sequence, callback|
      -> { call_callback(callback, sequence) }
    end

  sequence.call
  result
end

Private Instance Methods

call_callback(callback, sequence) click to toggle source

Invokes callback in context of invoker @param callback [Callback] current callbacks @param sequence [Proc] already built sequence of callbacks

# File lib/verifly/dependent_callbacks/invoker.rb, line 78
def call_callback(callback, sequence)
  log!(:debug, "Invokation", callback: callback)

  case callback.position
  when :before then call_callback_before(callback, sequence)
  when :after then call_callback_after(callback, sequence)
  when :around then call_callback_around(callback, sequence)
  end

  nil
end
call_callback_after(callback, sequence) click to toggle source

Invokes after_<name> callbacks @param callback [Callback] current callbacks @param sequence [Proc] already built sequence of callbacks

# File lib/verifly/dependent_callbacks/invoker.rb, line 106
def call_callback_after(callback, sequence)
  sequence.call
  call_with_time_report!(callback, binding_, *context)
end
call_callback_around(callback, sequence) click to toggle source

Invokes around_<name> callbacks @param callback [Callback] current callbacks @param sequence [Proc] already built sequence of callbacks

# File lib/verifly/dependent_callbacks/invoker.rb, line 114
def call_callback_around(callback, sequence)
  inner_executed = false
  inner = lambda do
    inner_executed = true
    if break_if_proc.call(*context)
      log!(:warn, "Chain halted", callback: callback)
    else
      sequence.call
    end
  end

  call_with_time_report!(callback, binding_, inner, *context)

  unless inner_executed
    log!(:warn, "Chain halted (sequential block not called)", callback: callback)
  end
end
call_callback_before(callback, sequence) click to toggle source

Invokes before_<name> callbacks @param callback [Callback] current callbacks @param sequence [Proc] already built sequence of callbacks

# File lib/verifly/dependent_callbacks/invoker.rb, line 93
def call_callback_before(callback, sequence)
  call_with_time_report!(callback, binding_, *context)

  if break_if_proc.call(*context)
    log!(:warn, "Chain halted", callback: callback)
  else
    sequence.call
  end
end
log!(severity, message, callback: nil) click to toggle source

Logger interface to decorate messages. Uses DependentCallbacks.logger @param severity [:debug, :info, :warn, :error, :fatal] severity level @param message [String] message @param callback [Callback?] callback to get extra context

# File lib/verifly/dependent_callbacks/invoker.rb, line 136
      def log!(severity, message, callback: nil)
        DependentCallbacks.logger.public_send(severity, "Verifly::DependentCallbacks::Invoker") do
          if callback
            <<~TXT.squish if callback
              #{message} callback #{callback.name || "(anonymous)"}
                         in #{callback.action.source_location(binding_)&.join(':')}
            TXT
          else
            message
          end
        end
      end