state_machines-mermaid 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 387fad10965ac7756257b30ac3a36b20770c2702631796c2727c3b0cb4eeea69
4
- data.tar.gz: 26d45114695fab1ed434862f36520c4006f895d5db17903519995366e6045090
3
+ metadata.gz: 0d80eff2b0ed1c6b458aba82d93fd4e423281ad88d4e6c32b60057e17e2be418
4
+ data.tar.gz: fbd58452672027b070cd34e0be625477a1acccc553e1f0d4396496446c418824
5
5
  SHA512:
6
- metadata.gz: c92e86302c88b96d81dded14771366d26225970a915e9077d0b71311fdd02cdc4e4b2a068311afbc1fca8e951e018d04fb4ed39f5e311c92035316e1acf97bb2
7
- data.tar.gz: bb1ce73610a01f2892c2dc1b7737eff292b851e2a988e20a0bf13628a5c28e24a492cc9c6065714adb7278da03b7574354264f96c8a8619d714b143e5df032b3
6
+ metadata.gz: f0d020c31b57e63e05e880d57c855df53b7fa07b60957f20e494ce314144d6215ab339fe5d06b8ba5f09aeb000f6e025ed7b1826c5954bd3018a391cdf55f398
7
+ data.tar.gz: 267911f506950eb0fc12792eb60722c3c2aba6f287850e8d249e33a6dba9011eb138df373e0c81308851ef9cc1905b4b8702fcda7d24e938826ddfbb2aa2bdb0
@@ -14,6 +14,7 @@ module StateMachines
14
14
  @last_state_metadata = nil
15
15
  @last_transition_metadata = nil
16
16
  @last_transition_metadata_map = nil
17
+ @last_state_id_map = nil
17
18
  end
18
19
 
19
20
  # The new simplified approach - leverages the diagram gem's semantic structure
@@ -75,10 +76,15 @@ module StateMachines
75
76
  return diagram.to_mermaid unless @last_transition_metadata
76
77
 
77
78
  lines = ["stateDiagram-v2"]
79
+ state_id_map = mermaid_state_id_map(diagram)
78
80
 
79
81
  diagram.states.each do |state|
80
- fragment = state.respond_to?(:to_mermaid_fragment) ? state.to_mermaid_fragment : state.id
82
+ fragment = render_state_fragment(state, state_id_map.fetch(state.id))
81
83
  lines << " #{fragment}" if fragment && !fragment.empty?
84
+
85
+ metadata = @last_state_metadata[state.id] || {}
86
+ lines << " [*] --> #{state_id_map.fetch(state.id)}" if metadata[:type] == 'initial'
87
+ lines << " #{state_id_map.fetch(state.id)} --> [*]" if metadata[:type] == 'final'
82
88
  end
83
89
 
84
90
  diagram.transitions.each do |transition|
@@ -126,16 +132,11 @@ module StateMachines
126
132
  def build_condition_tokens(conditions)
127
133
  return [] unless conditions.is_a?(Hash)
128
134
 
129
- tokens = []
130
- Array(conditions[:if]).each do |token|
131
- next if token.nil? || token.to_s.empty?
132
- tokens << "if #{token}"
133
- end
134
- Array(conditions[:unless]).each do |token|
135
- next if token.nil? || token.to_s.empty?
136
- tokens << "unless #{token}"
135
+ %i[if unless if_state unless_state if_all_states unless_all_states if_any_state unless_any_state].flat_map do |kind|
136
+ Array(conditions[kind]).filter_map do |token|
137
+ "#{kind} #{token}" unless token.nil? || token.to_s.empty?
138
+ end
137
139
  end
138
- tokens
139
140
  end
140
141
 
141
142
  def build_callback_tokens(callbacks)
@@ -153,39 +154,56 @@ module StateMachines
153
154
 
154
155
  def format_callback_reference(callback)
155
156
  case callback
156
- when Symbol, String
157
- callback.to_s
158
157
  when Proc, Method
159
- if callback.respond_to?(:source_location) && callback.source_location
160
- file, line = callback.source_location
161
- filename = File.basename(file) if file
162
- "lambda@#{filename}:#{line}"
163
- else
164
- 'lambda'
165
- end
158
+ StateMachines::Diagram::Builder.callable_label(callback)
166
159
  else
167
160
  callback.to_s
168
161
  end
169
162
  end
170
163
 
171
164
  def format_node_id(node_id)
172
- node_id == '*' ? '[*]' : node_id
165
+ return '[*]' if node_id == '*'
166
+
167
+ @last_state_id_map.fetch(node_id, node_id)
173
168
  end
174
169
 
175
- def filter_mermaid_for_state(mermaid_syntax, state_name)
176
- lines = mermaid_syntax.split("\n")
177
- relevant_lines = lines.select do |line|
178
- line.include?(state_name) || line.start_with?("stateDiagram")
170
+ def mermaid_state_id_map(diagram)
171
+ @last_state_id_map ||= diagram.states.each_with_object({}) do |state, map|
172
+ map[state.id] = safe_state_id(state.id)
179
173
  end
180
- relevant_lines.join("\n")
174
+ end
175
+
176
+ def safe_state_id(state_id)
177
+ value = state_id.to_s
178
+ return value if value.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
179
+
180
+ "state_#{value.bytes.map { |byte| byte.to_s(16).rjust(2, '0') }.join}"
181
+ end
182
+
183
+ def render_state_fragment(state, rendered_id)
184
+ label = state.label || state.id
185
+ return "#{rendered_id} : #{label}" if rendered_id == state.id
186
+
187
+ "state #{mermaid_string(label)} as #{rendered_id}"
188
+ end
189
+
190
+ def mermaid_string(value)
191
+ %Q{"#{value.to_s.gsub('"', '\\\"')}"}
192
+ end
193
+
194
+ def filter_mermaid_for_state(mermaid_syntax, state_name)
195
+ filter_mermaid_lines(mermaid_syntax, state_name)
181
196
  end
182
197
 
183
198
  def filter_mermaid_for_event(mermaid_syntax, event_name)
184
- lines = mermaid_syntax.split("\n")
185
- relevant_lines = lines.select do |line|
186
- line.include?(event_name) || line.start_with?("stateDiagram")
187
- end
188
- relevant_lines.join("\n")
199
+ filter_mermaid_lines(mermaid_syntax, event_name)
200
+ end
201
+
202
+ # Keeps only the diagram header and the lines mentioning the given term
203
+ def filter_mermaid_lines(mermaid_syntax, term)
204
+ mermaid_syntax.split("\n").select do |line|
205
+ line.include?(term) || line.start_with?("stateDiagram")
206
+ end.join("\n")
189
207
  end
190
208
  end
191
209
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module StateMachines
4
4
  module Mermaid
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -46,12 +46,23 @@ class MermaidRendererTest < Minitest::Test
46
46
  end
47
47
 
48
48
  def test_final_states
49
+ klass = Class.new do
50
+ attr_accessor :state
51
+
52
+ state_machine :state, initial: :idle do
53
+ event :finish do
54
+ transition idle: :done
55
+ end
56
+ end
57
+ end
58
+
49
59
  io = StringIO.new
50
- StateMachines::Mermaid::Renderer.draw_machine(Character.state_machine(:status), io: io)
60
+ StateMachines::Mermaid::Renderer.draw_machine(klass.state_machine(:state), io: io)
51
61
 
52
62
  output = io.string
53
- # The mermaid gem doesn't add [*] for final states by default
54
- assert_includes output, "dead : dead"
63
+ assert_includes output, "done : done"
64
+ assert_includes output, "[*] --> idle"
65
+ assert_includes output, "done --> [*]"
55
66
  end
56
67
 
57
68
  def test_multiple_from_states
@@ -143,11 +154,10 @@ class MermaidRendererTest < Minitest::Test
143
154
  StateMachines::Mermaid::Renderer.draw_machine(klass.state_machine(:status), io: io)
144
155
 
145
156
  output = io.string
146
- # The mermaid gem doesn't sanitize IDs by default
147
- assert_includes output, "waiting-for-input"
148
- assert_includes output, "processing/data"
149
- assert_includes output, "error!"
150
- assert_includes output, "waiting-for-input --> processing/data"
157
+ assert_includes output, 'state "waiting-for-input" as state_77616974696e672d666f722d696e707574'
158
+ assert_includes output, 'state "processing/data" as state_70726f63657373696e672f64617461'
159
+ assert_includes output, 'state "error!" as state_6572726f7221'
160
+ assert_includes output, 'state_77616974696e672d666f722d696e707574 --> state_70726f63657373696e672f64617461'
151
161
  end
152
162
 
153
163
  def test_dragon_age_progression
@@ -190,6 +200,33 @@ class MermaidRendererTest < Minitest::Test
190
200
  assert output.include?("-->"), "Should have transitions"
191
201
  end
192
202
 
203
+ def test_renders_coordinated_state_guards
204
+ klass = Class.new do
205
+ attr_accessor :state, :gate
206
+
207
+ state_machine :gate, initial: :locked do
208
+ event :unlock do
209
+ transition locked: :unlocked
210
+ end
211
+ end
212
+
213
+ state_machine :state, initial: :idle do
214
+ event :run do
215
+ transition idle: :running, if_state: { gate: :unlocked }
216
+ end
217
+ end
218
+ end
219
+
220
+ io = StringIO.new
221
+ StateMachines::Mermaid::Renderer.draw_machine(
222
+ klass.state_machine(:state),
223
+ io: io,
224
+ show_conditions: true
225
+ )
226
+
227
+ assert_includes io.string, '[if_state gate=unlocked]'
228
+ end
229
+
193
230
  def test_all_character_types_render_properly
194
231
  [@dragon, @mage, @troll, @regiment].each do |character|
195
232
  character.class.state_machines.each do |name, machine|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_machines-mermaid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.0
32
+ version: 0.2.0
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.0
39
+ version: 0.2.0
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: mermaid
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -83,16 +83,16 @@ dependencies:
83
83
  name: minitest
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - ">="
86
+ - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: 5.6.0
88
+ version: '6.0'
89
89
  type: :development
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ">="
93
+ - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: 5.6.0
95
+ version: '6.0'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: minitest-reporters
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -140,14 +140,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - ">="
142
142
  - !ruby/object:Gem::Version
143
- version: 3.3.0
143
+ version: '4.0'
144
144
  required_rubygems_version: !ruby/object:Gem::Requirement
145
145
  requirements:
146
146
  - - ">="
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
149
  requirements: []
150
- rubygems_version: 3.6.9
150
+ rubygems_version: 4.0.20
151
151
  specification_version: 4
152
152
  summary: Mermaid renderer for state machines
153
153
  test_files: