Module: Axn::Core::Flow::Handlers::Invoker

Extended by:
Invoker
Included in:
Invoker
Defined in:
lib/axn/core/flow/handlers/invoker.rb

Overview

Shared block evaluation with consistent arity handling and error piping.

allow_flow_control: when true, done!/fail! (EarlyCompletion/Failure) propagate out of the handler — use for execution-phase blocks (preprocess, defaults, etc.) where the user legitimately controls action flow. When false (default), they are piped as errors — use for post-execution contexts (callbacks, messages, matchers) where the result is already finalized.

Instance Method Summary collapse

Instance Method Details

#call(action:, handler:, exception: nil, operation: "executing handler", allow_flow_control: false) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/axn/core/flow/handlers/invoker.rb', line 18

def call(action:, handler:, exception: nil, operation: "executing handler", allow_flow_control: false)
  return call_symbol_handler(action:, symbol: handler, exception:) if symbol?(handler)
  return call_callable_handler(action:, callable: handler, exception:) if callable?(handler)

  literal_value(handler)
rescue Axn::Internal::EarlyCompletion, Axn::Failure
  raise if allow_flow_control

  Axn::Extensions.best_effort(operation, action:) { raise $ERROR_INFO }
rescue StandardError => e
  Axn::Extensions.best_effort(operation, action:) { raise e }
rescue Exception => e # rubocop:disable Lint/RescueException
  # A handler that blows the stack (or hits an unfinished method) is a bug in the HANDLER, and
  # must not become the action's outcome. This matters most on the settle path: a callback
  # dispatched from the executor's rescue clause raises past the sibling `rescue Exception`
  # there, so without this the callback's exception would escape `.call` and replace the real
  # failure the callback was invoked to observe. Anything axn doesn't absorb (a signal, an
  # `exit`, a library's own control-flow signal) still propagates untouched.
  raise unless Axn::Extensions.swallowable?(e)

  Axn::Extensions.best_effort(operation, action:) { raise e }
end

#callable?(value) ⇒ Boolean

Public so the contract DSL can validate a declared handler against the same notion of "invokable" used here at call time — what expects ..., user_facing: accepts is exactly what this invoker will actually call, with no second, divergent predicate. Requires both traits the invoker actually uses: to_proc (it runs the handler as instance_exec(&...)) and arity (it arity-filters the args). An object answering one but not the other would pass an arity-only check yet raise at call time — Procs/lambdas/Methods answer both.

Returns:

  • (Boolean)


47
# File 'lib/axn/core/flow/handlers/invoker.rb', line 47

def callable?(value) = value.respond_to?(:to_proc) && value.respond_to?(:arity)