Module: Statecraft::Machine::ClassMethods

Defined in:
lib/statecraft/machine.rb

Overview

Class-level DSL collected declaratively and compiled by finalize!. Names arrive as symbols or strings interchangeably (the ActiveRecord idiom) and are normalized to symbols at the declaration line.

Instance Method Summary collapse

Instance Method Details

#compiled_graphObject



135
136
137
# File 'lib/statecraft/machine.rb', line 135

def compiled_graph
  finalize!
end

#declared_callbacksObject



162
163
164
# File 'lib/statecraft/machine.rb', line 162

def declared_callbacks
  @statecraft_declared_callbacks ||= CALLBACK_PHASES.to_h { |phase| [phase, []] }
end

#declared_edgesObject



154
155
156
# File 'lib/statecraft/machine.rb', line 154

def declared_edges
  @statecraft_declared_edges ||= []
end

#declared_event_namesObject



158
159
160
# File 'lib/statecraft/machine.rb', line 158

def declared_event_names
  @statecraft_declared_event_names ||= []
end

#declared_statesObject



150
151
152
# File 'lib/statecraft/machine.rb', line 150

def declared_states
  @statecraft_declared_states ||= []
end

#event(name, from: nil, to: nil, guard: nil, record_guard: nil, lock: false, &declarations) ⇒ Object



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
# File 'lib/statecraft/machine.rb', line 71

def event(name, from: nil, to: nil, guard: nil, record_guard: nil, lock: false, &declarations)
  name = name.to_sym
  declared_event_names << name
  if declarations
    if from || to
      raise CompilationError, "event #{name.inspect} takes either inline from:/to: or a block, not both"
    end

    begin
      @statecraft_current_event = { name: name, lock: lock }
      yield
    ensure
      @statecraft_current_event = nil
    end
  else
    raise CompilationError, "event #{name.inspect} needs from:/to: or a block" if from.nil? || to.nil?

    @statecraft_current_event = { name: name, lock: false }
    begin
      transition(from: from, to: to, guard: guard, record_guard: record_guard, lock: lock)
    ensure
      @statecraft_current_event = nil
    end
  end
end

#eventsObject



115
116
117
# File 'lib/statecraft/machine.rb', line 115

def events
  compiled_graph.events.keys
end

#finalize!Object

Compilation memoizes the graph and freezes the declaration lists, so a reopened machine class fails loudly on any late state/transition/event instead of silently ignoring it (callbacks already freeze in compile).



146
147
148
# File 'lib/statecraft/machine.rb', line 146

def finalize!
  @statecraft_compiled_graph ||= Compiler.new(self).compile.tap { freeze_declarations }
end

#finalized?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/statecraft/machine.rb', line 139

def finalized?
  !@statecraft_compiled_graph.nil?
end

#initial_stateObject



131
132
133
# File 'lib/statecraft/machine.rb', line 131

def initial_state
  compiled_graph.initial_state
end

#state(name, initial: false) ⇒ Object



58
59
60
# File 'lib/statecraft/machine.rb', line 58

def state(name, initial: false)
  declared_states << { name: name.to_sym, initial: initial }
end

#statesObject



111
112
113
# File 'lib/statecraft/machine.rb', line 111

def states
  compiled_graph.states
end

#transition(from:, to:, guard: nil, record_guard: nil, lock: false) ⇒ Object



62
63
64
65
66
67
68
69
# File 'lib/statecraft/machine.rb', line 62

def transition(from:, to:, guard: nil, record_guard: nil, lock: false)
  declared_edges << {
    from: from.to_sym, to: to.to_sym,
    guards: Array(guard), record_guards: Array(record_guard),
    lock: lock || current_event_lock,
    event: current_event_name
  }
end

#transitions_from(state) ⇒ Object

The graph's shape from one state: a frozen descriptor per outgoing edge, events empty for a bare edge. Shape, not a prediction — no guards are consulted, and a state outside the graph honestly owns no edges. Class-level on purpose: the answer is a property of the graph, never of a record.



124
125
126
127
128
129
# File 'lib/statecraft/machine.rb', line 124

def transitions_from(state)
  normalized = state.to_sym
  compiled_graph.edges.filter_map do |(from, _to), edge|
    { to: edge.to, events: edge.event_names }.freeze if from == normalized
  end.freeze
end