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.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/phronomy/workflow.rb', line 128

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 = []
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



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'lib/phronomy/workflow.rb', line 283

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
  )

  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



175
176
177
# File 'lib/phronomy/workflow.rb', line 175

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



187
188
189
# File 'lib/phronomy/workflow.rb', line 187

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)


148
149
150
# File 'lib/phronomy/workflow.rb', line 148

def initial(state_name)
  @initial = state_name
end

#state(name, action: 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+.



159
160
161
162
# File 'lib/phronomy/workflow.rb', line 159

def state(name, action: nil)
  @declared_states << name
  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)



211
212
213
214
# File 'lib/phronomy/workflow.rb', line 211

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)



195
196
197
# File 'lib/phronomy/workflow.rb', line 195

def wait_state(name)
  @wait_state_names << name
end