Class: Phronomy::Workflow::Builder

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/workflow.rb

Overview

DSL builder for Phronomy::Workflow.define. Collects state/event/transition declarations and produces a WorkflowRunner.

Constant Summary collapse

FINISH =
Phronomy::WorkflowRunner::FINISH

Instance Method Summary collapse

Constructor Details

#initialize(context_class, state_store: nil) ⇒ Builder

Returns a new instance of Builder.



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/phronomy/workflow.rb', line 171

def initialize(context_class, state_store: nil)
  @context_class = context_class
  @state_store = state_store
  @initial = nil
  # Ordered list of declared state names (action states only, not wait states).
  @declared_states = []
  # { state_name => [callable, ...] } — entry actions registered via entry()
  @entry_actions = {}
  # { state_name => [callable, ...] } — exit actions registered via exit()
  @exit_actions = {}
  # Array of { from:, to:, guard:, on: } — all transitions in declaration order
  @transitions = []
  # Set of wait state names
  @wait_state_names = []
  # { state_name => Numeric } — per-state action timeout in seconds
  @action_timeouts = {}
end

Instance Method Details

#buildObject

Builds and returns a Phronomy::Workflow backed by a WorkflowRunner. Performs build-time validation of the graph structure:

- raises ArgumentError when no initial state is declared and no states have been defined
- raises ArgumentError when a transition references an undeclared target state
- warns when declared states are unreachable from the initial state

Raises:

  • (ArgumentError)

    on structural errors



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/phronomy/workflow.rb', line 332

def build
  entry_actions = @entry_actions.dup
  exit_actions = @exit_actions.dup

  validate_graph!

  # Auto-fire transitions (no :on): fire automatically when action completes.
  # External events (with :on): triggered manually via send_event.
  auto_transitions = []
  external_events = {}

  @transitions.each do |t|
    if t[:on]
      external_events[t[:on]] ||= []
      external_events[t[:on]] << {from: t[:from], to: t[:to], guard: t[:guard]}
    else
      auto_transitions << {from: t[:from], to: t[:to], guard: t[:guard]}
    end
  end

  runner = Phronomy::WorkflowRunner.new(
    state_class: @context_class,
    entry_actions: entry_actions,
    exit_actions: exit_actions,
    declared_states: @declared_states.dup,
    auto_transitions: auto_transitions,
    external_events: external_events,
    entry_point: @initial || @declared_states.first,
    wait_state_names: @wait_state_names,
    state_store: @state_store,
    action_timeouts: @action_timeouts.dup
  )

  Workflow.new(runner)
end

#entry(name, callable) ⇒ Object

Declares an entry action for a state. The callable is invoked when the workflow enters name. It receives the current context. Two styles are supported:

- Mutation-in-place: mutate context fields directly (+s.field = value+);
the return value is ignored.
- Immutable update: return a new context via +s.merge(field: value)+;
the returned context replaces the current one.

Multiple calls for the same state are allowed; callables fire in declaration order.

Parameters:

  • name (Symbol)

    state name

  • callable (#call)

    receives context; may return a new WorkflowContext



224
225
226
# File 'lib/phronomy/workflow.rb', line 224

def entry(name, callable)
  (@entry_actions[name] ||= []) << callable
end

#exit(name, callable) ⇒ Object

Declares an exit action for a state. The callable is invoked when the workflow leaves name. It receives the current context and should mutate it in place. Return value is ignored. Multiple calls for the same state are allowed; callables fire in declaration order.

Parameters:

  • name (Symbol)

    state name

  • callable (#call)

    receives context, mutates it in place



236
237
238
# File 'lib/phronomy/workflow.rb', line 236

def exit(name, callable)
  (@exit_actions[name] ||= []) << callable
end

#initial(state_name) ⇒ Object

Declares the initial (entry) state. rubocop:disable Style/TrivialAccessors

Parameters:

  • state_name (Symbol)


193
194
195
# File 'lib/phronomy/workflow.rb', line 193

def initial(state_name)
  @initial = state_name
end

#state(name, action: nil, action_timeout: nil) ⇒ Object

Declares an action state.

Parameters:

  • name (Symbol)

    state name

  • action (#call, nil) (defaults to: nil)

    optional entry action shorthand. state :generate, action: MY_PROC is equivalent to state :generate; entry :generate, MY_PROC.

  • action_timeout (Numeric, nil) (defaults to: nil)

    seconds before an async (Task-returning) entry action is cancelled and ActionTimeoutError is raised. Only applies when the action returns a Task or PendingOperation.



207
208
209
210
211
# File 'lib/phronomy/workflow.rb', line 207

def state(name, action: nil, action_timeout: nil)
  @declared_states << name
  @action_timeouts[name] = action_timeout if action_timeout
  entry(name, action) if action
end

#transition(from:, to:, guard: nil, on: nil) ⇒ Object

Declares a transition between states. Auto-fire transitions (no on:) fire automatically when an action state's action completes. External transitions (+on: :event_name+) are triggered manually via send_event. When guard: is provided the transition is taken only if the guard returns truthy for the current context. Multiple transitions from the same source are evaluated in declaration order; the first passing guard wins.

Parameters:

  • from (Symbol)

    source state

  • to (Symbol)

    destination state or :finish

  • guard (Proc, nil) (defaults to: nil)

    optional guard — receives context, returns truthy/falsy

  • on (Symbol, nil) (defaults to: nil)

    named event for manual triggers (e.g. :approve)



260
261
262
263
# File 'lib/phronomy/workflow.rb', line 260

def transition(from:, to:, guard: nil, on: nil)
  dest = (to == :__finish__) ? FINISH : to
  @transitions << {from: from, to: dest, guard: guard, on: on}
end

#wait_state(name) ⇒ Object

Declares a wait state that automatically halts execution when reached. No entry action is registered; the workflow pauses here until an event resumes it.

Parameters:

  • name (Symbol)

    wait state name (conventionally :awaiting_something)



244
245
246
# File 'lib/phronomy/workflow.rb', line 244

def wait_state(name)
  @wait_state_names << name
end