Module: Statecraft::RSpec::StateReport

Defined in:
lib/statecraft/rspec/state_report.rb

Overview

The shared sentences of every failure message: where the record stands, what the graph declares from there, what is reachable right now, and which guards refused. Built strictly on the public introspection surface — the matchers add words, never new answers.

Class Method Summary collapse

Class Method Details

.current_state(record) ⇒ Object



12
13
14
# File 'lib/statecraft/rspec/state_report.rb', line 12

def current_state(record)
  record[record.class.statecraft_mounting.column]&.to_sym
end

.declared_shape(machine_class, state) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/statecraft/rspec/state_report.rb', line 29

def declared_shape(machine_class, state)
  descriptors = machine_class.transitions_from(state)
  return "no edges are declared from #{state.inspect}" if descriptors.empty?

  rendered = descriptors.map do |descriptor|
    if descriptor[:events].empty?
      "to #{descriptor[:to].inspect} (direct)"
    else
      "to #{descriptor[:to].inspect} via #{descriptor[:events].inspect}"
    end
  end
  "declared from #{state.inspect}: #{rendered.join("; ")}"
end

.declared_shape_of(record) ⇒ Object



43
44
45
# File 'lib/statecraft/rspec/state_report.rb', line 43

def declared_shape_of(record)
  declared_shape(machine(record), current_state(record))
end

.event_declared?(record, event_name) ⇒ Boolean

Returns:

  • (Boolean)


24
25
26
27
# File 'lib/statecraft/rspec/state_report.rb', line 24

def event_declared?(record, event_name)
  machine(record).transitions_from(current_state(record))
                 .any? { |descriptor| descriptor[:events].include?(event_name) }
end

.machine(record) ⇒ Object



20
21
22
# File 'lib/statecraft/rspec/state_report.rb', line 20

def machine(record)
  record.class.statecraft_mounting.machine_class
end

.reachable(record, metadata) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/statecraft/rspec/state_report.rb', line 47

def reachable(record, )
  transitions = record.available_transitions(metadata: )
  state = current_state(record).inspect
  return "nothing is reachable from #{state} right now" if transitions.empty?

  rendered = transitions.map { |availability| "to #{availability.to.inspect} via #{availability.via.inspect}" }
  "reachable from #{state}: #{rendered.join("; ")}"
end

.refusal(record, event_name, metadata) ⇒ Object



56
57
58
59
60
61
62
63
64
# File 'lib/statecraft/rspec/state_report.rb', line 56

def refusal(record, event_name, )
  refusals = record.refusals_for(event_name)
  if refusals.empty?
    "no record-layer guard refused — an input-reading guard: said no to metadata #{.inspect}"
  else
    named = refusals.map { |entry| "#{entry.guard.inspect} (#{entry.layer})" }
    "refused by record-layer guards: #{named.join(", ")}"
  end
end

.standing(record) ⇒ Object



16
17
18
# File 'lib/statecraft/rspec/state_report.rb', line 16

def standing(record)
  "#{record.class.name} in state #{current_state(record).inspect}"
end