Module: Axn::Core::Flow::Handlers::Invoker
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
- #call(action:, handler:, exception: nil, operation: "executing handler", allow_flow_control: false) ⇒ Object
-
#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.
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.
47 |
# File 'lib/axn/core/flow/handlers/invoker.rb', line 47 def callable?(value) = value.respond_to?(:to_proc) && value.respond_to?(:arity) |