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
- #compiled_graph ⇒ Object
- #declared_callbacks ⇒ Object
- #declared_edges ⇒ Object
- #declared_event_names ⇒ Object
- #declared_states ⇒ Object
- #event(name, from: nil, to: nil, guard: nil, record_guard: nil, input_guard: nil, lock: false, &declarations) ⇒ Object
- #events ⇒ Object
-
#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).
- #finalized? ⇒ Boolean
- #initial_state ⇒ Object
- #state(name, initial: false) ⇒ Object
- #states ⇒ Object
-
#strict! ⇒ Object
Opt-in shape strictness: with strict! the compiler additionally requires every declared state to be reachable from the initial one.
- #strict? ⇒ Boolean
- #transition(from:, to:, guard: nil, record_guard: nil, input_guard: nil, lock: false) ⇒ Object
-
#transitions_from(state) ⇒ Object
The graph's shape from one state: a frozen descriptor per outgoing edge,
eventsempty for a bare edge.
Instance Method Details
#compiled_graph ⇒ Object
150 151 152 |
# File 'lib/statecraft/machine.rb', line 150 def compiled_graph finalize! end |
#declared_callbacks ⇒ Object
177 178 179 |
# File 'lib/statecraft/machine.rb', line 177 def declared_callbacks @statecraft_declared_callbacks ||= CALLBACK_PHASES.to_h { |phase| [phase, []] } end |
#declared_edges ⇒ Object
169 170 171 |
# File 'lib/statecraft/machine.rb', line 169 def declared_edges @statecraft_declared_edges ||= [] end |
#declared_event_names ⇒ Object
173 174 175 |
# File 'lib/statecraft/machine.rb', line 173 def declared_event_names @statecraft_declared_event_names ||= [] end |
#declared_states ⇒ Object
165 166 167 |
# File 'lib/statecraft/machine.rb', line 165 def declared_states @statecraft_declared_states ||= [] end |
#event(name, from: nil, to: nil, guard: nil, record_guard: nil, input_guard: nil, lock: false, &declarations) ⇒ Object
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 |
# File 'lib/statecraft/machine.rb', line 75 def event(name, from: nil, to: nil, guard: nil, record_guard: nil, input_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, input_guard: input_guard, lock: lock) ensure @statecraft_current_event = nil end end end |
#events ⇒ Object
130 131 132 |
# File 'lib/statecraft/machine.rb', line 130 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).
161 162 163 |
# File 'lib/statecraft/machine.rb', line 161 def finalize! @statecraft_compiled_graph ||= Compiler.new(self).compile.tap { freeze_declarations } end |
#finalized? ⇒ Boolean
154 155 156 |
# File 'lib/statecraft/machine.rb', line 154 def finalized? !@statecraft_compiled_graph.nil? end |
#initial_state ⇒ Object
146 147 148 |
# File 'lib/statecraft/machine.rb', line 146 def initial_state compiled_graph.initial_state end |
#state(name, initial: false) ⇒ Object
61 62 63 |
# File 'lib/statecraft/machine.rb', line 61 def state(name, initial: false) declared_states << { name: name.to_sym, initial: initial } end |
#states ⇒ Object
126 127 128 |
# File 'lib/statecraft/machine.rb', line 126 def states compiled_graph.states end |
#strict! ⇒ Object
Opt-in shape strictness: with strict! the compiler additionally requires every declared state to be reachable from the initial one. Off by default — the column is written by more than the gem, so an unconnected state is legal unless the machine claims a closed graph.
120 121 122 |
# File 'lib/statecraft/machine.rb', line 120 def strict! @statecraft_strict = true end |
#strict? ⇒ Boolean
124 |
# File 'lib/statecraft/machine.rb', line 124 def strict? = @statecraft_strict == true |
#transition(from:, to:, guard: nil, record_guard: nil, input_guard: nil, lock: false) ⇒ Object
65 66 67 68 69 70 71 72 73 |
# File 'lib/statecraft/machine.rb', line 65 def transition(from:, to:, guard: nil, record_guard: nil, input_guard: nil, lock: false) declared_edges << { from: from.to_sym, to: to.to_sym, guards: Array(guard), record_guards: Array(record_guard), input_guards: Array(input_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.
139 140 141 142 143 144 |
# File 'lib/statecraft/machine.rb', line 139 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 |