Module: RubyReactor::Dsl::AsyncMacros

Included in:
Reactor::ClassMethods
Defined in:
lib/ruby_reactor/dsl/async_macros.rb

Overview

The three ways a reactor sends work out of the calling process, kept together because they are one design decision seen from three distances:

background     — the rest of THIS reactor moves to a worker
               (`all: true` — EVERYTHING, incl. input validation)
async_step     — ONE step's work becomes its own job
async_reactor  — a whole nested reactor runs independently

Mixed into Dsl::Reactor::ClassMethods.

Instance Method Summary collapse

Instance Method Details

#async?Boolean

True only for the whole-reactor hand-off (background all: true) — the entire run, including input validation, happens in a worker.

Returns:



47
48
49
# File 'lib/ruby_reactor/dsl/async_macros.rb', line 47

def async?
  background_handoff&.fetch(:mode, nil) == :all
end

#async_reactor(name, child_reactor_class, &block) ⇒ Object

Dispatch a whole nested reactor to run INDEPENDENTLY — linked to this one by execution id for traceability, but excluded from its compensation graph. Fire-and-forget unless a later step reads result(:name), which blocks until the child is terminal and hands over the child's real Success/Failure to inspect.

Contrast with compose, which runs the child inline, synchronously, and fully wired into the parent's rollback path.



151
152
153
154
155
156
# File 'lib/ruby_reactor/dsl/async_macros.rb', line 151

def async_reactor(name, child_reactor_class, &block)
  builder = RubyReactor::Dsl::AsyncReactorBuilder.new(name, child_reactor_class, self)
  builder.instance_eval(&block) if block_given?

  steps[name] = builder.build
end

#async_step(name, impl = nil, &block) ⇒ Object

A step whose work is dispatched to its own independent worker job while this reactor keeps executing every other ready step. Same call shape and same block DSL as stepargument, run, compensate, undo, retries, validators all behave identically; only WHERE the body runs changes.

Any step reading result(:name) blocks (bounded) until the unit finishes. A failure with no reader does NOT compensate this reactor — compensation is opt-in, via a reader that inspects the result and returns Failure itself.



136
137
138
139
140
141
# File 'lib/ruby_reactor/dsl/async_macros.rb', line 136

def async_step(name, impl = nil, &block)
  builder = RubyReactor::Dsl::StepBuilder.new(name, impl, self)
  builder.instance_eval(&block) if block_given?

  steps[name] = builder.build(async_dispatch: :step)
end

#background(after: nil, before: nil, all: false) ⇒ Object

The single, unambiguous cut point between what runs in the calling process and what is handed to a worker. Replaces the per-step async flag, where only the first flagged step ever took effect and the rest were silently ignored, and the whole-reactor async true flag, which named the same idea with a different word.

background after:  :second   # :second is the LAST step to run here
background before: :third    # :third is the FIRST step in the worker
background all: true         # the ENTIRE reactor runs in the worker,
                            # including input validation

after:/before: name one cut point from opposite sides — identical in a linear chain, different in a DAG, where each pins the step it names. all: names no step: there is nothing left to pin, everything moves. Triggering is keyed to REACHING the named step (or, for all:, to the run starting at all), not to where this declaration sits in the class body.



32
33
34
35
36
# File 'lib/ruby_reactor/dsl/async_macros.rb', line 32

def background(after: nil, before: nil, all: false)
  point = validate_background_declaration!(after, before, all)

  @background_handoff = point
end

#background_handoffObject

The normalized { mode:, step: } pair — one reader, never a one-sided background_after, so no consumer can be accidentally implemented for after: only. step is nil when mode is :all.



41
42
43
# File 'lib/ruby_reactor/dsl/async_macros.rb', line 41

def background_handoff
  @background_handoff
end