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.



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/phronomy/workflow.rb', line 113

def initialize(context_class, state_store: nil)
  @context_class = context_class
  @state_store = state_store
  @initial = nil
  # { node_name => callable }
  @states = {}
  # Array of { from:, to: } — auto-transitions after a state action
  @after_transitions = []
  # Array of { name:, from:, to:, guard: } — event-driven transitions
  @event_transitions = []
  # Set of wait state names
  @wait_state_names = []
end

Instance Method Details

#after(from, to:) ⇒ Object

Declares an automatic transition that fires after a state's action completes.

Parameters:

  • from (Symbol)

    source state name

  • to (Symbol)

    destination state name or :finish



153
154
155
156
# File 'lib/phronomy/workflow.rb', line 153

def after(from, to:)
  dest = (to == :__finish__) ? FINISH : to
  @after_transitions << {from: from, to: dest}
end

#buildObject

Builds and returns a Phronomy::Workflow backed by a WorkflowRunner.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/phronomy/workflow.rb', line 173

def build
  nodes = @states.dup

  # After-transitions: { from => to }
  # Unconditional transitions that fire automatically after an action state completes.
  after_transitions = @after_transitions.each_with_object({}) do |t, h|
    h[t[:from]] = t[:to]
  end

  # Route transitions: { from => {event_name:, entries: [{guard:, to:}, ...]} }
  # Events declared from action states (not wait states) fire automatically
  # after the action completes. The event name is used to register the
  # state_machines event and may be any symbol (e.g. :route, :route_review).
  # Declaration order is preserved so guarded entries appear before fallbacks.
  route_transitions = {}

  # External events: { event_name => [{from:, to:, guard:}, ...] }
  # Events declared from wait states, triggered by human input (e.g. :approve).
  external_events = {}

  @event_transitions.each do |t|
    if @wait_state_names.include?(t[:from])
      # Source is a wait state → external event
      external_events[t[:name]] ||= []
      external_events[t[:name]] << {from: t[:from], to: t[:to], guard: t[:guard]}
    else
      # Source is an action state → routing event (auto-fires after action)
      # The event name is taken from the first declaration for each from-state.
      route_transitions[t[:from]] ||= {event_name: t[:name], entries: []}
      route_transitions[t[:from]][:entries] << {guard: t[:guard], to: t[:to]}
    end
  end

  runner = Phronomy::WorkflowRunner.new(
    state_class: @context_class,
    nodes: nodes,
    after_transitions: after_transitions,
    route_transitions: route_transitions,
    external_events: external_events,
    entry_point: @initial || nodes.keys.first,
    wait_state_names: @wait_state_names,
    state_store: @state_store
  )

  Workflow.new(runner)
end

#event(name, from:, to:, guard: nil) ⇒ Object

Declares an event-driven transition. When +guard:+ is provided, the transition is taken only if the guard returns truthy for the current context. Multiple events with the same name and source are evaluated in declaration order; the first passing guard wins.

Parameters:

  • name (Symbol)

    event name

  • from (Symbol)

    source state where this event can be fired

  • to (Symbol)

    destination state or :finish

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

    optional guard — receives context, returns truthy/falsy



167
168
169
170
# File 'lib/phronomy/workflow.rb', line 167

def event(name, from:, to:, guard: nil)
  dest = (to == :__finish__) ? FINISH : to
  @event_transitions << {name: name, from: from, to: dest, guard: guard}
end

#initial(state_name) ⇒ Object

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

Parameters:

  • state_name (Symbol)


130
131
132
# File 'lib/phronomy/workflow.rb', line 130

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)

    callable invoked when entering the state. If nil, the state is treated as a no-op pass-through.



139
140
141
# File 'lib/phronomy/workflow.rb', line 139

def state(name, action: nil)
  @states[name] = action || ->(s) { s }
end

#wait_state(name) ⇒ Object

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

Parameters:

  • name (Symbol)

    wait state name (conventionally :awaiting_something)



146
147
148
# File 'lib/phronomy/workflow.rb', line 146

def wait_state(name)
  @wait_state_names << name
end