state_machines-graphviz 0.0.1 → 0.1.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 +5 -5
- data/LICENSE.txt +1 -1
- data/README.md +71 -5
- data/lib/state_machines/graphviz/graph.rb +5 -5
- data/lib/state_machines/graphviz/renderer.rb +155 -0
- data/lib/state_machines/graphviz/version.rb +3 -1
- data/lib/state_machines/graphviz.rb +35 -1
- data/lib/state_machines/tasks/railtie.rb +11 -0
- data/lib/state_machines/tasks/state_machines.rake +5 -0
- data/lib/state_machines-graphviz.rb +8 -0
- data/{spec → test}/files/switch.rb +2 -0
- data/test/graph_default_test.rb +24 -0
- data/test/graph_edges_test.rb +28 -0
- data/test/graph_nodes_test.rb +25 -0
- data/test/graph_output_test.rb +21 -0
- data/test/machine_class_drawing_test.rb +28 -0
- data/test/machine_drawing_test.rb +72 -0
- data/test/machine_drawing_with_dynamic_states_test.rb +38 -0
- data/test/machine_drawing_with_integer_states_test.rb +38 -0
- data/test/machine_drawing_with_nil_states_test.rb +37 -0
- data/test/state_drawing_final_test.rb +17 -0
- data/test/state_drawing_initial_test.rb +25 -0
- data/test/state_drawing_lambda_value_test.rb +21 -0
- data/test/state_drawing_nil_name_test.rb +22 -0
- data/test/state_drawing_non_final_test.rb +20 -0
- data/test/state_drawing_test.rb +33 -0
- data/test/state_drawing_with_human_name_test.rb +20 -0
- data/test/test_helper.rb +6 -0
- metadata +64 -28
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.travis.yaml +0 -10
- data/Gemfile +0 -4
- data/Rakefile +0 -10
- data/lib/state_machines/graphviz/monkeypatch.rb +0 -161
- data/spec/graph_spec.rb +0 -92
- data/spec/machine_drawing_spec.rb +0 -215
- data/spec/machine_spec.rb +0 -19
- data/spec/spec_helper.rb +0 -2
- data/spec/state_spec.rb +0 -142
- data/state_machines-graphviz.gemspec +0 -25
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
|
|
5
|
+
describe StateMachines::Graph do
|
|
6
|
+
def setup
|
|
7
|
+
@klass = Class.new do
|
|
8
|
+
def self.name
|
|
9
|
+
@name ||= "Vehicle_#{rand(1_000_000)}"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@machine = StateMachines::Machine.new(@klass, initial: :parked)
|
|
14
|
+
@machine.event :ignite do
|
|
15
|
+
transition parked: :idling
|
|
16
|
+
end
|
|
17
|
+
@machine.state :parked, value: nil
|
|
18
|
+
|
|
19
|
+
@graph = @machine.draw
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_should_draw_all_states
|
|
23
|
+
assert(@graph.node_count, 3)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_should_draw_all_events
|
|
27
|
+
assert(@graph.edge_count, 2)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_should_draw_machine
|
|
31
|
+
assert(File.exist?("doc/state_machines/#{@klass.name}_state.png"))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def teardown
|
|
35
|
+
assert(FileUtils.rm(Dir["doc/state_machines/#{@klass.name}_state.png"]))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked)
|
|
8
|
+
|
|
9
|
+
graph = StateMachines::Graph.new('test')
|
|
10
|
+
@state.draw(graph)
|
|
11
|
+
@node = graph.get_node('parked')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_should_use_doublecircle_as_shape
|
|
15
|
+
assert_equal('doublecircle', @node['shape'].to_s.gsub('"', ''))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked, initial: true)
|
|
8
|
+
@machine.event :ignite do
|
|
9
|
+
transition parked: :idling
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@graph = StateMachines::Graph.new('test')
|
|
13
|
+
@state.draw(@graph)
|
|
14
|
+
@node = @graph.get_node('parked')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_should_use_ellipse_as_shape
|
|
18
|
+
assert_equal 'ellipse', @node['shape'].to_s.gsub('"', '')
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_should_draw_edge_between_point_and_state
|
|
22
|
+
assert_equal(2, @graph.node_count)
|
|
23
|
+
assert_equal(1, @graph.edge_count)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked, value: -> {})
|
|
8
|
+
|
|
9
|
+
graph = StateMachines::Graph.new('test')
|
|
10
|
+
@state.draw(graph)
|
|
11
|
+
@node = graph.get_node('parked')
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_should_have_a_node
|
|
15
|
+
assert(@node)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_should_use_description_as_label
|
|
19
|
+
assert_equal('parked (*)', @node['label'].to_s.gsub('"', ''))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
8
|
+
@machine.states << @state = StateMachines::State.new(@machine, nil)
|
|
9
|
+
|
|
10
|
+
graph = StateMachines::Graph.new('test')
|
|
11
|
+
@state.draw(graph)
|
|
12
|
+
@node = graph.get_node('nil')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_should_have_a_node
|
|
16
|
+
assert(@node)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_should_use_description_as_label
|
|
20
|
+
assert_equal 'nil', @node['label'].to_s.gsub('"', '')
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked)
|
|
8
|
+
@machine.event :ignite do
|
|
9
|
+
transition parked: :idling
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
graph = StateMachines::Graph.new('test')
|
|
13
|
+
@state.draw(graph)
|
|
14
|
+
@node = graph.get_node('parked')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_should_use_ellipse_as_shape
|
|
18
|
+
assert_equal('ellipse', @node['shape'].to_s.gsub('"', ''))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
|
|
5
|
+
describe StateMachines::Graph do
|
|
6
|
+
def setup
|
|
7
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
8
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked, value: 1)
|
|
9
|
+
@machine.event :ignite do
|
|
10
|
+
transition parked: :idling
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
graph = StateMachines::Graph.new('test')
|
|
14
|
+
@state.draw(graph)
|
|
15
|
+
@node = graph.get_node('parked')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_should_use_ellipse_shape
|
|
19
|
+
assert_equal(@node['shape'].to_s.gsub('"', ''), 'ellipse')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_should_set_width_to_one
|
|
23
|
+
assert_equal('1', @node['width'].to_s.gsub('"', ''))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_should_set_height_to_one
|
|
27
|
+
assert_equal('1', @node['height'].to_s.gsub('"', ''))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_should_use_description_as_label
|
|
31
|
+
assert_equal(@node['label'].to_s.gsub('"', ''), 'parked (1)')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
describe StateMachines::Graph do
|
|
5
|
+
def setup
|
|
6
|
+
@machine = StateMachines::Machine.new(Class.new)
|
|
7
|
+
@machine.states << @state = StateMachines::State.new(@machine, :parked, human_name: 'Parked')
|
|
8
|
+
@machine.event :ignite do
|
|
9
|
+
transition parked: :idling
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
graph = StateMachines::Graph.new('test')
|
|
13
|
+
@state.draw(graph, human_name: true)
|
|
14
|
+
@node = graph.get_node('parked')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_should_use_description_with_human_name_as_label
|
|
18
|
+
assert_equal('Parked', @node['label'].to_s.gsub('"', ''))
|
|
19
|
+
end
|
|
20
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: state_machines-graphviz
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
8
8
|
- Aaron Pfeifer
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: state_machines
|
|
@@ -25,6 +24,20 @@ dependencies:
|
|
|
25
24
|
- - ">="
|
|
26
25
|
- !ruby/object:Gem::Version
|
|
27
26
|
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: state_machines-diagram
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.1.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.1.0
|
|
28
41
|
- !ruby/object:Gem::Dependency
|
|
29
42
|
name: ruby-graphviz
|
|
30
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -68,49 +81,57 @@ dependencies:
|
|
|
68
81
|
- !ruby/object:Gem::Version
|
|
69
82
|
version: '0'
|
|
70
83
|
- !ruby/object:Gem::Dependency
|
|
71
|
-
name:
|
|
84
|
+
name: minitest
|
|
72
85
|
requirement: !ruby/object:Gem::Requirement
|
|
73
86
|
requirements:
|
|
74
87
|
- - '='
|
|
75
88
|
- !ruby/object:Gem::Version
|
|
76
|
-
version:
|
|
89
|
+
version: 5.27.0
|
|
77
90
|
type: :development
|
|
78
91
|
prerelease: false
|
|
79
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
80
93
|
requirements:
|
|
81
94
|
- - '='
|
|
82
95
|
- !ruby/object:Gem::Version
|
|
83
|
-
version:
|
|
84
|
-
description: Graphviz module for state
|
|
96
|
+
version: 5.27.0
|
|
97
|
+
description: Graphviz module for state machines
|
|
85
98
|
email:
|
|
86
99
|
- terminale@gmail.com
|
|
87
100
|
executables: []
|
|
88
101
|
extensions: []
|
|
89
102
|
extra_rdoc_files: []
|
|
90
103
|
files:
|
|
91
|
-
- ".gitignore"
|
|
92
|
-
- ".rspec"
|
|
93
|
-
- ".travis.yaml"
|
|
94
|
-
- Gemfile
|
|
95
104
|
- LICENSE.txt
|
|
96
105
|
- README.md
|
|
97
|
-
-
|
|
106
|
+
- lib/state_machines-graphviz.rb
|
|
98
107
|
- lib/state_machines/graphviz.rb
|
|
99
108
|
- lib/state_machines/graphviz/graph.rb
|
|
100
|
-
- lib/state_machines/graphviz/
|
|
109
|
+
- lib/state_machines/graphviz/renderer.rb
|
|
101
110
|
- lib/state_machines/graphviz/version.rb
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
|
|
111
|
+
- lib/state_machines/tasks/railtie.rb
|
|
112
|
+
- lib/state_machines/tasks/state_machines.rake
|
|
113
|
+
- test/files/switch.rb
|
|
114
|
+
- test/graph_default_test.rb
|
|
115
|
+
- test/graph_edges_test.rb
|
|
116
|
+
- test/graph_nodes_test.rb
|
|
117
|
+
- test/graph_output_test.rb
|
|
118
|
+
- test/machine_class_drawing_test.rb
|
|
119
|
+
- test/machine_drawing_test.rb
|
|
120
|
+
- test/machine_drawing_with_dynamic_states_test.rb
|
|
121
|
+
- test/machine_drawing_with_integer_states_test.rb
|
|
122
|
+
- test/machine_drawing_with_nil_states_test.rb
|
|
123
|
+
- test/state_drawing_final_test.rb
|
|
124
|
+
- test/state_drawing_initial_test.rb
|
|
125
|
+
- test/state_drawing_lambda_value_test.rb
|
|
126
|
+
- test/state_drawing_nil_name_test.rb
|
|
127
|
+
- test/state_drawing_non_final_test.rb
|
|
128
|
+
- test/state_drawing_test.rb
|
|
129
|
+
- test/state_drawing_with_human_name_test.rb
|
|
130
|
+
- test/test_helper.rb
|
|
131
|
+
homepage: https://github.com/state-machines/state_machines-graphviz
|
|
110
132
|
licenses:
|
|
111
133
|
- MIT
|
|
112
134
|
metadata: {}
|
|
113
|
-
post_install_message:
|
|
114
135
|
rdoc_options: []
|
|
115
136
|
require_paths:
|
|
116
137
|
- lib
|
|
@@ -125,10 +146,25 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
146
|
- !ruby/object:Gem::Version
|
|
126
147
|
version: '0'
|
|
127
148
|
requirements: []
|
|
128
|
-
|
|
129
|
-
rubygems_version: 2.2.2
|
|
130
|
-
signing_key:
|
|
149
|
+
rubygems_version: 4.0.3
|
|
131
150
|
specification_version: 4
|
|
132
|
-
summary: Drawing module for state
|
|
133
|
-
test_files:
|
|
134
|
-
|
|
151
|
+
summary: Drawing module for state machines
|
|
152
|
+
test_files:
|
|
153
|
+
- test/files/switch.rb
|
|
154
|
+
- test/graph_default_test.rb
|
|
155
|
+
- test/graph_edges_test.rb
|
|
156
|
+
- test/graph_nodes_test.rb
|
|
157
|
+
- test/graph_output_test.rb
|
|
158
|
+
- test/machine_class_drawing_test.rb
|
|
159
|
+
- test/machine_drawing_test.rb
|
|
160
|
+
- test/machine_drawing_with_dynamic_states_test.rb
|
|
161
|
+
- test/machine_drawing_with_integer_states_test.rb
|
|
162
|
+
- test/machine_drawing_with_nil_states_test.rb
|
|
163
|
+
- test/state_drawing_final_test.rb
|
|
164
|
+
- test/state_drawing_initial_test.rb
|
|
165
|
+
- test/state_drawing_lambda_value_test.rb
|
|
166
|
+
- test/state_drawing_nil_name_test.rb
|
|
167
|
+
- test/state_drawing_non_final_test.rb
|
|
168
|
+
- test/state_drawing_test.rb
|
|
169
|
+
- test/state_drawing_with_human_name_test.rb
|
|
170
|
+
- test/test_helper.rb
|
data/.gitignore
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
*.gem
|
|
2
|
-
*.rbc
|
|
3
|
-
.bundle
|
|
4
|
-
.config
|
|
5
|
-
.yardoc
|
|
6
|
-
Gemfile.lock
|
|
7
|
-
InstalledFiles
|
|
8
|
-
_yardoc
|
|
9
|
-
coverage
|
|
10
|
-
doc/
|
|
11
|
-
lib/bundler/man
|
|
12
|
-
pkg
|
|
13
|
-
rdoc
|
|
14
|
-
spec/reports
|
|
15
|
-
test/tmp
|
|
16
|
-
test/version_tmp
|
|
17
|
-
tmp
|
|
18
|
-
*.bundle
|
|
19
|
-
*.so
|
|
20
|
-
*.o
|
|
21
|
-
*.a
|
|
22
|
-
mkmf.log
|
data/.rspec
DELETED
data/.travis.yaml
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
#TODO register graphviz as render engine
|
|
2
|
-
module StateMachines
|
|
3
|
-
class Machine
|
|
4
|
-
class << self
|
|
5
|
-
# Draws the state machines defined in the given classes using GraphViz.
|
|
6
|
-
# The given classes must be a comma-delimited string of class names.
|
|
7
|
-
#
|
|
8
|
-
# Configuration options:
|
|
9
|
-
# * <tt>:file</tt> - A comma-delimited string of files to load that
|
|
10
|
-
# contain the state machine definitions to draw
|
|
11
|
-
# * <tt>:path</tt> - The path to write the graph file to
|
|
12
|
-
# * <tt>:format</tt> - The image format to generate the graph in
|
|
13
|
-
# * <tt>:font</tt> - The name of the font to draw state names in
|
|
14
|
-
def draw(class_names, options = {})
|
|
15
|
-
raise ArgumentError, 'At least one class must be specified' unless class_names && class_names.split(',').any?
|
|
16
|
-
|
|
17
|
-
# Load any files
|
|
18
|
-
if files = options.delete(:file)
|
|
19
|
-
files.split(',').each { |file| require file }
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
class_names.split(',').each do |class_name|
|
|
23
|
-
# Navigate through the namespace structure to get to the class
|
|
24
|
-
klass = Object
|
|
25
|
-
class_name.split('::').each do |name|
|
|
26
|
-
klass = klass.const_defined?(name) ? klass.const_get(name) : klass.const_missing(name)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Draw each of the class's state machines
|
|
30
|
-
klass.state_machines.each_value do |machine|
|
|
31
|
-
machine.draw(options)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Draws a directed graph of the machine for visualizing the various events,
|
|
38
|
-
# states, and their transitions.
|
|
39
|
-
#
|
|
40
|
-
# This requires both the Ruby graphviz gem and the graphviz library be
|
|
41
|
-
# installed on the system.
|
|
42
|
-
#
|
|
43
|
-
# Configuration options:
|
|
44
|
-
# * <tt>:name</tt> - The name of the file to write to (without the file extension).
|
|
45
|
-
# Default is "#{owner_class.name}_#{name}"
|
|
46
|
-
# * <tt>:path</tt> - The path to write the graph file to. Default is the
|
|
47
|
-
# current directory (".").
|
|
48
|
-
# * <tt>:format</tt> - The image format to generate the graph in.
|
|
49
|
-
# Default is "png'.
|
|
50
|
-
# * <tt>:font</tt> - The name of the font to draw state names in.
|
|
51
|
-
# Default is "Arial".
|
|
52
|
-
# * <tt>:orientation</tt> - The direction of the graph ("portrait" or
|
|
53
|
-
# "landscape"). Default is "portrait".
|
|
54
|
-
# * <tt>:human_names</tt> - Whether to use human state / event names for
|
|
55
|
-
# node labels on the graph instead of the internal name. Default is false.
|
|
56
|
-
def draw(graph_options = {})
|
|
57
|
-
name = graph_options.delete(:name) || "#{owner_class.name}_#{self.name}"
|
|
58
|
-
draw_options = {:human_name => false}
|
|
59
|
-
draw_options[:human_name] = graph_options.delete(:human_names) if graph_options.include?(:human_names)
|
|
60
|
-
|
|
61
|
-
graph = Graph.new(name, graph_options)
|
|
62
|
-
|
|
63
|
-
# Add nodes / edges
|
|
64
|
-
states.by_priority.each { |state| state.draw(graph, draw_options) }
|
|
65
|
-
events.each { |event| event.draw(graph, draw_options) }
|
|
66
|
-
|
|
67
|
-
# Output result
|
|
68
|
-
graph.output
|
|
69
|
-
graph
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
class State
|
|
74
|
-
# Draws a representation of this state on the given machine. This will
|
|
75
|
-
# create a new node on the graph with the following properties:
|
|
76
|
-
# * +label+ - The human-friendly description of the state.
|
|
77
|
-
# * +width+ - The width of the node. Always 1.
|
|
78
|
-
# * +height+ - The height of the node. Always 1.
|
|
79
|
-
# * +shape+ - The actual shape of the node. If the state is a final
|
|
80
|
-
# state, then "doublecircle", otherwise "ellipse".
|
|
81
|
-
#
|
|
82
|
-
# Configuration options:
|
|
83
|
-
# * <tt>:human_name</tt> - Whether to use the state's human name for the
|
|
84
|
-
# node's label that gets drawn on the graph
|
|
85
|
-
def draw(graph, options = {})
|
|
86
|
-
node = graph.add_nodes(name ? name.to_s : 'nil',
|
|
87
|
-
:label => description(options),
|
|
88
|
-
:width => '1',
|
|
89
|
-
:height => '1',
|
|
90
|
-
:shape => final? ? 'doublecircle' : 'ellipse'
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
# Add open arrow for initial state
|
|
94
|
-
graph.add_edges(graph.add_nodes('starting_state', :shape => 'point'), node) if initial?
|
|
95
|
-
|
|
96
|
-
true
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
class Event
|
|
101
|
-
# Draws a representation of this event on the given graph. This will
|
|
102
|
-
# create 1 or more edges on the graph for each branch (i.e. transition)
|
|
103
|
-
# configured.
|
|
104
|
-
#
|
|
105
|
-
# Configuration options:
|
|
106
|
-
# * <tt>:human_name</tt> - Whether to use the event's human name for the
|
|
107
|
-
# node's label that gets drawn on the graph
|
|
108
|
-
def draw(graph, options = {})
|
|
109
|
-
valid_states = machine.states.by_priority.map {|state| state.name}
|
|
110
|
-
branches.each do |branch|
|
|
111
|
-
branch.draw(graph, options[:human_name] ? human_name : name, valid_states)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
true
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
class Branch
|
|
119
|
-
# Draws a representation of this branch on the given graph. This will draw
|
|
120
|
-
# an edge between every state this branch matches *from* to either the
|
|
121
|
-
# configured to state or, if none specified, then a loopback to the from
|
|
122
|
-
# state.
|
|
123
|
-
#
|
|
124
|
-
# For example, if the following from states are configured:
|
|
125
|
-
# * +idling+
|
|
126
|
-
# * +first_gear+
|
|
127
|
-
# * +backing_up+
|
|
128
|
-
#
|
|
129
|
-
# ...and the to state is +parked+, then the following edges will be created:
|
|
130
|
-
# * +idling+ -> +parked+
|
|
131
|
-
# * +first_gear+ -> +parked+
|
|
132
|
-
# * +backing_up+ -> +parked+
|
|
133
|
-
#
|
|
134
|
-
# Each edge will be labeled with the name of the event that would cause the
|
|
135
|
-
# transition.
|
|
136
|
-
def draw(graph, event, valid_states)
|
|
137
|
-
state_requirements.each do |state_requirement|
|
|
138
|
-
# From states determined based on the known valid states
|
|
139
|
-
from_states = state_requirement[:from].filter(valid_states)
|
|
140
|
-
|
|
141
|
-
# If a to state is not specified, then it's a loopback and each from
|
|
142
|
-
# state maps back to itself
|
|
143
|
-
if state_requirement[:to].values.empty?
|
|
144
|
-
loopback = true
|
|
145
|
-
else
|
|
146
|
-
to_state = state_requirement[:to].values.first
|
|
147
|
-
to_state = to_state ? to_state.to_s : 'nil'
|
|
148
|
-
loopback = false
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Generate an edge between each from and to state
|
|
152
|
-
from_states.each do |from_state|
|
|
153
|
-
from_state = from_state ? from_state.to_s : 'nil'
|
|
154
|
-
graph.add_edges(from_state, loopback ? from_state : to_state, :label => event.to_s)
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
true
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
end
|
data/spec/graph_spec.rb
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
describe StateMachines::Graph do
|
|
3
|
-
context 'GraphDefault' do
|
|
4
|
-
before(:each) do
|
|
5
|
-
@graph = StateMachines::Graph.new('test')
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
it 'should_have_a_default_font' do
|
|
9
|
-
expect('Arial').to eq(@graph.font)
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
it 'should_use_current_directory_for_filepath' do
|
|
13
|
-
expect('doc/state_machines/test.png').to eq(@graph.file_path)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
it 'should_have_a_default_file_format' do
|
|
17
|
-
expect('png').to eq(@graph.file_format)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it 'should_have_a_default_orientation' do
|
|
21
|
-
expect('TB').to eq(@graph[:rankdir].source)
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
context 'GraphNodes' do
|
|
26
|
-
before(:each) do
|
|
27
|
-
@graph = StateMachines::Graph.new('test')
|
|
28
|
-
@node = @graph.add_nodes('parked', :shape => 'ellipse')
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
it 'should_return_generated_node' do
|
|
32
|
-
expect(@node).to_not be_nil
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it 'should_use_specified_name' do
|
|
36
|
-
expect(@node).to eq(@graph.get_node('parked'))
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'should_use_specified_options' do
|
|
40
|
-
expect('ellipse').to eq(@node['shape'].to_s.gsub('"', ''))
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'should_set_default_font' do
|
|
44
|
-
expect('Arial').to eq(@node['fontname'].to_s.gsub('"', ''))
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
context 'GraphEdges' do
|
|
49
|
-
before(:each) do
|
|
50
|
-
@graph = StateMachines::Graph.new('test')
|
|
51
|
-
@graph.add_nodes('parked', :shape => 'ellipse')
|
|
52
|
-
@graph.add_nodes('idling', :shape => 'ellipse')
|
|
53
|
-
@edge = @graph.add_edges('parked', 'idling', :label => 'ignite')
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it 'should_return_generated_edge' do
|
|
57
|
-
expect(@edge).to_not be_nil
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
it 'should_use_specified_nodes' do
|
|
61
|
-
expect('parked').to eq(@edge.node_one(false))
|
|
62
|
-
expect('idling').to eq(@edge.node_two(false))
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
it 'should_use_specified_options' do
|
|
66
|
-
expect('ignite').to eq(@edge['label'].to_s.gsub('"', ''))
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'should_set_default_font' do
|
|
70
|
-
expect('Arial').to eq(@edge['fontname'].to_s.gsub('"', ''))
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
context 'GraphOutput' do
|
|
75
|
-
before(:each) do
|
|
76
|
-
@graph_name = "test_#{rand(1000000)}"
|
|
77
|
-
@graph = StateMachines::Graph.new(@graph_name)
|
|
78
|
-
@graph.add_nodes('parked', :shape => 'ellipse')
|
|
79
|
-
@graph.add_nodes('idling', :shape => 'ellipse')
|
|
80
|
-
@graph.add_edges('parked', 'idling', :label => 'ignite')
|
|
81
|
-
@graph.output
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
it 'should_save_file' do
|
|
85
|
-
expect(File.exist?("doc/state_machines/#{@graph_name}.png")).to be_truthy
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
after(:each) do
|
|
89
|
-
FileUtils.rm Dir["doc/state_machines/#{@graph_name}.png"]
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|