Class: Phronomy::Agent::Lifecycle::PhaseMachineBuilder Private

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/agent/lifecycle/phase_machine_builder.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Builds the anonymous state-machine Class used by WorkflowRunner to track workflow phase transitions.

Extracted from WorkflowRunner#build_phase_machine_class to reduce the span of WorkflowRunner's initializer and to give the FSM construction logic an explicit, testable home.

Call #build to obtain the generated +Class+. The returned class responds to +#context+ / +#context=+ and +#async_pending+ / +#async_pending=+, and has a +state_machine :phase+ definition with all registered transitions and callbacks.

Instance Method Summary collapse

Constructor Details

#initialize(entry_point:, declared_states:, wait_state_names:, external_events:, entry_actions:, action_timeouts:, auto_transitions:, exit_actions:) ⇒ PhaseMachineBuilder

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of PhaseMachineBuilder.

Parameters:

  • entry_point (Symbol)

    initial state for the phase machine

  • declared_states (Array<Symbol>)

    all states declared in the workflow

  • wait_state_names (Array<Symbol>)

    states that wait for external events

  • external_events (Hash{Symbol => Array<Hash>})

    +{ event_name => [to:, guard:, ...] }+

  • entry_actions (Hash{Symbol => Array<#call>})

    +{ state_name => [callable, ...] }+

  • action_timeouts (Hash{Symbol => Numeric})

    +{ state_name => seconds }+

  • auto_transitions (Array<Hash>)

    +[{ from:, to:, guard: }, ...]+ — all auto-fire transitions

  • exit_actions (Hash{Symbol => Array<#call>})

    +{ state_name => [callable, ...] }+



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/phronomy/agent/lifecycle/phase_machine_builder.rb', line 36

def initialize(
  entry_point:,
  declared_states:,
  wait_state_names:,
  external_events:,
  entry_actions:,
  action_timeouts:,
  auto_transitions:,
  exit_actions:
)
  @entry_point = entry_point
  @declared_states = declared_states
  @wait_state_names = wait_state_names
  @external_events = external_events
  @entry_actions = entry_actions
  @action_timeouts = action_timeouts
  @auto_transitions = auto_transitions
  @exit_actions = exit_actions
end

Instance Method Details

#buildClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Constructs and returns the anonymous phase-machine Class.

Returns:

  • (Class)

    an anonymous class with a +state_machine :phase+ definition

Raises:

  • (ArgumentError)

    if state_machines raises during class construction



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/phronomy/agent/lifecycle/phase_machine_builder.rb', line 61

def build
  entry = @entry_point
  all_states = (@declared_states + @wait_state_names + [:__end__]).uniq
  auto_trans = @auto_transitions
  ext_events = @external_events
  entry_acts = @entry_actions
  exit_acts = @exit_actions
  act_timeouts = @action_timeouts
  build_cb = method(:build_entry_callback)

  Class.new do
    # Holds the current WorkflowContext so guards and callbacks can read it.
    attr_accessor :context

    # Set to true by an entry action that returned an awaitable Task.
    # When true, FSMSession skips the automatic advance_or_halt step and
    # waits for the async worker thread to post a state_completed event back.
    attr_accessor :async_pending

    state_machine :phase, initial: entry do
      all_states.each { |s| state s }

      # Auto-fire transitions: all auto transitions unified under :state_completed.
      # Includes unguarded (unconditional) and guarded (conditional) transitions.
      # Declaration order is preserved; guards are evaluated before unguarded fallbacks.
      event :state_completed do
        auto_trans.each do |t|
          if t[:guard]
            guard_proc = t[:guard]
            transition t[:from] => t[:to], :if => ->(m) { guard_proc.call(m.context) }
          else
            transition t[:from] => t[:to]
          end
        end
      end

      # External events: human-in-the-loop triggers from wait states.
      ext_events.each do |ev_name, transitions|
        event ev_name do
          transitions.each do |t|
            if t[:guard]
              guard_proc = t[:guard]
              transition t[:from] => t[:to], :if => ->(m) { guard_proc.call(m.context) }
            else
              transition t[:from] => t[:to]
            end
          end
        end
      end

      # Entry callbacks: fire after_transition into each state.
      #    Each callable is registered as a separate callback; state_machines
      #    accumulates them and fires in declaration order.
      #    If the callable returns a WorkflowContext (e.g. via s.merge(...)),
      #    the returned context replaces the current one on the tracker.
      entry_acts.each do |state_name, callables|
        callables.each do |callable|
          cb = build_cb.call(callable, state_name, act_timeouts[state_name])
          after_transition to: state_name, &cb
        end
      end

      # Exit callbacks: fire before_transition out of each state.
      #    Each callable is registered as a separate callback; state_machines
      #    accumulates them and fires in declaration order.
      exit_acts.each do |state_name, callables|
        callables.each do |callable|
          before_transition from: state_name do |machine|
            callable.call(machine.context)
          end
        end
      end
    end
  end
rescue => e
  raise ArgumentError, "Failed to build phase machine: #{e.message}"
end