dogviz 0.0.17 → 0.0.18
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 +4 -4
- data/lib/dogviz/colorizer.rb +25 -0
- data/lib/dogviz/common.rb +89 -0
- data/lib/dogviz/container.rb +88 -0
- data/lib/dogviz/duplicate_lookup_error.rb +7 -0
- data/lib/dogviz/flow.rb +67 -0
- data/lib/dogviz/flowable.rb +9 -0
- data/lib/dogviz/graphviz_renderer.rb +60 -0
- data/lib/dogviz/logical_container.rb +8 -0
- data/lib/dogviz/lookup_error.rb +7 -0
- data/lib/dogviz/missing_match_block_error.rb +7 -0
- data/lib/dogviz/nominator.rb +18 -0
- data/lib/dogviz/parent.rb +38 -0
- data/lib/dogviz/process.rb +18 -0
- data/lib/dogviz/registry.rb +39 -0
- data/lib/dogviz/rendered_sequence.rb +13 -0
- data/lib/dogviz/sequence_renderer.rb +47 -0
- data/lib/dogviz/sigma_graph_hash.rb +25 -0
- data/lib/dogviz/sigma_renderer.rb +32 -0
- data/lib/dogviz/system.rb +95 -0
- data/lib/dogviz/thing.rb +135 -0
- data/lib/dogviz/version.rb +1 -1
- data/lib/dogviz.rb +7 -687
- data/tests/test_dogviz_sigma_rendering.rb +68 -0
- data/todo.txt +7 -0
- metadata +24 -2
data/lib/dogviz.rb
CHANGED
@@ -1,689 +1,9 @@
|
|
1
|
-
|
1
|
+
require_relative 'dogviz/thing.rb'
|
2
|
+
require_relative 'dogviz/container.rb'
|
3
|
+
require_relative 'dogviz/logical_container.rb'
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(processor, description)
|
6
|
-
@processor = processor
|
7
|
-
@description = description
|
8
|
-
end
|
9
|
-
def name
|
10
|
-
@processor.name
|
11
|
-
end
|
12
|
-
def description
|
13
|
-
@description
|
14
|
-
end
|
15
|
-
attr_reader :processor
|
16
|
-
end
|
17
|
-
module Flowable
|
18
|
-
def does(action)
|
19
|
-
Process.new(self, action)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
module Nominator
|
23
|
-
def nominate(names_to_nominees)
|
24
|
-
names_to_nominees.each {|name, nominee|
|
25
|
-
self.class.send(:define_method, name) do
|
26
|
-
nominee
|
27
|
-
end
|
28
|
-
}
|
29
|
-
end
|
30
|
-
def nominate_from(nominee_nominator, *nominee_names)
|
31
|
-
nominee_names.each {|name|
|
32
|
-
accessor_sym = name.to_s.to_sym
|
33
|
-
nominate accessor_sym => nominee_nominator.send(accessor_sym)
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
module Common
|
38
|
-
def create_id(name, parent)
|
39
|
-
parts = []
|
40
|
-
parts << parent.id if parent.respond_to? :id
|
41
|
-
parts += name.split /\s/
|
42
|
-
parts.join '_'
|
43
|
-
end
|
44
|
-
def root
|
45
|
-
ancestors.last
|
46
|
-
end
|
47
|
-
def ancestors
|
48
|
-
ancestors = [parent]
|
49
|
-
loop do
|
50
|
-
break unless ancestors.last.respond_to?(:parent)
|
51
|
-
ancestors << ancestors.last.parent
|
52
|
-
end
|
53
|
-
ancestors
|
54
|
-
end
|
55
|
-
def info(fields)
|
56
|
-
@info.merge! fields
|
57
|
-
setup_render_attributes(label: label_with_info)
|
58
|
-
end
|
59
|
-
def doclink(url)
|
60
|
-
setup_render_attributes(URL: url)
|
61
|
-
end
|
62
|
-
def label_with_info
|
63
|
-
lines = [ name ]
|
64
|
-
@info.each {|k, v|
|
65
|
-
lines << "#{k}: #{v}"
|
66
|
-
}
|
67
|
-
lines.join "\n"
|
68
|
-
end
|
69
|
-
def setup_render_attributes(attributes)
|
70
|
-
@attributes = {} if @attributes.nil?
|
71
|
-
@attributes.merge!(attributes)
|
72
|
-
end
|
73
|
-
def rollup?
|
74
|
-
@rollup
|
75
|
-
end
|
76
|
-
def rollup!
|
77
|
-
@rollup = true
|
78
|
-
self
|
79
|
-
end
|
80
|
-
def skip!
|
81
|
-
@skip = true
|
82
|
-
self
|
83
|
-
end
|
5
|
+
require_relative 'dogviz/graphviz_renderer.rb'
|
6
|
+
require_relative 'dogviz/sigma_renderer.rb'
|
84
7
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
def in_skip?
|
90
|
-
skip? || under_skip?
|
91
|
-
end
|
92
|
-
|
93
|
-
def under_skip?
|
94
|
-
ancestors.any? &:skip?
|
95
|
-
end
|
96
|
-
|
97
|
-
def under_rollup?
|
98
|
-
ancestors.any? &:rollup?
|
99
|
-
end
|
100
|
-
def in_rollup?
|
101
|
-
rollup? || under_rollup?
|
102
|
-
end
|
103
|
-
def on_top_rollup?
|
104
|
-
rollup? && !under_rollup?
|
105
|
-
end
|
106
|
-
def inherited_render_options
|
107
|
-
inherited = {}
|
108
|
-
inherited[:fontname] = parent.render_options[:fontname] if parent.render_options.include?(:fontname)
|
109
|
-
inherited
|
110
|
-
end
|
111
|
-
end
|
112
|
-
module Parent
|
113
|
-
def find_all(&matcher)
|
114
|
-
raise MissingMatchBlockError.new unless block_given?
|
115
|
-
@by_name.find_all &matcher
|
116
|
-
end
|
117
|
-
def find(name=nil, &matcher)
|
118
|
-
if block_given?
|
119
|
-
@by_name.find &matcher
|
120
|
-
else
|
121
|
-
raise 'Need to provide name or block' if name.nil?
|
122
|
-
@by_name.lookup name
|
123
|
-
end
|
124
|
-
end
|
125
|
-
def thing(name, options={})
|
126
|
-
add Thing.new self, name, options
|
127
|
-
end
|
128
|
-
def container(name, options={})
|
129
|
-
add Container.new self, name, options
|
130
|
-
end
|
131
|
-
def logical_container(name, options={})
|
132
|
-
add LogicalContainer.new self, name, options
|
133
|
-
end
|
134
|
-
def group(name, options={})
|
135
|
-
logical_container name, options
|
136
|
-
end
|
137
|
-
def add(child)
|
138
|
-
@children << child
|
139
|
-
child
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
class Colorizer
|
144
|
-
def initialize
|
145
|
-
@i = 0
|
146
|
-
@colors = %w(#9e0142
|
147
|
-
#d53e4f
|
148
|
-
#e45d33
|
149
|
-
#ed9e61
|
150
|
-
#762a83
|
151
|
-
#9970ab
|
152
|
-
#c6f578
|
153
|
-
#abdda4
|
154
|
-
#66c2a5
|
155
|
-
#3288bd
|
156
|
-
#5e4fa2)
|
157
|
-
end
|
158
|
-
|
159
|
-
def next
|
160
|
-
color = @colors[@i]
|
161
|
-
@i += 1
|
162
|
-
@i = 0 unless @i < @colors.length
|
163
|
-
color
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
class Thing
|
168
|
-
include Common
|
169
|
-
include Nominator
|
170
|
-
include Flowable
|
171
|
-
attr_reader :parent
|
172
|
-
attr_reader :name, :id, :pointers, :edge_heads
|
173
|
-
|
174
|
-
@@colorizer = Colorizer.new
|
175
|
-
|
176
|
-
def initialize(parent, name, options = {})
|
177
|
-
@parent = parent
|
178
|
-
@name = name
|
179
|
-
@id = create_id(name, parent)
|
180
|
-
@pointers = []
|
181
|
-
@rollup = false
|
182
|
-
@skip = false
|
183
|
-
@info = {}
|
184
|
-
@edge_heads = []
|
185
|
-
|
186
|
-
rollup! if options[:rollup]
|
187
|
-
options.delete(:rollup)
|
188
|
-
|
189
|
-
@render_options = options
|
190
|
-
setup_render_attributes({label: name}.merge inherited_render_options)
|
191
|
-
|
192
|
-
parent.register name, self
|
193
|
-
end
|
194
|
-
|
195
|
-
def points_to_all(*others)
|
196
|
-
others.each {|other|
|
197
|
-
points_to other
|
198
|
-
}
|
199
|
-
end
|
200
|
-
|
201
|
-
def points_to(other, options = {})
|
202
|
-
setup_render_edge(other, options)
|
203
|
-
other
|
204
|
-
end
|
205
|
-
|
206
|
-
def render(renderer)
|
207
|
-
do_render_node(renderer) unless in_rollup? || in_skip?
|
208
|
-
end
|
209
|
-
|
210
|
-
def render_edges(renderer)
|
211
|
-
pointers.each {|p|
|
212
|
-
render_pointer p, renderer
|
213
|
-
}
|
214
|
-
end
|
215
|
-
|
216
|
-
private
|
217
|
-
|
218
|
-
def do_render_node(renderer)
|
219
|
-
render_options = @render_options
|
220
|
-
attributes = @attributes
|
221
|
-
renderer.render_node(parent, id, render_options, attributes)
|
222
|
-
end
|
223
|
-
|
224
|
-
def setup_render_edge(other, options)
|
225
|
-
pointers << {
|
226
|
-
other: other,
|
227
|
-
options: {
|
228
|
-
xlabel: options[:name],
|
229
|
-
style: options[:style]
|
230
|
-
}.merge(inherited_render_options)
|
231
|
-
}
|
232
|
-
|
233
|
-
if options[:colorize] || root.colorize_edges?
|
234
|
-
edge_color = next_colorizer_color
|
235
|
-
pointers.last[:options].merge!({
|
236
|
-
color: edge_color,
|
237
|
-
fontcolor: edge_color
|
238
|
-
})
|
239
|
-
end
|
240
|
-
|
241
|
-
end
|
242
|
-
|
243
|
-
def render_pointer(pointer, renderer)
|
244
|
-
other = pointer[:other]
|
245
|
-
while (other.in_rollup? && !other.on_top_rollup?) do
|
246
|
-
other = other.parent
|
247
|
-
end
|
248
|
-
return if other.under_rollup?
|
249
|
-
|
250
|
-
from = self
|
251
|
-
while (from.in_rollup? && !from.on_top_rollup?) do
|
252
|
-
from = from.parent
|
253
|
-
end
|
254
|
-
|
255
|
-
return if from.in_skip?
|
256
|
-
|
257
|
-
return if from == self && from.in_rollup?
|
258
|
-
return if from == other
|
259
|
-
return if already_added_connection?(other)
|
260
|
-
|
261
|
-
if other.in_skip?
|
262
|
-
others = resolve_skipped_others other
|
263
|
-
else
|
264
|
-
others = [other]
|
265
|
-
end
|
266
|
-
|
267
|
-
others.each do |other|
|
268
|
-
edge_heads << other
|
269
|
-
render_options = pointer[:options]
|
270
|
-
renderer.render_edge(from, other, render_options)
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
def already_added_connection?(other)
|
275
|
-
edge_heads.include? other
|
276
|
-
end
|
277
|
-
|
278
|
-
def resolve_skipped_others(skipped)
|
279
|
-
resolved = []
|
280
|
-
skipped.pointers.each {|pointer|
|
281
|
-
next_in_line = pointer[:other]
|
282
|
-
if next_in_line.in_skip?
|
283
|
-
resolved += resolve_skipped_others next_in_line
|
284
|
-
else
|
285
|
-
resolved << next_in_line
|
286
|
-
end
|
287
|
-
}
|
288
|
-
resolved
|
289
|
-
end
|
290
|
-
|
291
|
-
def next_colorizer_color
|
292
|
-
@@colorizer.next
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
class Container
|
297
|
-
include Common
|
298
|
-
include Nominator
|
299
|
-
include Parent
|
300
|
-
attr_reader :parent
|
301
|
-
attr_reader :name, :id, :node, :render_id, :render_type, :render_options, :children
|
302
|
-
|
303
|
-
def initialize(parent, name, options = {})
|
304
|
-
@children = []
|
305
|
-
@by_name = Registry.new name
|
306
|
-
@parent = parent
|
307
|
-
@name = name
|
308
|
-
@id = create_id(name, parent)
|
309
|
-
@skip = false
|
310
|
-
@info = {}
|
311
|
-
|
312
|
-
init_rollup options
|
313
|
-
|
314
|
-
setup_render_attributes label: name
|
315
|
-
|
316
|
-
@render_options = options.merge inherited_render_options
|
317
|
-
|
318
|
-
parent.register name, self
|
319
|
-
end
|
320
|
-
|
321
|
-
def register(name, thing)
|
322
|
-
@by_name.register name, thing
|
323
|
-
parent.register name, thing
|
324
|
-
end
|
325
|
-
|
326
|
-
def render(renderer)
|
327
|
-
if on_top_rollup?
|
328
|
-
do_render_node renderer
|
329
|
-
elsif !under_rollup?
|
330
|
-
do_render_subgraph renderer
|
331
|
-
end
|
332
|
-
|
333
|
-
children.each {|c|
|
334
|
-
c.render renderer
|
335
|
-
}
|
336
|
-
end
|
337
|
-
|
338
|
-
def render_edges(renderer)
|
339
|
-
children.each {|c|
|
340
|
-
c.render_edges renderer
|
341
|
-
}
|
342
|
-
end
|
343
|
-
|
344
|
-
private
|
345
|
-
|
346
|
-
def do_render_subgraph(renderer)
|
347
|
-
@render_type = :subgraph
|
348
|
-
render_id = cluster_prefix + id
|
349
|
-
attributes = @attributes
|
350
|
-
@render_id = render_id
|
351
|
-
@subgraph = renderer.render_subgraph(parent, render_id, render_options, attributes)
|
352
|
-
end
|
353
|
-
|
354
|
-
def do_render_node(renderer)
|
355
|
-
@render_type = :node
|
356
|
-
@render_id = id
|
357
|
-
render_id = @render_id
|
358
|
-
attributes = @attributes
|
359
|
-
renderer.render_node(parent, render_id, render_options, attributes)
|
360
|
-
end
|
361
|
-
|
362
|
-
def init_rollup(options)
|
363
|
-
@rollup = false
|
364
|
-
rollup! if options[:rollup]
|
365
|
-
options.delete(:rollup)
|
366
|
-
end
|
367
|
-
|
368
|
-
def cluster_prefix
|
369
|
-
is_cluster = true
|
370
|
-
if @render_options.has_key? :cluster
|
371
|
-
is_cluster = @render_options[:cluster]
|
372
|
-
@render_options.delete :cluster
|
373
|
-
end
|
374
|
-
cluster_prefix = (is_cluster ? 'cluster_' : '')
|
375
|
-
end
|
376
|
-
|
377
|
-
end
|
378
|
-
|
379
|
-
class LogicalContainer < Container
|
380
|
-
def initialize(parent, name, options)
|
381
|
-
super parent, name, options.merge(cluster: false)
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
require 'date'
|
386
|
-
|
387
|
-
class GraphvizRenderer
|
388
|
-
attr_reader :graph
|
389
|
-
|
390
|
-
def initialize(title, hints)
|
391
|
-
@graph = GraphViz.digraph(title)
|
392
|
-
@graph[hints]
|
393
|
-
@subgraphs = {}
|
394
|
-
@nodes = {}
|
395
|
-
end
|
396
|
-
|
397
|
-
def render_edge(from, other, options)
|
398
|
-
edge = graph.add_edges from.id, other.id
|
399
|
-
options.each { |key, value|
|
400
|
-
edge[key] = value unless value.nil?
|
401
|
-
}
|
402
|
-
edge
|
403
|
-
end
|
404
|
-
|
405
|
-
def render_node(parent, id, options, attributes)
|
406
|
-
clean_node_options options
|
407
|
-
default_options = {:shape => 'box', :style => ''}
|
408
|
-
node = parent_node(parent).add_nodes(id, default_options.merge(options))
|
409
|
-
apply_render_attributes node, attributes
|
410
|
-
end
|
411
|
-
|
412
|
-
def render_subgraph(parent, id, options, attributes)
|
413
|
-
subgraph = parent_node(parent).add_graph(id, options)
|
414
|
-
apply_render_attributes subgraph, attributes
|
415
|
-
@subgraphs[id] = subgraph
|
416
|
-
subgraph
|
417
|
-
end
|
418
|
-
|
419
|
-
private
|
420
|
-
|
421
|
-
def clean_node_options(options)
|
422
|
-
options.delete(:rank)
|
423
|
-
options.delete(:cluster)
|
424
|
-
options
|
425
|
-
end
|
426
|
-
|
427
|
-
def parent_node(parent)
|
428
|
-
return graph unless parent.respond_to?(:render_id)
|
429
|
-
node = graph.search_node(parent.render_id)
|
430
|
-
return node unless node.nil?
|
431
|
-
subgraph = @subgraphs[parent.render_id]
|
432
|
-
raise "couldn't find node or graph: #{parent.render_id}, out of graphs: #{graph_ids}" if subgraph.nil?
|
433
|
-
subgraph
|
434
|
-
end
|
435
|
-
|
436
|
-
def apply_render_attributes(node, attributes)
|
437
|
-
attributes.each do |key, value|
|
438
|
-
node[key] = value
|
439
|
-
end
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
class Flow
|
444
|
-
def initialize(sys, name)
|
445
|
-
@sys = sys
|
446
|
-
@name = name
|
447
|
-
@calls = []
|
448
|
-
end
|
449
|
-
|
450
|
-
def make_connections
|
451
|
-
calls.each {|from, to, label|
|
452
|
-
thing_of(from).points_to thing_of(to), label: label
|
453
|
-
}
|
454
|
-
end
|
455
|
-
|
456
|
-
def flows(*steps)
|
457
|
-
from = nil
|
458
|
-
to = nil
|
459
|
-
label = nil
|
460
|
-
steps.each do |step|
|
461
|
-
if from.nil?
|
462
|
-
from = ensure_is_thing(step)
|
463
|
-
elsif label.nil? && step.is_a?(String)
|
464
|
-
label = step
|
465
|
-
elsif to.nil?
|
466
|
-
to = ensure_is_thing(step)
|
467
|
-
end
|
468
|
-
unless to.nil?
|
469
|
-
calls << [from, to, label]
|
470
|
-
from = to
|
471
|
-
to = label = nil
|
472
|
-
end
|
473
|
-
end
|
474
|
-
end
|
475
|
-
|
476
|
-
def ensure_is_thing(step)
|
477
|
-
raise "Expected some thing or process: '#{step}' already got: #{calls}" unless step.is_a?(Thing) || step.is_a?(Process)
|
478
|
-
step
|
479
|
-
end
|
480
|
-
|
481
|
-
def output(type_to_file)
|
482
|
-
type = type_to_file.keys.first
|
483
|
-
raise "Only support sequence, not: '#{type}'" unless type == :sequence
|
484
|
-
render.output(type_to_file)
|
485
|
-
end
|
486
|
-
|
487
|
-
def render
|
488
|
-
renderer = SequenceRenderer.new(@name)
|
489
|
-
calls.each do |from, to, label|
|
490
|
-
renderer.render_edge from, to, {label: label}
|
491
|
-
end
|
492
|
-
renderer.rendered
|
493
|
-
end
|
494
|
-
|
495
|
-
private
|
496
|
-
|
497
|
-
attr_reader :calls, :sys
|
498
|
-
|
499
|
-
def thing_of(it)
|
500
|
-
return it.processor if it.is_a?(Process)
|
501
|
-
it
|
502
|
-
end
|
503
|
-
end
|
504
|
-
|
505
|
-
|
506
|
-
class RenderedSequence
|
507
|
-
def initialize(lines)
|
508
|
-
@lines = lines
|
509
|
-
end
|
510
|
-
def output(type_to_file)
|
511
|
-
text = @lines.map(&:strip).join "\n"
|
512
|
-
File.write type_to_file.values.first, text
|
513
|
-
text
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
class SequenceRenderer
|
518
|
-
attr_reader :lines
|
519
|
-
def initialize(title)
|
520
|
-
@lines = []
|
521
|
-
end
|
522
|
-
|
523
|
-
def render_edge(from, other, options)
|
524
|
-
|
525
|
-
detail = options[:label]
|
526
|
-
receiver_label = other.name
|
527
|
-
sender_label = from.name
|
528
|
-
if other.is_a?(Process)
|
529
|
-
detail = process_annotations(detail, sender_label, receiver_label, other.description)
|
530
|
-
receiver_label = process_start_label(receiver_label)
|
531
|
-
elsif from.is_a?(Process)
|
532
|
-
receiver_label = process_end_label(receiver_label)
|
533
|
-
end
|
534
|
-
lines << "#{sender_label} -> #{receiver_label}: #{detail}"
|
535
|
-
end
|
536
|
-
|
537
|
-
def rendered
|
538
|
-
RenderedSequence.new lines
|
539
|
-
end
|
540
|
-
|
541
|
-
private
|
542
|
-
|
543
|
-
def process_start_label(receiver_label)
|
544
|
-
"+#{receiver_label}"
|
545
|
-
end
|
546
|
-
|
547
|
-
def process_end_label(receiver_label)
|
548
|
-
"-#{receiver_label}"
|
549
|
-
end
|
550
|
-
|
551
|
-
def process_annotations(detail, sender, receiver, process_description)
|
552
|
-
detail = [detail,
|
553
|
-
"note right of #{receiver}",
|
554
|
-
" #{process_description}",
|
555
|
-
'end note'].join("\n")
|
556
|
-
end
|
557
|
-
end
|
558
|
-
|
559
|
-
class System
|
560
|
-
include Parent
|
561
|
-
include Nominator
|
562
|
-
|
563
|
-
attr_reader :render_hints, :title, :children, :graph
|
564
|
-
|
565
|
-
alias :name :title
|
566
|
-
alias :render_options :render_hints
|
567
|
-
|
568
|
-
def initialize(name, hints = {splines: 'line'})
|
569
|
-
@children = []
|
570
|
-
@by_name = Registry.new name
|
571
|
-
@non_render_hints = remove_dogviz_hints!(hints)
|
572
|
-
@render_hints = hints
|
573
|
-
@title = create_title(name)
|
574
|
-
@rendered = false
|
575
|
-
end
|
576
|
-
|
577
|
-
def output(*args)
|
578
|
-
render
|
579
|
-
out = graph.output *args
|
580
|
-
puts "Created output: #{args.join ' '}" if run_from_command_line?
|
581
|
-
out
|
582
|
-
end
|
583
|
-
|
584
|
-
def flow(name)
|
585
|
-
Flow.new self, name
|
586
|
-
end
|
587
|
-
|
588
|
-
def render(type=:graphviz)
|
589
|
-
return @graph if @rendered
|
590
|
-
raise "dunno bout that '#{type}', only know :graphviz" unless type == :graphviz
|
591
|
-
|
592
|
-
renderer = GraphvizRenderer.new @title, render_hints
|
593
|
-
|
594
|
-
children.each {|c|
|
595
|
-
c.render renderer
|
596
|
-
}
|
597
|
-
children.each {|c|
|
598
|
-
c.render_edges renderer
|
599
|
-
}
|
600
|
-
@rendered = true
|
601
|
-
@graph = renderer.graph
|
602
|
-
end
|
603
|
-
|
604
|
-
def rollup?
|
605
|
-
false
|
606
|
-
end
|
607
|
-
|
608
|
-
def skip?
|
609
|
-
false
|
610
|
-
end
|
611
|
-
|
612
|
-
def register(name, thing)
|
613
|
-
@by_name.register name, thing
|
614
|
-
end
|
615
|
-
|
616
|
-
def colorize_edges?
|
617
|
-
@non_render_hints[:colorize_edges]
|
618
|
-
end
|
619
|
-
|
620
|
-
private
|
621
|
-
|
622
|
-
def remove_dogviz_hints!(hints)
|
623
|
-
dogviz_only_hints = {}
|
624
|
-
%i(colorize_edges).each {|k|
|
625
|
-
dogviz_only_hints[k] = hints.delete k
|
626
|
-
}
|
627
|
-
dogviz_only_hints
|
628
|
-
end
|
629
|
-
|
630
|
-
def create_title(name)
|
631
|
-
now = DateTime.now
|
632
|
-
"#{now.strftime '%H:%M'} #{name} #{now.strftime '%F'}"
|
633
|
-
end
|
634
|
-
|
635
|
-
def run_from_command_line?
|
636
|
-
$stdout.isatty
|
637
|
-
end
|
638
|
-
end
|
639
|
-
|
640
|
-
class LookupError < StandardError
|
641
|
-
def initialize(context, message)
|
642
|
-
super "(in context '#{context}') #{message}"
|
643
|
-
end
|
644
|
-
end
|
645
|
-
class MissingMatchBlockError < LookupError
|
646
|
-
def initialize(context)
|
647
|
-
super context, 'need to provide match block'
|
648
|
-
end
|
649
|
-
end
|
650
|
-
class DuplicateLookupError < LookupError
|
651
|
-
def initialize(context, name)
|
652
|
-
super context, "More than one object registered of name '#{name}' - you'll need to search in a narrower context"
|
653
|
-
end
|
654
|
-
end
|
655
|
-
class Registry
|
656
|
-
def initialize(context)
|
657
|
-
@context = context
|
658
|
-
@by_name = {}
|
659
|
-
@all = []
|
660
|
-
end
|
661
|
-
|
662
|
-
def register(name, thing)
|
663
|
-
@all << thing
|
664
|
-
if @by_name.has_key?(name)
|
665
|
-
@by_name[name] = DuplicateLookupError.new @context, name
|
666
|
-
else
|
667
|
-
@by_name[name] = thing
|
668
|
-
end
|
669
|
-
end
|
670
|
-
|
671
|
-
def find(&matcher)
|
672
|
-
raise LookupError.new(@context, "need to provide match block") unless block_given?
|
673
|
-
@all.find &matcher
|
674
|
-
end
|
675
|
-
|
676
|
-
def find_all(&matcher)
|
677
|
-
raise MissingMatchBlockError.new(@context) unless block_given?
|
678
|
-
@all.select &matcher
|
679
|
-
end
|
680
|
-
|
681
|
-
def lookup(name)
|
682
|
-
found = @by_name[name]
|
683
|
-
raise LookupError.new(@context, "could not find '#{name}'") if found.nil?
|
684
|
-
raise found if found.is_a?(Exception)
|
685
|
-
found
|
686
|
-
end
|
687
|
-
end
|
688
|
-
|
689
|
-
end
|
8
|
+
require_relative 'dogviz/flow.rb'
|
9
|
+
require_relative 'dogviz/system.rb'
|