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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2f28cea79616735c3ff115295186438f33407ff
4
- data.tar.gz: afbe975455318158f50c85cf236f8308e385ed21
3
+ metadata.gz: d93013b0cc858fbd5459c5fc0794cc334df4ab7f
4
+ data.tar.gz: ee353c1dfe91e9b10901d2305944806b62f3e5c8
5
5
  SHA512:
6
- metadata.gz: 5facef62f26a853f1569cafe84b9c66724830d461816f1ed264c66d9d379da95670e959a89512bf74baa35bd5bd08d8e6bf8e3a571c16799ad006c2a12c61b84
7
- data.tar.gz: c1eac0792f9a326575726b4d321c615c331526c30081bb1fe04d3b4afac463c8851d8bf4409fdeed04be5eb952bc0e6bec0f995b5269f29dcc0b8e3c18067ef3
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
@@ -0,0 +1,7 @@
1
+ module Dogviz
2
+ class DuplicateLookupError < LookupError
3
+ def initialize(context, name)
4
+ super context, "More than one object registered of name '#{name}' - you'll need to search in a narrower context"
5
+ end
6
+ end
7
+ end
@@ -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,9 @@
1
+ require_relative 'process'
2
+
3
+ module Dogviz
4
+ module Flowable
5
+ def does(action)
6
+ Process.new(self, action)
7
+ end
8
+ end
9
+ 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,8 @@
1
+ require_relative 'container'
2
+ module Dogviz
3
+ class LogicalContainer < Container
4
+ def initialize(parent, name, options)
5
+ super parent, name, options.merge(cluster: false)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Dogviz
2
+ class LookupError < StandardError
3
+ def initialize(context, message)
4
+ super "(in context '#{context}') #{message}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Dogviz
2
+ class MissingMatchBlockError < LookupError
3
+ def initialize(context)
4
+ super context, 'need to provide match block'
5
+ end
6
+ end
7
+ 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,13 @@
1
+ module Dogviz
2
+ class RenderedSequence
3
+ def initialize(lines)
4
+ @lines = lines
5
+ end
6
+
7
+ def output(type_to_file)
8
+ text = @lines.map(&:strip).join "\n"
9
+ File.write type_to_file.values.first, text
10
+ text
11
+ end
12
+ end
13
+ 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