gviz 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +113 -0
- data/Rakefile +1 -0
- data/examples/arrow.dot +88 -0
- data/examples/arrows.rb +59 -0
- data/examples/color.dot +1302 -0
- data/examples/colors.rb +673 -0
- data/examples/examples.rb +71 -0
- data/examples/sample1.png +0 -0
- data/examples/sample2.png +0 -0
- data/examples/sample3.png +0 -0
- data/examples/scraper.rb +59 -0
- data/examples/shape.dot +108 -0
- data/examples/shapes.rb +79 -0
- data/gviz.gemspec +19 -0
- data/lib/gviz.rb +211 -0
- data/lib/gviz/version.rb +3 -0
- data/spec/gviz_spec.rb +395 -0
- data/spec/spec_helper.rb +9 -0
- metadata +69 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative "../lib/gviz"
|
2
|
+
|
3
|
+
simple = Gviz.new
|
4
|
+
simple.graph do
|
5
|
+
route :main => [:init, :parse, :cleanup, :printf]
|
6
|
+
route :init => :make
|
7
|
+
route :parse => :execute
|
8
|
+
route :execute => [:make, :compare, :printf]
|
9
|
+
end
|
10
|
+
# simple.save(:a, :svg)
|
11
|
+
|
12
|
+
enhanced = Gviz.new
|
13
|
+
enhanced.graph do
|
14
|
+
route :main => [:init, :parse, :cleanup, :printf]
|
15
|
+
route :init => :make, :parse => :execute
|
16
|
+
route :execute => [:make, :compare, :printf]
|
17
|
+
node(:main, :shape => 'box')
|
18
|
+
node(:compare, :shape => 'box', :style => 'filled', :color => '.7 .3 1.0')
|
19
|
+
node(:make, :label => "make a\nstring")
|
20
|
+
edge(:main_printf, :style => 'bold', :label => "100 times", :color => 'red', :weight => 8)
|
21
|
+
edge(:main_parse, :weight => 8)
|
22
|
+
edge(:main_init, :style => "dotted")
|
23
|
+
edge(:execute_compare, :color => 'red')
|
24
|
+
end
|
25
|
+
# puts enhanced
|
26
|
+
|
27
|
+
polygonal = Gviz.new
|
28
|
+
polygonal.graph do
|
29
|
+
route :a => :b, :b => [:c, :d]
|
30
|
+
node(:a, :shape => 'polygon', :sides => 5, :peripheries => 3, :style => 'filled', :fillcolor => '#DA70D6')
|
31
|
+
node(:c, :shape => 'polygon', :sides => 4, :skew => 0.4, :label => "hellow world", :peripheries => 2, :orientation => 20, :fontname => 'Helvetica', :fontcolor => '#DA70D6')
|
32
|
+
node(:d, :shape => 'invtriangle')
|
33
|
+
node(:e, :shape => 'polygon', :style => 'setlinewidth(8)', :sides => 4, :distortion => 0.7)
|
34
|
+
edge(:b_d, :label => 'path to the world', :decorate => true, :labelfloat => true)
|
35
|
+
edge(:a_b, :dir => 'both', :headport => 'se')
|
36
|
+
edge(:a_b_1, :dir => 'back')
|
37
|
+
global(:rankdir => "TB", :rankseq => 'equally')
|
38
|
+
end
|
39
|
+
# puts polygonal
|
40
|
+
|
41
|
+
record = Gviz.new
|
42
|
+
record.graph do
|
43
|
+
nodes(:shape => 'record')
|
44
|
+
route :st1 => [:st2, :st3]
|
45
|
+
node(:st1, :label => "<f0> left|<f1> middle|<f2> right")
|
46
|
+
node(:st2, :label => "<f0> one|<f1> two")
|
47
|
+
node(:st3, :label => "hello\nworld|{b|{c|<here> d|e}|f}|g|h")
|
48
|
+
edge("st1:f1_st2:f0")
|
49
|
+
edge("st1:f2_st3:here")
|
50
|
+
end
|
51
|
+
# puts record
|
52
|
+
|
53
|
+
sub = Gviz.new
|
54
|
+
sub.graph do
|
55
|
+
subgraph do
|
56
|
+
nodes(style:'filled', color:'white')
|
57
|
+
global(style:'filled', color:'lightgrey', label:'process #1')
|
58
|
+
route(:a0 => :a1, :a1 => :a2, :a2 => :a3)
|
59
|
+
end
|
60
|
+
|
61
|
+
subgraph do
|
62
|
+
nodes(style:'filled')
|
63
|
+
global(color:'blue', label:'process #2')
|
64
|
+
route(:b0 => :b1, :b1 => :b2, :b2 => :b3)
|
65
|
+
end
|
66
|
+
route :start => [:a0, :b0]
|
67
|
+
route :a1 => :b3, :b2 => :a3, :a3 => [:a0, :end], :b3 => :end
|
68
|
+
node(:start, shape:'Mdiamond')
|
69
|
+
node(:end, shape:'Msquare')
|
70
|
+
end
|
71
|
+
# sub.save('a', 'png')
|
Binary file
|
Binary file
|
Binary file
|
data/examples/scraper.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require "nokogiri"
|
3
|
+
require "open-uri"
|
4
|
+
|
5
|
+
module Gviz
|
6
|
+
class Scraper
|
7
|
+
URL = "http://www.graphviz.org/content/"
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def build(path)
|
11
|
+
@html ||= get(URL+path)
|
12
|
+
parse @html
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(url)
|
16
|
+
@html = Nokogiri::HTML(open url)
|
17
|
+
rescue OpenURI::HTTPError => e
|
18
|
+
STDERR.puts "HTTP Access Error:#{e}"
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(html)
|
23
|
+
q = []
|
24
|
+
|
25
|
+
# ## shapes
|
26
|
+
# html.css("div.content>table td>a").each do |a|
|
27
|
+
# q << a.text
|
28
|
+
# end
|
29
|
+
# return q.uniq
|
30
|
+
|
31
|
+
## arrows
|
32
|
+
html.css("table").each_with_index do |tbl, i|
|
33
|
+
if i == 5
|
34
|
+
tbl.css("td").each do |e|
|
35
|
+
txt = e.text.strip.chomp
|
36
|
+
q << txt unless txt.empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return q.uniq
|
41
|
+
|
42
|
+
# ## X11 colors scheme
|
43
|
+
# table = html.at("div.content>table").css('td>a').each do |color|
|
44
|
+
# txt = color.text.gsub(' ','')
|
45
|
+
# q << txt unless txt.match(/^\d+$/)
|
46
|
+
# end
|
47
|
+
# return q.sort.uniq.group_by { |c| c[/^\D+/]; $& }
|
48
|
+
# .flat_map { |k, v| v.sort_by { |c| c[/\d+/].to_i } }
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if __FILE__ == $0
|
56
|
+
# puts Gviz::Scraper.build("node-shapes")
|
57
|
+
puts Gviz::Scraper.build("arrow-shapes")
|
58
|
+
# puts Gviz::Scraper.build("color-names")
|
59
|
+
end
|
data/examples/shape.dot
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
digraph G {
|
2
|
+
node[style="filled",fillcolor="orchid"];
|
3
|
+
edge[arrowhead="none"];
|
4
|
+
box[shape="box"];
|
5
|
+
polygon[shape="polygon"];
|
6
|
+
ellipse[shape="ellipse"];
|
7
|
+
oval[shape="oval"];
|
8
|
+
circle[shape="circle"];
|
9
|
+
point[shape="point"];
|
10
|
+
egg[shape="egg"];
|
11
|
+
triangle[shape="triangle"];
|
12
|
+
plaintext[shape="plaintext"];
|
13
|
+
diamond[shape="diamond"];
|
14
|
+
trapezium[shape="trapezium"];
|
15
|
+
parallelogram[shape="parallelogram"];
|
16
|
+
house[shape="house"];
|
17
|
+
pentagon[shape="pentagon"];
|
18
|
+
hexagon[shape="hexagon"];
|
19
|
+
septagon[shape="septagon"];
|
20
|
+
octagon[shape="octagon"];
|
21
|
+
doublecircle[shape="doublecircle"];
|
22
|
+
doubleoctagon[shape="doubleoctagon"];
|
23
|
+
tripleoctagon[shape="tripleoctagon"];
|
24
|
+
invtriangle[shape="invtriangle"];
|
25
|
+
invtrapezium[shape="invtrapezium"];
|
26
|
+
invhouse[shape="invhouse"];
|
27
|
+
Mdiamond[shape="Mdiamond"];
|
28
|
+
Msquare[shape="Msquare"];
|
29
|
+
Mcircle[shape="Mcircle"];
|
30
|
+
rect[shape="rect"];
|
31
|
+
rectangle[shape="rectangle"];
|
32
|
+
square[shape="square"];
|
33
|
+
none[shape="none"];
|
34
|
+
note[shape="note"];
|
35
|
+
tab[shape="tab"];
|
36
|
+
folder[shape="folder"];
|
37
|
+
box3d[shape="box3d"];
|
38
|
+
component[shape="component"];
|
39
|
+
promoter[shape="promoter"];
|
40
|
+
cds[shape="cds"];
|
41
|
+
terminator[shape="terminator"];
|
42
|
+
utr[shape="utr"];
|
43
|
+
primersite[shape="primersite"];
|
44
|
+
restrictionsite[shape="restrictionsite"];
|
45
|
+
fivepoverhang[shape="fivepoverhang"];
|
46
|
+
threepoverhang[shape="threepoverhang"];
|
47
|
+
noverhang[shape="noverhang"];
|
48
|
+
assembly[shape="assembly"];
|
49
|
+
signature[shape="signature"];
|
50
|
+
insulator[shape="insulator"];
|
51
|
+
ribosite[shape="ribosite"];
|
52
|
+
rnastab[shape="rnastab"];
|
53
|
+
proteasesite[shape="proteasesite"];
|
54
|
+
proteinstab[shape="proteinstab"];
|
55
|
+
rpromoter[shape="rpromoter"];
|
56
|
+
rarrow[shape="rarrow"];
|
57
|
+
larrow[shape="larrow"];
|
58
|
+
lpromoter[shape="lpromoter"];
|
59
|
+
box -> polygon;
|
60
|
+
polygon -> ellipse;
|
61
|
+
ellipse -> oval;
|
62
|
+
oval -> circle;
|
63
|
+
circle -> point[headlabel="point"];
|
64
|
+
point -> egg;
|
65
|
+
egg -> triangle;
|
66
|
+
triangle -> plaintext;
|
67
|
+
plaintext -> diamond;
|
68
|
+
trapezium -> parallelogram;
|
69
|
+
parallelogram -> house;
|
70
|
+
house -> pentagon;
|
71
|
+
pentagon -> hexagon;
|
72
|
+
hexagon -> septagon;
|
73
|
+
septagon -> octagon;
|
74
|
+
octagon -> doublecircle;
|
75
|
+
doublecircle -> doubleoctagon;
|
76
|
+
doubleoctagon -> tripleoctagon;
|
77
|
+
invtriangle -> invtrapezium;
|
78
|
+
invtrapezium -> invhouse;
|
79
|
+
invhouse -> Mdiamond;
|
80
|
+
Mdiamond -> Msquare;
|
81
|
+
Msquare -> Mcircle;
|
82
|
+
Mcircle -> rect;
|
83
|
+
rect -> rectangle;
|
84
|
+
rectangle -> square;
|
85
|
+
square -> none;
|
86
|
+
note -> tab;
|
87
|
+
tab -> folder;
|
88
|
+
folder -> box3d;
|
89
|
+
box3d -> component;
|
90
|
+
component -> promoter;
|
91
|
+
promoter -> cds;
|
92
|
+
cds -> terminator;
|
93
|
+
terminator -> utr;
|
94
|
+
utr -> primersite;
|
95
|
+
restrictionsite -> fivepoverhang;
|
96
|
+
fivepoverhang -> threepoverhang;
|
97
|
+
threepoverhang -> noverhang;
|
98
|
+
noverhang -> assembly;
|
99
|
+
assembly -> signature;
|
100
|
+
signature -> insulator;
|
101
|
+
insulator -> ribosite;
|
102
|
+
ribosite -> rnastab;
|
103
|
+
rnastab -> proteasesite;
|
104
|
+
proteinstab -> rpromoter;
|
105
|
+
rpromoter -> rarrow;
|
106
|
+
rarrow -> larrow;
|
107
|
+
larrow -> lpromoter;
|
108
|
+
}
|
data/examples/shapes.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Shape Varieties
|
2
|
+
require_relative "../lib/gviz"
|
3
|
+
|
4
|
+
shapes = DATA.lines.map(&:chomp).reject { |l| l.empty? }
|
5
|
+
|
6
|
+
g = Gviz.new
|
7
|
+
|
8
|
+
g.graph do
|
9
|
+
nodes(style:'filled', fillcolor:'orchid')
|
10
|
+
edges(arrowhead:'none')
|
11
|
+
shapes.each { |shape| node shape.intern, shape: shape }
|
12
|
+
|
13
|
+
shapes.each_slice(10) do |gr|
|
14
|
+
gr.each_cons(2) do |a, b|
|
15
|
+
route a.intern => b.intern
|
16
|
+
end
|
17
|
+
end
|
18
|
+
edge(:circle_point, headlabel:'point')
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
puts g
|
23
|
+
|
24
|
+
__END__
|
25
|
+
box
|
26
|
+
polygon
|
27
|
+
ellipse
|
28
|
+
oval
|
29
|
+
circle
|
30
|
+
point
|
31
|
+
egg
|
32
|
+
triangle
|
33
|
+
plaintext
|
34
|
+
diamond
|
35
|
+
trapezium
|
36
|
+
parallelogram
|
37
|
+
house
|
38
|
+
pentagon
|
39
|
+
hexagon
|
40
|
+
septagon
|
41
|
+
octagon
|
42
|
+
doublecircle
|
43
|
+
doubleoctagon
|
44
|
+
tripleoctagon
|
45
|
+
invtriangle
|
46
|
+
invtrapezium
|
47
|
+
invhouse
|
48
|
+
Mdiamond
|
49
|
+
Msquare
|
50
|
+
Mcircle
|
51
|
+
rect
|
52
|
+
rectangle
|
53
|
+
square
|
54
|
+
none
|
55
|
+
note
|
56
|
+
tab
|
57
|
+
folder
|
58
|
+
box3d
|
59
|
+
component
|
60
|
+
promoter
|
61
|
+
cds
|
62
|
+
terminator
|
63
|
+
utr
|
64
|
+
primersite
|
65
|
+
restrictionsite
|
66
|
+
fivepoverhang
|
67
|
+
threepoverhang
|
68
|
+
noverhang
|
69
|
+
assembly
|
70
|
+
signature
|
71
|
+
insulator
|
72
|
+
ribosite
|
73
|
+
rnastab
|
74
|
+
proteasesite
|
75
|
+
proteinstab
|
76
|
+
rpromoter
|
77
|
+
rarrow
|
78
|
+
larrow
|
79
|
+
lpromoter
|
data/gviz.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gviz/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "gviz"
|
8
|
+
gem.version = Gviz::VERSION
|
9
|
+
gem.authors = ["kyoendo"]
|
10
|
+
gem.email = ["postagie@gmail.com"]
|
11
|
+
gem.description = %q{Ruby's interface of graphviz}
|
12
|
+
gem.summary = %q{Ruby's interface of graphviz. It generate a dot file with simple ruby's syntax. }
|
13
|
+
gem.homepage = "https://github.com/melborne/Gviz"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/gviz.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
class Gviz
|
2
|
+
class Node < Struct.new(:id, :attrs)
|
3
|
+
def initialize(id, attrs={})
|
4
|
+
raise ArgumentError, "`id` must not include underscores" if id.match(/_/)
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
"#{id}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Edge < Struct.new(:id, :attrs)
|
14
|
+
attr_reader :st, :ed, :seq, :st_port, :ed_port
|
15
|
+
def initialize(id, attrs={})
|
16
|
+
raise ArgumentError, "`attrs` must a hash" unless attrs.is_a?(Hash)
|
17
|
+
id, @st, @ed, @seq, @st_port, @ed_port = parse_id(id)
|
18
|
+
super(id, attrs)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
stp = ":#{st_port}" if st_port
|
23
|
+
edp = ":#{ed_port}" if ed_port
|
24
|
+
"#{st}#{stp} -> #{ed}#{edp}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def nodes
|
28
|
+
[st, ed]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def parse_id(id)
|
33
|
+
st, ed, seq = "#{id}".split('_')
|
34
|
+
st, st_port = st.split(':').map(&:intern)
|
35
|
+
ed, ed_port = ed.split(':').map(&:intern)
|
36
|
+
id = (seq ? [st, ed, seq] : [st, ed]).join('_').intern
|
37
|
+
[id, st, ed, seq.to_i, st_port, ed_port]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :gnode_attrs, :gedge_attrs, :graph_attrs, :subgraphs, :ranks
|
42
|
+
def initialize(name=:G, type=:digraph)
|
43
|
+
@name, @type = name, type
|
44
|
+
@edges = {}
|
45
|
+
@nodes = {}
|
46
|
+
@gnode_attrs = {}
|
47
|
+
@gedge_attrs = {}
|
48
|
+
@graph_attrs = {}
|
49
|
+
@subgraphs = []
|
50
|
+
@ranks = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def nodeset
|
54
|
+
@nodes.values
|
55
|
+
end
|
56
|
+
|
57
|
+
def edgeset
|
58
|
+
@edges.values
|
59
|
+
end
|
60
|
+
|
61
|
+
def node(id, attrs={})
|
62
|
+
raise ArgumentError, '`id` must a symbol' unless id.is_a?(Symbol)
|
63
|
+
Node[id, attrs].tap do |node|
|
64
|
+
if exist = @nodes[node.id]
|
65
|
+
exist.attrs.update(node.attrs)
|
66
|
+
else
|
67
|
+
@nodes.update(node.id => node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def edge(id, attrs={})
|
73
|
+
unless id.match(/._./)
|
74
|
+
raise ArgumentError, '`id` must a symbol in which node names joined with "_"'
|
75
|
+
end
|
76
|
+
Edge[id.intern, attrs].tap do |edge|
|
77
|
+
if exist = @edges[id.intern]
|
78
|
+
exist.attrs.update(edge.attrs)
|
79
|
+
else
|
80
|
+
@edges.update(edge.id => edge)
|
81
|
+
create_nodes
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def nodes(attrs)
|
87
|
+
@gnode_attrs.update(attrs)
|
88
|
+
end
|
89
|
+
|
90
|
+
def edges(attrs)
|
91
|
+
@gedge_attrs.update(attrs)
|
92
|
+
end
|
93
|
+
|
94
|
+
def global(attrs)
|
95
|
+
@graph_attrs.update(attrs)
|
96
|
+
end
|
97
|
+
|
98
|
+
def subgraph(&blk)
|
99
|
+
Gviz.new("cluster#{subgraphs.size}", :subgraph).tap do |graph|
|
100
|
+
subgraphs << graph
|
101
|
+
graph.instance_eval &blk
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def graph(&blk)
|
106
|
+
instance_eval(&blk)
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def add(*nodes_or_routes)
|
111
|
+
nodes_or_routes.each do |unit|
|
112
|
+
case unit
|
113
|
+
when Hash
|
114
|
+
unit.each do |sts, eds|
|
115
|
+
Array(sts).product(Array(eds))
|
116
|
+
.each { |st, ed| edge([st,ed].join('_').intern) }
|
117
|
+
end
|
118
|
+
when Symbol
|
119
|
+
node(unit)
|
120
|
+
else
|
121
|
+
raise ArgumentError, 'pass nodes in symbol or edges in hash'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
self
|
125
|
+
end
|
126
|
+
alias :route :add
|
127
|
+
|
128
|
+
def rank(type, *nodes)
|
129
|
+
types = [:same, :min, :max, :source, :sink]
|
130
|
+
unless types.include?(type)
|
131
|
+
raise ArgumentError, "type must match any of #{types.join(', ')}"
|
132
|
+
end
|
133
|
+
@ranks << [type, nodes]
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
result = []
|
138
|
+
tabs = " "
|
139
|
+
result << "#{@type} #{@name} {"
|
140
|
+
|
141
|
+
unless graph_attrs.empty?
|
142
|
+
result << tabs + build_attrs(graph_attrs, false).join(";\n#{tabs}") + ";"
|
143
|
+
end
|
144
|
+
|
145
|
+
unless gnode_attrs.empty?
|
146
|
+
result << tabs + "node#{build_attrs(gnode_attrs)};"
|
147
|
+
end
|
148
|
+
|
149
|
+
unless gedge_attrs.empty?
|
150
|
+
result << tabs + "edge#{build_attrs(gedge_attrs)};"
|
151
|
+
end
|
152
|
+
|
153
|
+
@nodes.values.each do |node|
|
154
|
+
result << tabs + "#{node.id}#{build_attrs(node.attrs)};"
|
155
|
+
end
|
156
|
+
|
157
|
+
@edges.values.each do |edge|
|
158
|
+
result << tabs + "#{edge}#{build_attrs(edge.attrs)};"
|
159
|
+
end
|
160
|
+
|
161
|
+
subgraphs.each do |graph|
|
162
|
+
graph.to_s.lines do |line|
|
163
|
+
result << tabs + line.chomp
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
@ranks.each do |type, nodes|
|
168
|
+
result << tabs + "{ rank=#{type}; #{nodes.join('; ')}; }"
|
169
|
+
end
|
170
|
+
|
171
|
+
result << "}\n"
|
172
|
+
result.join("\n")
|
173
|
+
end
|
174
|
+
|
175
|
+
def save(path, type=nil)
|
176
|
+
File.open("#{path}.dot", "w") { |f| f.puts self }
|
177
|
+
system "dot -T#{type} #{path}.dot -o #{path}.#{type}" if type
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
def create_nodes
|
182
|
+
# only create unregistered nodes
|
183
|
+
ids = @edges.values.flat_map(&:nodes).uniq - @nodes.keys
|
184
|
+
ids.each { |id| node(id) }
|
185
|
+
@nodes
|
186
|
+
end
|
187
|
+
|
188
|
+
def build_attrs(attrs, join=true)
|
189
|
+
return nil if attrs.empty?
|
190
|
+
arr = attrs.map { |k, v| %(#{k}="#{v}").gsub("\n", "\\n") }
|
191
|
+
join ? '[' + arr.join(',') + ']' : arr
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if __FILE__ == $0
|
196
|
+
g = Gviz.new
|
197
|
+
g.graph do
|
198
|
+
add(:main => [:printf, :parse, :init, :cleanup])
|
199
|
+
add(:parse => :execute, :init => :make)
|
200
|
+
add(:execute => [:printf, :compare, :make])
|
201
|
+
node(:main, :shape => 'box')
|
202
|
+
node(:make, :label => "make a\nstring")
|
203
|
+
node(:compare, :shape => 'box', :style => 'filled', :color => ".7 .3 1.0")
|
204
|
+
edge(:main_parse, :weight => 8)
|
205
|
+
edge(:main_init, :style => 'dotted')
|
206
|
+
edge(:main_printf, :style => 'bold', :label => "100 times", :color => 'red')
|
207
|
+
edge(:execute_compare, :color => 'red')
|
208
|
+
end
|
209
|
+
puts g
|
210
|
+
end
|
211
|
+
|