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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d93013b0cc858fbd5459c5fc0794cc334df4ab7f
|
4
|
+
data.tar.gz: ee353c1dfe91e9b10901d2305944806b62f3e5c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76aa31f77889f81eae728587e3660ed9650b09a04a1fced9fe87699631870a73f98f62f0acfccb9af84399b7f68c380f177df846ce13aa2458e99561061c4499
|
7
|
+
data.tar.gz: 7fa12c43c393cb7714e697bebdece5fb387d72df776a96aea8ec794932b56593158b906c9bb8e68e2999dd893174cea158e2e92b76c8a5968062b7cdc02e867c
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Dogviz
|
2
|
+
class Colorizer
|
3
|
+
def initialize
|
4
|
+
@i = 0
|
5
|
+
@colors = %w(#9e0142
|
6
|
+
#d53e4f
|
7
|
+
#e45d33
|
8
|
+
#ed9e61
|
9
|
+
#762a83
|
10
|
+
#9970ab
|
11
|
+
#c6f578
|
12
|
+
#abdda4
|
13
|
+
#66c2a5
|
14
|
+
#3288bd
|
15
|
+
#5e4fa2)
|
16
|
+
end
|
17
|
+
|
18
|
+
def next
|
19
|
+
color = @colors[@i]
|
20
|
+
@i += 1
|
21
|
+
@i = 0 unless @i < @colors.length
|
22
|
+
color
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Dogviz
|
2
|
+
module Common
|
3
|
+
def create_id(name, parent)
|
4
|
+
parts = []
|
5
|
+
parts << parent.id if parent.respond_to? :id
|
6
|
+
parts += name.split /\s/
|
7
|
+
parts.join '_'
|
8
|
+
end
|
9
|
+
|
10
|
+
def root
|
11
|
+
ancestors.last
|
12
|
+
end
|
13
|
+
|
14
|
+
def ancestors
|
15
|
+
ancestors = [parent]
|
16
|
+
loop do
|
17
|
+
break unless ancestors.last.respond_to?(:parent)
|
18
|
+
ancestors << ancestors.last.parent
|
19
|
+
end
|
20
|
+
ancestors
|
21
|
+
end
|
22
|
+
|
23
|
+
def info(fields)
|
24
|
+
@info.merge! fields
|
25
|
+
setup_render_attributes(label: label_with_info)
|
26
|
+
end
|
27
|
+
|
28
|
+
def doclink(url)
|
29
|
+
setup_render_attributes(URL: url)
|
30
|
+
end
|
31
|
+
|
32
|
+
def label_with_info
|
33
|
+
lines = [name]
|
34
|
+
@info.each { |k, v|
|
35
|
+
lines << "#{k}: #{v}"
|
36
|
+
}
|
37
|
+
lines.join "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_render_attributes(attributes)
|
41
|
+
@attributes = {} if @attributes.nil?
|
42
|
+
@attributes.merge!(attributes)
|
43
|
+
end
|
44
|
+
|
45
|
+
def rollup?
|
46
|
+
@rollup
|
47
|
+
end
|
48
|
+
|
49
|
+
def rollup!
|
50
|
+
@rollup = true
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def skip!
|
55
|
+
@skip = true
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def skip?
|
60
|
+
@skip
|
61
|
+
end
|
62
|
+
|
63
|
+
def in_skip?
|
64
|
+
skip? || under_skip?
|
65
|
+
end
|
66
|
+
|
67
|
+
def under_skip?
|
68
|
+
ancestors.any? &:skip?
|
69
|
+
end
|
70
|
+
|
71
|
+
def under_rollup?
|
72
|
+
ancestors.any? &:rollup?
|
73
|
+
end
|
74
|
+
|
75
|
+
def in_rollup?
|
76
|
+
rollup? || under_rollup?
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_top_rollup?
|
80
|
+
rollup? && !under_rollup?
|
81
|
+
end
|
82
|
+
|
83
|
+
def inherited_render_options
|
84
|
+
inherited = {}
|
85
|
+
inherited[:fontname] = parent.render_options[:fontname] if parent.render_options.include?(:fontname)
|
86
|
+
inherited
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
require_relative 'nominator'
|
3
|
+
require_relative 'parent'
|
4
|
+
|
5
|
+
module Dogviz
|
6
|
+
class Container
|
7
|
+
include Common
|
8
|
+
include Nominator
|
9
|
+
include Parent
|
10
|
+
|
11
|
+
attr_reader :parent
|
12
|
+
attr_reader :name, :id, :node, :render_id, :render_type, :render_options, :children
|
13
|
+
|
14
|
+
def initialize(parent, name, options = {})
|
15
|
+
@children = []
|
16
|
+
@by_name = Registry.new name
|
17
|
+
@parent = parent
|
18
|
+
@name = name
|
19
|
+
@id = create_id(name, parent)
|
20
|
+
@skip = false
|
21
|
+
@info = {}
|
22
|
+
|
23
|
+
init_rollup options
|
24
|
+
|
25
|
+
setup_render_attributes label: name
|
26
|
+
|
27
|
+
@render_options = options.merge inherited_render_options
|
28
|
+
|
29
|
+
parent.register name, self
|
30
|
+
end
|
31
|
+
|
32
|
+
def register(name, thing)
|
33
|
+
@by_name.register name, thing
|
34
|
+
parent.register name, thing
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(renderer)
|
38
|
+
if on_top_rollup?
|
39
|
+
do_render_node renderer
|
40
|
+
elsif !under_rollup?
|
41
|
+
do_render_subgraph renderer
|
42
|
+
end
|
43
|
+
|
44
|
+
children.each { |c|
|
45
|
+
c.render renderer
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def render_edges(renderer)
|
50
|
+
children.each { |c|
|
51
|
+
c.render_edges renderer
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def do_render_subgraph(renderer)
|
58
|
+
@render_type = :subgraph
|
59
|
+
render_id = cluster_prefix + id
|
60
|
+
attributes = @attributes
|
61
|
+
@render_id = render_id
|
62
|
+
@subgraph = renderer.render_subgraph(parent, render_id, render_options, attributes)
|
63
|
+
end
|
64
|
+
|
65
|
+
def do_render_node(renderer)
|
66
|
+
@render_type = :node
|
67
|
+
@render_id = id
|
68
|
+
render_id = @render_id
|
69
|
+
attributes = @attributes
|
70
|
+
renderer.render_node(parent, render_id, render_options, attributes)
|
71
|
+
end
|
72
|
+
|
73
|
+
def init_rollup(options)
|
74
|
+
@rollup = false
|
75
|
+
rollup! if options[:rollup]
|
76
|
+
options.delete(:rollup)
|
77
|
+
end
|
78
|
+
|
79
|
+
def cluster_prefix
|
80
|
+
is_cluster = true
|
81
|
+
if @render_options.has_key? :cluster
|
82
|
+
is_cluster = @render_options[:cluster]
|
83
|
+
@render_options.delete :cluster
|
84
|
+
end
|
85
|
+
cluster_prefix = (is_cluster ? 'cluster_' : '')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/dogviz/flow.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'sequence_renderer'
|
2
|
+
require_relative 'thing'
|
3
|
+
require_relative 'process'
|
4
|
+
|
5
|
+
module Dogviz
|
6
|
+
class Flow
|
7
|
+
def initialize(sys, name)
|
8
|
+
@sys = sys
|
9
|
+
@name = name
|
10
|
+
@calls = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def make_connections
|
14
|
+
calls.each { |from, to, label|
|
15
|
+
thing_of(from).points_to thing_of(to), label: label
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def flows(*steps)
|
20
|
+
from = nil
|
21
|
+
to = nil
|
22
|
+
label = nil
|
23
|
+
steps.each do |step|
|
24
|
+
if from.nil?
|
25
|
+
from = ensure_is_thing(step)
|
26
|
+
elsif label.nil? && step.is_a?(String)
|
27
|
+
label = step
|
28
|
+
elsif to.nil?
|
29
|
+
to = ensure_is_thing(step)
|
30
|
+
end
|
31
|
+
unless to.nil?
|
32
|
+
calls << [from, to, label]
|
33
|
+
from = to
|
34
|
+
to = label = nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_is_thing(step)
|
40
|
+
raise "Expected some thing or process: '#{step}' already got: #{calls}" unless step.is_a?(Thing) || step.is_a?(Process)
|
41
|
+
step
|
42
|
+
end
|
43
|
+
|
44
|
+
def output(type_to_file)
|
45
|
+
type = type_to_file.keys.first
|
46
|
+
raise "Only support sequence, not: '#{type}'" unless type == :sequence
|
47
|
+
render.output(type_to_file)
|
48
|
+
end
|
49
|
+
|
50
|
+
def render
|
51
|
+
renderer = SequenceRenderer.new(@name)
|
52
|
+
calls.each do |from, to, label|
|
53
|
+
renderer.render_edge from, to, {label: label}
|
54
|
+
end
|
55
|
+
renderer.rendered
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_reader :calls, :sys
|
61
|
+
|
62
|
+
def thing_of(it)
|
63
|
+
return it.processor if it.is_a?(Process)
|
64
|
+
it
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'ruby-graphviz'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Dogviz
|
5
|
+
class GraphvizRenderer
|
6
|
+
attr_reader :graph
|
7
|
+
|
8
|
+
def initialize(title, hints)
|
9
|
+
@graph = GraphViz.digraph(title)
|
10
|
+
@graph[hints]
|
11
|
+
@subgraphs = {}
|
12
|
+
@nodes = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_edge(from, other, options)
|
16
|
+
edge = graph.add_edges from.id, other.id
|
17
|
+
options.each { |key, value|
|
18
|
+
edge[key] = value unless value.nil?
|
19
|
+
}
|
20
|
+
edge
|
21
|
+
end
|
22
|
+
|
23
|
+
def render_node(parent, id, options, attributes)
|
24
|
+
clean_node_options options
|
25
|
+
default_options = {:shape => 'box', :style => ''}
|
26
|
+
node = parent_node(parent).add_nodes(id, default_options.merge(options))
|
27
|
+
apply_render_attributes node, attributes
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_subgraph(parent, id, options, attributes)
|
31
|
+
subgraph = parent_node(parent).add_graph(id, options)
|
32
|
+
apply_render_attributes subgraph, attributes
|
33
|
+
@subgraphs[id] = subgraph
|
34
|
+
subgraph
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def clean_node_options(options)
|
40
|
+
options.delete(:rank)
|
41
|
+
options.delete(:cluster)
|
42
|
+
options
|
43
|
+
end
|
44
|
+
|
45
|
+
def parent_node(parent)
|
46
|
+
return graph unless parent.respond_to?(:render_id)
|
47
|
+
node = graph.search_node(parent.render_id)
|
48
|
+
return node unless node.nil?
|
49
|
+
subgraph = @subgraphs[parent.render_id]
|
50
|
+
raise "couldn't find node or graph: #{parent.render_id}, out of graphs: #{graph_ids}" if subgraph.nil?
|
51
|
+
subgraph
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply_render_attributes(node, attributes)
|
55
|
+
attributes.each do |key, value|
|
56
|
+
node[key] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Dogviz
|
2
|
+
module Nominator
|
3
|
+
def nominate(names_to_nominees)
|
4
|
+
names_to_nominees.each { |name, nominee|
|
5
|
+
self.class.send(:define_method, name) do
|
6
|
+
nominee
|
7
|
+
end
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def nominate_from(nominee_nominator, *nominee_names)
|
12
|
+
nominee_names.each { |name|
|
13
|
+
accessor_sym = name.to_s.to_sym
|
14
|
+
nominate accessor_sym => nominee_nominator.send(accessor_sym)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Dogviz
|
2
|
+
module Parent
|
3
|
+
def find_all(&matcher)
|
4
|
+
raise MissingMatchBlockError.new unless block_given?
|
5
|
+
@by_name.find_all &matcher
|
6
|
+
end
|
7
|
+
|
8
|
+
def find(name=nil, &matcher)
|
9
|
+
if block_given?
|
10
|
+
@by_name.find &matcher
|
11
|
+
else
|
12
|
+
raise 'Need to provide name or block' if name.nil?
|
13
|
+
@by_name.lookup name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def thing(name, options={})
|
18
|
+
add Thing.new self, name, options
|
19
|
+
end
|
20
|
+
|
21
|
+
def container(name, options={})
|
22
|
+
add Container.new self, name, options
|
23
|
+
end
|
24
|
+
|
25
|
+
def logical_container(name, options={})
|
26
|
+
add LogicalContainer.new self, name, options
|
27
|
+
end
|
28
|
+
|
29
|
+
def group(name, options={})
|
30
|
+
logical_container name, options
|
31
|
+
end
|
32
|
+
|
33
|
+
def add(child)
|
34
|
+
@children << child
|
35
|
+
child
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Dogviz
|
2
|
+
class Process
|
3
|
+
def initialize(processor, description)
|
4
|
+
@processor = processor
|
5
|
+
@description = description
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
@processor.name
|
10
|
+
end
|
11
|
+
|
12
|
+
def description
|
13
|
+
@description
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :processor
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'lookup_error.rb'
|
2
|
+
require_relative 'missing_match_block_error.rb'
|
3
|
+
require_relative 'duplicate_lookup_error.rb'
|
4
|
+
|
5
|
+
module Dogviz
|
6
|
+
class Registry
|
7
|
+
def initialize(context)
|
8
|
+
@context = context
|
9
|
+
@by_name = {}
|
10
|
+
@all = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def register(name, thing)
|
14
|
+
@all << thing
|
15
|
+
if @by_name.has_key?(name)
|
16
|
+
@by_name[name] = DuplicateLookupError.new @context, name
|
17
|
+
else
|
18
|
+
@by_name[name] = thing
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(&matcher)
|
23
|
+
raise LookupError.new(@context, "need to provide match block") unless block_given?
|
24
|
+
@all.find &matcher
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_all(&matcher)
|
28
|
+
raise MissingMatchBlockError.new(@context) unless block_given?
|
29
|
+
@all.select &matcher
|
30
|
+
end
|
31
|
+
|
32
|
+
def lookup(name)
|
33
|
+
found = @by_name[name]
|
34
|
+
raise LookupError.new(@context, "could not find '#{name}'") if found.nil?
|
35
|
+
raise found if found.is_a?(Exception)
|
36
|
+
found
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'process'
|
2
|
+
require_relative 'rendered_sequence'
|
3
|
+
|
4
|
+
module Dogviz
|
5
|
+
class SequenceRenderer
|
6
|
+
attr_reader :lines
|
7
|
+
|
8
|
+
def initialize(title)
|
9
|
+
@lines = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def render_edge(from, other, options)
|
13
|
+
|
14
|
+
detail = options[:label]
|
15
|
+
receiver_label = other.name
|
16
|
+
sender_label = from.name
|
17
|
+
if other.is_a?(Process)
|
18
|
+
detail = process_annotations(detail, sender_label, receiver_label, other.description)
|
19
|
+
receiver_label = process_start_label(receiver_label)
|
20
|
+
elsif from.is_a?(Process)
|
21
|
+
receiver_label = process_end_label(receiver_label)
|
22
|
+
end
|
23
|
+
lines << "#{sender_label} -> #{receiver_label}: #{detail}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def rendered
|
27
|
+
RenderedSequence.new lines
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def process_start_label(receiver_label)
|
33
|
+
"+#{receiver_label}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_end_label(receiver_label)
|
37
|
+
"-#{receiver_label}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def process_annotations(detail, sender, receiver, process_description)
|
41
|
+
detail = [detail,
|
42
|
+
"note right of #{receiver}",
|
43
|
+
" #{process_description}",
|
44
|
+
'end note'].join("\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Dogviz
|
4
|
+
class SigmaGraphHash < Hash
|
5
|
+
def initialize(hash)
|
6
|
+
hash.each { |k, v|
|
7
|
+
self[k] = v
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def output(type_to_filename)
|
12
|
+
raise StandardError.new('must provide hash (json: somejsonfilename.json)') unless type_to_filename.is_a?(Hash)
|
13
|
+
filename = get_json_filename(type_to_filename)
|
14
|
+
File.write filename, to_json
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def get_json_filename(type_to_filename)
|
20
|
+
type = type_to_filename.keys.first
|
21
|
+
raise StandardError.new('json output only supported') unless type == :json
|
22
|
+
type_to_filename[type]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'sigma_graph_hash'
|
2
|
+
|
3
|
+
module Dogviz
|
4
|
+
class SigmaRenderer
|
5
|
+
def initialize(title)
|
6
|
+
@title = title
|
7
|
+
@nodes = []
|
8
|
+
@edges = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def graph
|
12
|
+
SigmaGraphHash.new(nodes: nodes, edges: edges)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_node(parent, id, render_options, attributes)
|
16
|
+
@nodes << {id: id, label: id}
|
17
|
+
end
|
18
|
+
|
19
|
+
def render_edge(from, to, options)
|
20
|
+
@edges << {
|
21
|
+
id: "#{from.id}->#{to.id}",
|
22
|
+
label: "#{from.id}->#{to.id}",
|
23
|
+
source: from.id,
|
24
|
+
target: to.id
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :nodes, :edges
|
31
|
+
end
|
32
|
+
end
|