ellington 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -43,6 +43,10 @@ module Ellington
43
43
  @state_history ||= []
44
44
  end
45
45
 
46
+ def state_history_includes?(*states)
47
+ (state_history & states).length == states.length
48
+ end
49
+
46
50
  def transition_to(new_state)
47
51
  if !locked?
48
52
  message = "Cannot transition an unlocked #{self.class.name}'s state"
@@ -40,10 +40,11 @@ module Ellington
40
40
  (@subclasses ||= []) << subclass
41
41
  end
42
42
 
43
- def generate_graphs(dir)
43
+ def generate_graphs(dir, options={})
44
+ options[:format] ||= :svg
44
45
  FileUtils.mkdir_p(dir)
45
46
  @subclasses.each do |subclass|
46
- Ellington::Visualizer.new(subclass.new, dir).graph
47
+ Ellington::Visualizer.new(subclass.new, dir, options[:format]).graph_all
47
48
  end
48
49
  end
49
50
 
@@ -122,6 +123,7 @@ module Ellington
122
123
  log route_info
123
124
  end
124
125
  Ellington.logger.info "\n" if Ellington.logger
126
+ binding.pry
125
127
  end
126
128
 
127
129
  required_connections.each do |connection|
@@ -1,3 +1,3 @@
1
1
  module Ellington
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -28,76 +28,122 @@ module Ellington
28
28
  end
29
29
  end
30
30
 
31
- attr_reader :route, :dir
31
+ attr_reader :route, :dir, :format
32
32
 
33
- def initialize(route, dir)
33
+ def initialize(route, dir, format=:svg)
34
34
  @route = route
35
35
  @dir = dir
36
+ @format = format
36
37
  end
37
38
 
38
- def graph
39
- graph_route_basic
40
- graph_route
41
- graph_lines_basic
42
- graph_lines
39
+ FONTNAME = "Helvetica"
40
+ RANKSEP = 0.4
41
+ NODE_COLOR = "white"
42
+ NODE_COLOR_VIRTUAL = "gray50"
43
+ NODE_COLOR_LINE_GOAL = "green2"
44
+ NODE_COLOR_ROUTE_GOAL = "gold"
45
+ NODE_COLOR_PASSENGER_HIT = "royalblue1"
46
+ NODE_FILLCOLOR = "white"
47
+ NODE_FILLCOLOR_LINE_GOAL = "green2"
48
+ NODE_FILLCOLOR_ROUTE_GOAL = "gold"
49
+ NODE_FONTCOLOR_VIRTUAL = "gray40"
50
+ NODE_SHAPE = "box"
51
+ NODE_STYLE = "filled,rounded"
52
+ NODE_STYLE_VIRTUAL = "rounded"
53
+ NODE_PENWIDTH_PASSENGER_HIT = 2
54
+ EDGE_PENWIDTH_PASSENGER_HIT = 2
55
+ EDGE_COLOR_PASSENGER_HIT = "royalblue1"
56
+ EDGE_STYLE_PASSENGER_MISS = "dotted"
57
+ CLUSTER_STYLE = "filled"
58
+ CLUSTER_COLOR = "gray70"
59
+ CLUSTER_FILLCOLOR = "gray70"
60
+ CLUSTER_PENCOLOR = "gray50"
61
+
62
+ def graph_all(passenger=nil)
63
+ graph_route_basic passenger
64
+ graph_route passenger
65
+ graph_lines_basic passenger
66
+ graph_lines passenger
43
67
  end
44
68
 
45
- def graph_lines_basic
46
- g = Node.new(nil, GraphViz.new("G"))
69
+ def graph_lines_basic(passenger=nil)
70
+ g = Node.new(nil, GraphViz.new("GraphLinesBasic"))
71
+ set_graph_defaults g.viz
47
72
  g.viz["label"] = "#{route.name} Lines - basic"
48
- g.viz["ranksep"] = 0.4
49
- g.viz["fontname"] = "Helvetica"
50
- g.viz.node["fontname"] = "Helvetica"
51
- g.viz.node["shape"] = "box"
52
- g.viz.node["style"] = "rounded"
53
73
 
54
74
  route.lines.each_with_index do |line, index|
55
75
  line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
76
+ set_cluster_defaults line_cluster.viz
56
77
  line_cluster.viz["label"] = line.class.name
57
- line_cluster.viz["style"] = "filled"
58
78
  g << line_cluster
59
79
 
60
80
  line.stations.each do |station|
61
81
  station_node = Node.new(station, line_cluster.viz.add_nodes(station.class.name))
62
- station_node.viz["style"] = "filled,rounded"
63
- station_node.viz["color"] = "white"
64
82
  line_cluster << station_node
83
+
84
+ if !(line.goal & station.states.keys).empty?
85
+ station_node.viz["color"] = NODE_COLOR_LINE_GOAL
86
+ station_node.viz["fillcolor"] = NODE_COLOR_LINE_GOAL
87
+ end
88
+
89
+ if !(route.goal & station.states.keys).empty?
90
+ station_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
91
+ station_node.viz["fillcolor"] = NODE_COLOR_ROUTE_GOAL
92
+ end
93
+
94
+ if passenger && !(passenger.state_history & station.states.keys).empty?
95
+ station_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
96
+ station_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
97
+ end
65
98
  end
66
99
 
67
100
  line_cluster.each_with_index.each do |node, node_index|
68
101
  next_node = line_cluster[node_index + 1]
69
- line_cluster.viz.add_edges node.viz, next_node.viz if next_node
102
+ if next_node
103
+ station = node.base
104
+ next_station = next_node.base
105
+ edge = line_cluster.viz.add_edges(node.viz, next_node.viz)
106
+ if passenger
107
+ if color_name(node.viz["color"]) == NODE_COLOR_PASSENGER_HIT &&
108
+ color_name(next_node.viz["color"]) == NODE_COLOR_PASSENGER_HIT
109
+ edge["color"] = EDGE_COLOR_PASSENGER_HIT
110
+ edge["penwidth"] = EDGE_PENWIDTH_PASSENGER_HIT
111
+ else
112
+ edge["style"] = EDGE_STYLE_PASSENGER_MISS
113
+ end
114
+ end
115
+ end
70
116
  end
71
117
  end
72
118
 
73
- file_name = "#{route.name.downcase.gsub("::", "_")}-lines-basic.pdf"
74
- g.viz.output(:pdf => File.join(dir, file_name))
119
+ file_name = "#{route.name.downcase.gsub("::", "_")}-lines-basic.#{format}"
120
+ g.viz.output(format => File.join(dir, file_name))
75
121
  end
76
122
 
77
- def graph_lines
78
- g = Node.new(nil, GraphViz.new("G"))
123
+ def graph_lines(passenger=nil)
124
+ g = Node.new(nil, GraphViz.new("GraphLines"))
125
+ set_graph_defaults g.viz
79
126
  g.viz["label"] = "#{route.name} Lines"
80
- g.viz["ranksep"] = 0.4
81
- g.viz["fontname"] = "Helvetica"
82
- g.viz.node["fontname"] = "Helvetica"
83
- g.viz.node["shape"] = "box"
84
- g.viz.node["style"] = "rounded"
85
127
 
86
128
  route.lines.each_with_index do |line, index|
87
129
  line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
130
+ set_cluster_defaults line_cluster.viz
88
131
  line_cluster.viz["label"] = line.class.name
89
- line_cluster.viz["style"] = "filled"
90
132
  g << line_cluster
91
133
 
92
134
  line.states.keys.each do |state|
93
135
  state_node = Node.new(state, line_cluster.viz.add_nodes(state))
94
- state_node.viz["style"] = "filled,rounded"
95
- state_node.viz["color"] = "white"
96
136
  if line.goal.include?(state)
97
- state_node.viz["color"] = "green2"
137
+ state_node.viz["color"] = NODE_COLOR_LINE_GOAL
138
+ state_node.viz["fillcolor"] = NODE_FILLCOLOR_LINE_GOAL
98
139
  end
99
140
  if route.goal.include?(state)
100
- state_node.viz["color"] = "gold"
141
+ state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
142
+ state_node.viz["fillcolor"] = NODE_FILLCOLOR_ROUTE_GOAL
143
+ end
144
+ if passenger && passenger.state_history_includes?(state)
145
+ state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
146
+ state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
101
147
  end
102
148
  line_cluster << state_node
103
149
  end
@@ -106,45 +152,64 @@ module Ellington
106
152
  a = line_cluster.find(state)
107
153
  (transitions || []).each do |transition|
108
154
  b = line_cluster.find(transition)
109
- line_cluster.viz.add_edges a.viz, b.viz
155
+ edge = line_cluster.viz.add_edges(a.viz, b.viz)
156
+ if passenger
157
+ if passenger.state_history_includes?(state, transition)
158
+ edge["color"] = EDGE_COLOR_PASSENGER_HIT
159
+ edge["penwidth"] = EDGE_PENWIDTH_PASSENGER_HIT
160
+ else
161
+ edge["style"] = EDGE_STYLE_PASSENGER_MISS
162
+ end
163
+ end
110
164
  end
111
165
  end
112
166
  end
113
167
 
114
- file_name = "#{route.name.downcase.gsub("::", "_")}-lines.pdf"
115
- g.viz.output(:pdf => File.join(dir, file_name))
168
+ file_name = "#{route.name.downcase.gsub("::", "_")}-lines.#{format}"
169
+ g.viz.output(format => File.join(dir, file_name))
116
170
  end
117
171
 
118
- def graph_route_basic
119
- g = Node.new(nil, GraphViz.new("G"))
172
+ def graph_route_basic(passenger=nil)
173
+ g = Node.new(nil, GraphViz.new("GraphRouteBasic"))
174
+ set_graph_defaults g.viz
175
+ g.viz["ranksep"] = 1
120
176
  g.viz["label"] = "#{route.name} Route - basic"
121
- g.viz["compound"] = true
122
- g.viz["ranksep"] = 1.2
123
- g.viz["fontname"] = "Helvetica"
124
- g.viz.node["fontname"] = "Helvetica"
125
- g.viz.node["shape"] = "box"
126
- g.viz.node["style"] = "rounded"
127
177
 
128
178
  route.lines.each_with_index do |line, index|
129
179
  line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
180
+ set_cluster_defaults line_cluster.viz
130
181
  line_cluster.viz["label"] = line.class.name
131
- line_cluster.viz["style"] = "filled"
132
182
  g << line_cluster
133
183
 
184
+ passenger_hit = false
134
185
  %w{PASS FAIL ERROR}.each do |state|
135
186
  state_node = Node.new(state, line_cluster.viz.add_nodes("#{line.class.name}#{state}", "label" => state))
136
- state_node.viz["style"] = "filled,rounded"
137
- state_node.viz["color"] = "white"
138
- if !(route.goal & line.stations.map{ |s| "#{state} #{s.name}" }).empty?
139
- state_node.viz["color"] = "gold"
140
- end
141
187
  line_cluster << state_node
188
+ states = line.stations.map{ |s| "#{state} #{s.name}" }
189
+
190
+ if !(line.goal & states).empty?
191
+ state_node.viz["color"] = NODE_COLOR_LINE_GOAL
192
+ state_node.viz["fillcolor"] = NODE_COLOR_LINE_GOAL
193
+ end
194
+
195
+ if !(route.goal & states).empty?
196
+ state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
197
+ state_node.viz["fillcolor"] = NODE_COLOR_ROUTE_GOAL
198
+ end
199
+
200
+ if passenger
201
+ if !passenger_hit && !(passenger.state_history & line.send("#{state.downcase}ed")).empty?
202
+ passenger_hit = true
203
+ state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
204
+ state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
205
+ end
206
+ end
142
207
  end
143
208
  end
144
209
 
145
210
  route.connections.each do |connection|
146
211
  to_node = g.find(connection.line)
147
- from_line = to_node.base
212
+ to_line = to_node.base
148
213
 
149
214
  combos = {}
150
215
  g.to_a.each do |node|
@@ -157,10 +222,10 @@ module Ellington
157
222
  if connection.type == :if_any
158
223
  combos.each do |state, nodes|
159
224
  nodes.each do |node|
160
- to_line = node.base
225
+ from_line = node.base
161
226
  g.viz.add_edges(
162
- node.viz.get_node("#{to_line.class.name}#{state}"),
163
- to_node.viz.get_node("#{from_line.class.name}PASS"),
227
+ node.viz.get_node("#{from_line.class.name}#{state}"),
228
+ to_node.viz.get_node("#{to_line.class.name}PASS"),
164
229
  "lhead" => to_node.viz.id
165
230
  )
166
231
  end
@@ -172,9 +237,9 @@ module Ellington
172
237
  node_name = nodes.map{ |n| n.base.class.name }.join + state
173
238
  node_label = nodes.map{ |n| "#{n.base.class.name} #{state}"}.join("\n")
174
239
  viz = g.viz.add_nodes(node_name, "label" => node_label)
175
- viz["style"] = "rounded"
176
- viz["color"] = "gray50"
177
- viz["fontcolor"] = "gray40"
240
+ viz["style"] = NODE_STYLE_VIRTUAL
241
+ viz["color"] = NODE_COLOR_VIRTUAL
242
+ viz["fontcolor"] = NODE_FONTCOLOR_VIRTUAL
178
243
 
179
244
  nodes.each do |node|
180
245
  from_viz = node.viz.get_node("#{node.base.class.name}#{state}")
@@ -190,41 +255,62 @@ module Ellington
190
255
  end
191
256
  end
192
257
 
193
- file_name = "#{route.name.downcase.gsub("::", "_")}-route-basic.pdf"
194
- g.viz.output(:pdf => File.join(dir, file_name))
258
+ file_name = "#{route.name.downcase.gsub("::", "_")}-route-basic.#{format}"
259
+ g.viz.output(format => File.join(dir, file_name))
195
260
  end
196
261
 
197
- def graph_route
198
- g = Node.new(nil, GraphViz.new("G"))
262
+ def graph_route(passenger=nil)
263
+ g = Node.new(nil, GraphViz.new("GraphRoute"))
264
+ set_graph_defaults g.viz
199
265
  g.viz["label"] = "#{route.name} Lines"
200
- g.viz["compound"] = true
201
266
  g.viz["ranksep"] = 0.8
202
- g.viz["fontname"] = "Helvetica"
203
- g.viz.node["fontname"] = "Helvetica"
204
- g.viz.node["shape"] = "box"
205
- g.viz.node["style"] = "rounded"
206
267
 
207
268
  route.lines.each_with_index do |line, index|
208
269
  line_cluster = Node.new(line, g.viz.add_graph("cluster#{index}"))
270
+ set_cluster_defaults line_cluster.viz
209
271
  line_cluster.viz["label"] = line.class.name
210
- line_cluster.viz["style"] = "filled"
211
272
  g << line_cluster
212
273
 
213
274
  line.states.keys.each do |state|
214
275
  state_node = Node.new(state, line_cluster.viz.add_nodes(state))
215
- state_node.viz["style"] = "filled,rounded"
216
- state_node.viz["color"] = "white"
217
276
  if line.goal.include?(state)
218
- state_node.viz["color"] = "green2"
277
+ state_node.viz["color"] = NODE_COLOR_LINE_GOAL
278
+ state_node.viz["fillcolor"] = NODE_FILLCOLOR_LINE_GOAL
219
279
  end
220
280
  if route.goal.include?(state)
221
- state_node.viz["color"] = "gold"
281
+ state_node.viz["color"] = NODE_COLOR_ROUTE_GOAL
282
+ state_node.viz["fillcolor"] = NODE_FILLCOLOR_ROUTE_GOAL
283
+ end
284
+ if passenger && passenger.state_history_includes?(state)
285
+ state_node.viz["color"] = NODE_COLOR_PASSENGER_HIT
286
+ state_node.viz["penwidth"] = NODE_PENWIDTH_PASSENGER_HIT
222
287
  end
223
288
  line_cluster << state_node
224
289
  end
225
290
  end
226
291
 
227
292
  viz = g.viz.add_nodes(route.initial_state)
293
+ rendered_edges = {}
294
+
295
+ if passenger
296
+ passenger_nodes = g.reduce([]) do |memo, line_cluster|
297
+ line_cluster.children.each do |node|
298
+ if node.viz["color"].to_s.gsub(/\W/, "") == NODE_COLOR_PASSENGER_HIT
299
+ memo << node
300
+ end
301
+ end
302
+ memo
303
+ end
304
+ previous_node = nil
305
+ passenger_nodes.each do |node|
306
+ from_viz = previous_node.nil? ? viz : previous_node.viz
307
+ rendered_edges[from_viz.id + node.viz.id] = true
308
+ edge = g.viz.add_edges(from_viz, node.viz)
309
+ edge["color"] = EDGE_COLOR_PASSENGER_HIT
310
+ edge["penwidth"] = EDGE_PENWIDTH_PASSENGER_HIT
311
+ previous_node = node
312
+ end
313
+ end
228
314
 
229
315
  route.states.each do |from_state, to_states|
230
316
  (to_states || []).each do |to_state|
@@ -237,17 +323,43 @@ module Ellington
237
323
  to_viz = to_node.viz.get_node(to_state) if to_node
238
324
  to_viz ||= g.viz.get_node(to_state)
239
325
 
240
- if from_viz && to_viz
241
- g.viz.add_edges(
326
+ if from_viz && to_viz && !rendered_edges[from_viz.id + to_viz.id]
327
+ rendered_edges[from_viz.id + to_viz.id] = true
328
+ edge = g.viz.add_edges(
242
329
  from_viz,
243
330
  to_viz
244
331
  )
332
+ edge["style"] = EDGE_STYLE_PASSENGER_MISS if passenger
245
333
  end
246
334
  end
247
335
  end
248
336
 
249
- file_name = "#{route.name.downcase.gsub("::", "_")}-route.pdf"
250
- g.viz.output(:pdf => File.join(dir, file_name))
337
+ file_name = "#{route.name.downcase.gsub("::", "_")}-route.#{format}"
338
+ g.viz.output(format => File.join(dir, file_name))
339
+ end
340
+
341
+ private
342
+
343
+ def color_name(graphviz_color)
344
+ graphviz_color.to_s.gsub("\"", "")
345
+ end
346
+
347
+ def set_graph_defaults(graph)
348
+ graph["compound"] = true
349
+ graph["ranksep"] = RANKSEP
350
+ graph["fontname"] = FONTNAME
351
+ graph.node["fontname"] = FONTNAME
352
+ graph.node["shape"] = NODE_SHAPE
353
+ graph.node["style"] = NODE_STYLE
354
+ graph.node["color"] = NODE_COLOR
355
+ graph.node["fillcolor"] = NODE_FILLCOLOR
356
+ end
357
+
358
+ def set_cluster_defaults(cluster)
359
+ cluster["style"] = CLUSTER_STYLE
360
+ cluster["color"] = CLUSTER_COLOR
361
+ cluster["fillcolor"] = CLUSTER_FILLCOLOR
362
+ cluster["pencolor"] = CLUSTER_PENCOLOR
251
363
  end
252
364
 
253
365
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ellington
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-28 00:00:00.000000000 Z
12
+ date: 2013-03-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hero
@@ -90,7 +90,6 @@ files:
90
90
  - lib/ellington/version.rb
91
91
  - lib/ellington/visualizer.rb
92
92
  - lib/ellington.rb
93
- - Blast.pdf
94
93
  - Gemfile
95
94
  - Gemfile.lock
96
95
  - LICENSE.txt
@@ -134,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
133
  version: '0'
135
134
  requirements: []
136
135
  rubyforge_project:
137
- rubygems_version: 1.8.23
136
+ rubygems_version: 1.8.25
138
137
  signing_key:
139
138
  specification_version: 3
140
139
  summary: A micro framework to ensure your projects are easy to manage, develop, &
@@ -157,4 +156,3 @@ test_files:
157
156
  - test/ticket_test.rb
158
157
  - test/transition_info_test.rb
159
158
  - test/unique_type_array_test.rb
160
- has_rdoc:
data/Blast.pdf DELETED
Binary file