GraphvizR 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/README.txt +0 -2
- data/lib/graphviz_r.rb +134 -60
- data/test/test_graphviz_r.rb +68 -2
- metadata +5 -5
data/History.txt
CHANGED
data/README.txt
CHANGED
data/lib/graphviz_r.rb
CHANGED
@@ -33,7 +33,7 @@
|
|
33
33
|
# gvr = GraphvizR.new 'sample'
|
34
34
|
# gvr.alpha >> gvr.beta
|
35
35
|
# generates
|
36
|
-
#
|
36
|
+
# digraph sample {
|
37
37
|
# alpla -> beta
|
38
38
|
# }
|
39
39
|
# while
|
@@ -44,6 +44,28 @@
|
|
44
44
|
# alpla -- beta
|
45
45
|
# }
|
46
46
|
#
|
47
|
+
# == Grouping Nodes
|
48
|
+
#
|
49
|
+
# When one node is the root of some nodes, you can aggregate the children by using an array.
|
50
|
+
# For example,
|
51
|
+
# gvr = GraphvizR.new 'sample'
|
52
|
+
# gvr.alpha >> [gvr.beta, gvr.gamma, gvr.delta]
|
53
|
+
# generates
|
54
|
+
# digraph sample {
|
55
|
+
# alpha -> {beta; gamma; delta;}
|
56
|
+
# }
|
57
|
+
#
|
58
|
+
# == Consective Edges
|
59
|
+
#
|
60
|
+
# You can write:
|
61
|
+
# gvr = GraphvizR.new 'sample'
|
62
|
+
# gvr.alpha >> gvr.beta >> gvr.gamma >> gvr.delta
|
63
|
+
# instead of doing:
|
64
|
+
# gvr = GraphvizR.new 'sample'
|
65
|
+
# gvr.alpha >> gvr.beta
|
66
|
+
# gvr.beta >> gvr.gamma
|
67
|
+
# gvr.gamma >> gvr.delta
|
68
|
+
#
|
47
69
|
# == Graph Attributes
|
48
70
|
#
|
49
71
|
# Attributes are specified by Hash in []. Thus, to set the fillcolor of a node abc, one would use
|
@@ -74,10 +96,17 @@
|
|
74
96
|
# (gvr.node2 >> gvr.node1(:p_right)) [:label => 'record']
|
75
97
|
# gvr.to_dot
|
76
98
|
#
|
77
|
-
# ==
|
99
|
+
# == Rank
|
100
|
+
#
|
101
|
+
# Ranks of nodes can be set as the same value for other nodes.
|
102
|
+
# gvr.rank :same, [gvr.a, gvr.b, gvr.c]
|
103
|
+
# means node a, b, and c has same rank value and generages:
|
104
|
+
# {rank = same; a; b; c;};
|
78
105
|
#
|
79
|
-
#
|
80
|
-
#
|
106
|
+
# == Clusters
|
107
|
+
#
|
108
|
+
# Cluster is a way to construct hierarchical graph in graphviz. GraphvizR allows you to use
|
109
|
+
# clusters by means of method calling with a block which has one argument. For example,
|
81
110
|
# gvr = GraphvizR.new 'sample'
|
82
111
|
# gvr.cluster0 do |c0|
|
83
112
|
# c0.graph [:color => :blue, :label => 'area 0', :style => :bold]
|
@@ -112,7 +141,7 @@
|
|
112
141
|
# c -> f [lhead = cluster1];
|
113
142
|
#
|
114
143
|
class GraphvizR
|
115
|
-
VERSION = '0.
|
144
|
+
VERSION = '0.4.0'
|
116
145
|
INDENT_UNIT = ' '
|
117
146
|
|
118
147
|
# This represents graphviz attributes.
|
@@ -122,20 +151,20 @@ class GraphvizR
|
|
122
151
|
@dot = dot
|
123
152
|
end
|
124
153
|
|
125
|
-
def [](
|
126
|
-
@dot.send :"#{@name}=",
|
154
|
+
def [](attr)
|
155
|
+
@dot.send :"#{@name}=", attr
|
127
156
|
end
|
128
157
|
end
|
129
158
|
|
130
159
|
# This represents graphviz node.
|
131
160
|
class Node
|
132
|
-
attr_reader :
|
161
|
+
attr_reader :attr, :name
|
133
162
|
|
134
163
|
def initialize(name, port, dot)
|
135
164
|
@name = name
|
136
165
|
@port = port || []
|
137
166
|
@dot = dot
|
138
|
-
@
|
167
|
+
@attr = {}
|
139
168
|
|
140
169
|
if !@port.empty? and @port[0].is_a? Array and @port[0][0].is_a? Hash
|
141
170
|
@dot[@name] = @port[0][0]
|
@@ -155,15 +184,21 @@ class GraphvizR
|
|
155
184
|
end
|
156
185
|
|
157
186
|
def add_edge_on_dot(node)
|
158
|
-
|
159
|
-
|
187
|
+
attr = nil
|
188
|
+
if node.is_a? Array
|
189
|
+
node = NodeGroup.new node
|
190
|
+
attr = @attr
|
191
|
+
else
|
192
|
+
attr = @attr.merge node.attr
|
193
|
+
end
|
194
|
+
edge = Edge.new([self, node], @dot, attr)
|
160
195
|
@dot.add_edge edge
|
161
196
|
edge
|
162
197
|
end
|
163
198
|
|
164
|
-
def [](
|
165
|
-
@dot[@name] =
|
166
|
-
@
|
199
|
+
def [](attr)
|
200
|
+
@dot[@name] = attr
|
201
|
+
@attr.merge! attr
|
167
202
|
self
|
168
203
|
end
|
169
204
|
|
@@ -181,18 +216,43 @@ class GraphvizR
|
|
181
216
|
name.map{|e| e.to_dot}.join(':')
|
182
217
|
end
|
183
218
|
end
|
219
|
+
|
220
|
+
# Aggregate some nodes.
|
221
|
+
class NodeGroup
|
222
|
+
def initialize(nodes, attr={})
|
223
|
+
@nodes = nodes
|
224
|
+
@attributes = attr
|
225
|
+
end
|
226
|
+
|
227
|
+
def to_sym
|
228
|
+
to_dot.to_sym
|
229
|
+
end
|
230
|
+
|
231
|
+
def to_dot
|
232
|
+
dot = "{"
|
233
|
+
dot += @attributes.to_a.map{|e| "#{e[0]} = #{e[1]};"}.join(' ') unless @attributes.empty?
|
234
|
+
dot += ' ' unless @attributes.empty?
|
235
|
+
dot += @nodes.map{|e| "#{e.to_dot};"}.join(' ')
|
236
|
+
dot += "}"
|
237
|
+
end
|
238
|
+
end
|
184
239
|
|
185
240
|
# This represents a graphviz edge.
|
186
241
|
class Edge
|
187
|
-
def initialize(
|
188
|
-
@
|
189
|
-
@to = to
|
242
|
+
def initialize(nodes, dot, attr={})
|
243
|
+
@nodes = nodes
|
190
244
|
@dot = dot
|
191
|
-
@
|
245
|
+
@attribute = attr
|
192
246
|
end
|
193
|
-
|
194
|
-
def [](
|
195
|
-
@
|
247
|
+
|
248
|
+
def [](attr)
|
249
|
+
@attribute.merge! attr
|
250
|
+
end
|
251
|
+
|
252
|
+
def >>(node, attr={})
|
253
|
+
@nodes << node
|
254
|
+
self[attr]
|
255
|
+
self
|
196
256
|
end
|
197
257
|
|
198
258
|
def node_to_dot(node)
|
@@ -200,8 +260,8 @@ class GraphvizR
|
|
200
260
|
end
|
201
261
|
|
202
262
|
def to_dot
|
203
|
-
dot =
|
204
|
-
dot += " #{@
|
263
|
+
dot = @nodes.map{|e| node_to_dot e}.join " #{@dot.arrow} "
|
264
|
+
dot += " #{@attribute.to_dot}" unless @attribute.empty?
|
205
265
|
dot
|
206
266
|
end
|
207
267
|
|
@@ -210,20 +270,20 @@ class GraphvizR
|
|
210
270
|
end
|
211
271
|
end
|
212
272
|
|
213
|
-
# This returns dot formatted string from given
|
273
|
+
# This returns dot formatted string from given attributes. To know more about <tt>attr</tt>,
|
214
274
|
# please see GraphvizR.new .
|
215
|
-
def self.dot(name,
|
216
|
-
GraphvizR.new(name,
|
275
|
+
def self.dot(name, attr)
|
276
|
+
GraphvizR.new(name, attr).to_dot
|
217
277
|
end
|
218
278
|
|
219
279
|
# This initialzes a GraphvizR instance.
|
220
280
|
# +name+:: the name of the graph
|
221
|
-
# +
|
281
|
+
# +attr+:: <b>!deprecated!</b> all attributes for a graph can be given as a hash instance.
|
222
282
|
# Please guess the contents of the hash from the test or examples below:
|
223
283
|
# gvr = GraphvizR.dot(:sample,
|
224
284
|
# :graph => {:size => "1.5, 2.5"},
|
225
285
|
# :label => 'example',
|
226
|
-
# :
|
286
|
+
# :node_attributes => {
|
227
287
|
# :beta => {:shape => :box},
|
228
288
|
# },
|
229
289
|
# :alpha => :beta,
|
@@ -235,7 +295,7 @@ class GraphvizR
|
|
235
295
|
# gvr = GraphvizR.dot(:sample,
|
236
296
|
# :graph => {:size => "1.5, 2.5"},
|
237
297
|
# :node => {:shape => :record},
|
238
|
-
# :
|
298
|
+
# :node_attributes => {
|
239
299
|
# :node1 => {:label => "<p_left> left|<p_center>center|<p_right> right"},
|
240
300
|
# :node2 => {:label => "left|center|right"}
|
241
301
|
# },
|
@@ -269,34 +329,38 @@ class GraphvizR
|
|
269
329
|
# )
|
270
330
|
# +parent+:: a parent graph is given when this graph is a subgraph.
|
271
331
|
# +indent+:: indent level when this instance is converted to rdot.
|
272
|
-
def initialize(name,
|
332
|
+
def initialize(name, attr={}, parent=nil, indent=0)
|
273
333
|
if name.is_a? Array
|
274
334
|
initialize(*name)
|
275
335
|
else
|
276
336
|
@name = name
|
337
|
+
@default_graph_attributes = {}
|
338
|
+
@default_node_attributes = {}
|
339
|
+
@default_edge_attributes = {}
|
340
|
+
@node_attributes = {}
|
277
341
|
@edges = []
|
278
|
-
@default_node_properties = {}
|
279
|
-
@node_properties = {}
|
280
|
-
@graph_properties = {}
|
281
342
|
@subgraphs = []
|
343
|
+
@node_groups = []
|
282
344
|
@indent = indent + 1
|
283
345
|
@parent = parent
|
284
346
|
@directed = true
|
285
|
-
interprete
|
347
|
+
interprete attr
|
286
348
|
end
|
287
349
|
end
|
288
350
|
|
289
|
-
def interprete(
|
290
|
-
|
351
|
+
def interprete(attr) #:nodoc:
|
352
|
+
attr.each do |k, v|
|
291
353
|
case k
|
292
|
-
when :
|
293
|
-
|
354
|
+
when :node_attribute, :node_attributes
|
355
|
+
add_node_attributes v
|
294
356
|
when :graph
|
295
|
-
@
|
357
|
+
@default_graph_attributes.merge! v
|
296
358
|
when :label, :size, :color, :fillcolor, :style
|
297
|
-
@
|
359
|
+
@default_graph_attributes[k] = v
|
298
360
|
when :node
|
299
|
-
@
|
361
|
+
@default_node_attributes[k] = v
|
362
|
+
when :edge
|
363
|
+
@default_edge_attributes[k] = v
|
300
364
|
when :subgraphs
|
301
365
|
add_subgraph(v)
|
302
366
|
else
|
@@ -305,9 +369,9 @@ class GraphvizR
|
|
305
369
|
end
|
306
370
|
end
|
307
371
|
|
308
|
-
# add
|
309
|
-
def
|
310
|
-
@
|
372
|
+
# add attributes for nodes
|
373
|
+
def add_node_attributes(attrs)
|
374
|
+
@node_attributes = attrs
|
311
375
|
end
|
312
376
|
|
313
377
|
# add an edge to the graph
|
@@ -316,19 +380,19 @@ class GraphvizR
|
|
316
380
|
f = from.keys[0]
|
317
381
|
t = from[f]
|
318
382
|
p = to
|
319
|
-
@edges << Edge.new(f, t, self, p)
|
383
|
+
@edges << Edge.new([f, t], self, p)
|
320
384
|
elsif to.nil?
|
321
385
|
# from is Edge instance
|
322
386
|
@edges << from
|
323
387
|
else
|
324
|
-
@edges << Edge.new(from, to, self)
|
388
|
+
@edges << Edge.new([from, to], self)
|
325
389
|
end
|
326
390
|
end
|
327
391
|
|
328
392
|
# add subgraph
|
329
393
|
def add_subgraph(graphs)
|
330
|
-
graphs.to_a.sort.each do |
|
331
|
-
@subgraphs << self.class.new(
|
394
|
+
graphs.to_a.sort.each do |key_attr|
|
395
|
+
@subgraphs << self.class.new(key_attr[0], key_attr[1], self, @indent)
|
332
396
|
end
|
333
397
|
end
|
334
398
|
|
@@ -373,6 +437,10 @@ class GraphvizR
|
|
373
437
|
end
|
374
438
|
end
|
375
439
|
|
440
|
+
def arrow
|
441
|
+
directed? ? '->' : '--'
|
442
|
+
end
|
443
|
+
|
376
444
|
# When block is given, create new subgraph. Otherwise, create new node.
|
377
445
|
def [](key, *args, &block)
|
378
446
|
if block
|
@@ -384,18 +452,18 @@ class GraphvizR
|
|
384
452
|
end
|
385
453
|
end
|
386
454
|
|
387
|
-
# set
|
455
|
+
# set attributes for a node
|
388
456
|
def []=(key, *args, &block)
|
389
|
-
@
|
457
|
+
@node_attributes[key] = args[0]
|
390
458
|
end
|
391
459
|
|
392
460
|
[:label, :size, :color, :fillcolor, :style].each do |method|
|
393
461
|
define_method :"#{method}=" do |v|
|
394
|
-
@
|
462
|
+
@default_graph_attributes[method] = v
|
395
463
|
end
|
396
464
|
end
|
397
465
|
|
398
|
-
# set graph
|
466
|
+
# set graph attributes
|
399
467
|
def graph(*args)
|
400
468
|
if args.empty?
|
401
469
|
Attributes.new :graph, self
|
@@ -404,20 +472,26 @@ class GraphvizR
|
|
404
472
|
end
|
405
473
|
end
|
406
474
|
|
407
|
-
# set graph
|
408
|
-
def graph=(
|
409
|
-
@
|
475
|
+
# set graph attributes
|
476
|
+
def graph=(attr)
|
477
|
+
@default_graph_attributes.merge! attr
|
478
|
+
end
|
479
|
+
|
480
|
+
def rank(same, nodes)
|
481
|
+
@node_groups << (NodeGroup.new nodes, {:rank => same})
|
410
482
|
end
|
411
483
|
|
412
484
|
# convert this instance to dot
|
413
485
|
def to_dot
|
414
486
|
graph_type = if subgraph?; 'subgraph'elsif directed?; 'digraph' else 'graph' end
|
415
487
|
dot = indent_enter("#{graph_type} #{@name} {", true)
|
416
|
-
dot += indent_enter("graph #{@
|
488
|
+
dot += indent_enter("graph #{@default_graph_attributes.to_dot};") unless @default_graph_attributes.empty?
|
417
489
|
dot += @subgraphs.map{|e| e.to_dot}.join('') unless @subgraphs.empty?
|
418
|
-
dot +=
|
419
|
-
dot +=
|
490
|
+
dot += attributes_to_dot(@default_node_attributes) unless @default_node_attributes.empty?
|
491
|
+
dot += attributes_to_dot(@default_edge_attributes) unless @default_edge_attributes.empty?
|
492
|
+
dot += attributes_to_dot(@node_attributes) unless @node_attributes.empty?
|
420
493
|
dot += @edges.sort.map{|e| indent_enter("#{e.to_dot};")}.join("")
|
494
|
+
dot += @node_groups.map{|e| indent_enter("#{e.to_dot};")}.join("")
|
421
495
|
dot += indent_enter("}", true)
|
422
496
|
dot
|
423
497
|
end
|
@@ -466,12 +540,12 @@ class GraphvizR
|
|
466
540
|
|
467
541
|
protected
|
468
542
|
|
469
|
-
def
|
543
|
+
def attributes_to_dot(hash) #:nodoc:
|
470
544
|
hash.to_a.sort.map{|e| indent_enter "#{e[0]} #{e[1].to_dot};"}.join()
|
471
545
|
end
|
472
546
|
end
|
473
547
|
|
474
|
-
class
|
548
|
+
class Numeric #:nodoc:
|
475
549
|
def to_dot
|
476
550
|
to_s
|
477
551
|
end
|
data/test/test_graphviz_r.rb
CHANGED
@@ -20,7 +20,7 @@ class TestGraphvizR < Test::Unit::TestCase
|
|
20
20
|
gvr = GraphvizR.dot(:sample,
|
21
21
|
:graph => {:size => "1.5, 2.5"},
|
22
22
|
:label => 'example',
|
23
|
-
:
|
23
|
+
:node_attributes => {
|
24
24
|
:beta => {:shape => :box},
|
25
25
|
},
|
26
26
|
:alpha => :beta,
|
@@ -70,7 +70,8 @@ digraph sample {
|
|
70
70
|
gvr = GraphvizR.dot(:sample,
|
71
71
|
:graph => {:size => "1.5, 2.5"},
|
72
72
|
:node => {:shape => :record},
|
73
|
-
:
|
73
|
+
:edge => {:fontsize => 24},
|
74
|
+
:node_attributes => {
|
74
75
|
:node1 => {:label => "<p_left> left|<p_center>center|<p_right> right"},
|
75
76
|
:node2 => {:label => "left|center|right"}
|
76
77
|
},
|
@@ -84,6 +85,7 @@ digraph sample {
|
|
84
85
|
digraph sample {
|
85
86
|
graph [size = "1.5, 2.5"];
|
86
87
|
node [shape = record];
|
88
|
+
edge [fontsize = 24];
|
87
89
|
node1 [label = "<p_left> left|<p_center>center|<p_right> right"];
|
88
90
|
node2 [label = "left|center|right"];
|
89
91
|
node1 -> node2;
|
@@ -296,6 +298,70 @@ digraph sample {
|
|
296
298
|
assert_equal <<-end_of_string, gvr.data(:dot)
|
297
299
|
digraph sample {
|
298
300
|
"alpha" -> "beta" [label = "label1"];
|
301
|
+
}
|
302
|
+
end_of_string
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_consective_edges
|
306
|
+
gvr = GraphvizR.new 'sample'
|
307
|
+
gvr.alpha >> gvr.beta
|
308
|
+
gvr.alpha >> gvr.beta >> gvr.gamma
|
309
|
+
gvr.alpha >> gvr.beta >> gvr.gamma >> gvr.delta
|
310
|
+
|
311
|
+
assert_equal <<-end_of_string, gvr.to_dot
|
312
|
+
digraph sample {
|
313
|
+
alpha -> beta;
|
314
|
+
alpha -> beta -> gamma;
|
315
|
+
alpha -> beta -> gamma -> delta;
|
316
|
+
}
|
317
|
+
end_of_string
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_node_group
|
321
|
+
gvr = GraphvizR.new 'sample'
|
322
|
+
# gvr.a >> gvr.grouping(){gvr.b, gvr.c, gvr.d}
|
323
|
+
# gvr.aa >> gvr.grouping(gvr.bb, gvr.cc, gvr.dd)
|
324
|
+
# a -> {b c d};
|
325
|
+
# aa -> {bb cc dd};
|
326
|
+
gvr.aaa >> [gvr.bbb, gvr.ccc, gvr.ddd]
|
327
|
+
|
328
|
+
assert_equal <<-end_of_string, gvr.to_dot
|
329
|
+
digraph sample {
|
330
|
+
aaa -> {bbb; ccc; ddd;};
|
331
|
+
}
|
332
|
+
end_of_string
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_rank
|
336
|
+
gvr = GraphvizR.new 'sample'
|
337
|
+
gvr.graph [:size => "3, 3"]
|
338
|
+
gvr.Level_1 >> gvr.Level_2 >> gvr.Level_3
|
339
|
+
gvr.a >> gvr.b
|
340
|
+
gvr.a >> gvr.c
|
341
|
+
gvr.a >> gvr.d
|
342
|
+
gvr.b >> gvr.c
|
343
|
+
gvr.b >> gvr.e
|
344
|
+
gvr.d >> gvr.e
|
345
|
+
gvr.d >> gvr.f
|
346
|
+
#gvr.grouping(:rank => :same) {gvr.Level_1; gvr.a}
|
347
|
+
gvr.rank :same, [gvr.Level_1, gvr.a]
|
348
|
+
gvr.rank :same, [gvr.Level_2, gvr.c, gvr.b, gvr.d]
|
349
|
+
gvr.rank :same, [gvr.Level_3, gvr.e, gvr.f]
|
350
|
+
|
351
|
+
assert_equal <<-end_of_string, gvr.to_dot
|
352
|
+
digraph sample {
|
353
|
+
graph [size = "3, 3"];
|
354
|
+
Level_1 -> Level_2 -> Level_3;
|
355
|
+
a -> b;
|
356
|
+
a -> c;
|
357
|
+
a -> d;
|
358
|
+
b -> c;
|
359
|
+
b -> e;
|
360
|
+
d -> e;
|
361
|
+
d -> f;
|
362
|
+
{rank = same; Level_1; a;};
|
363
|
+
{rank = same; Level_2; c; b; d;};
|
364
|
+
{rank = same; Level_3; e; f;};
|
299
365
|
}
|
300
366
|
end_of_string
|
301
367
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: GraphvizR
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-02-
|
6
|
+
version: 0.4.0
|
7
|
+
date: 2007-02-26 00:00:00 +09:00
|
8
8
|
summary: Graphviz wrapper for Ruby and Rails
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -35,8 +35,8 @@ description: "Graphviz wrapper for Ruby. This can be used as a common library, a
|
|
35
35
|
node1 [:label => @label1] node2 [:label => @label2] node1 >> node2
|
36
36
|
node1(:p_left) >> node2 node2 >> node1(:p_center) (node2 >> node1(:p_right))
|
37
37
|
[:label => 'record'] == DEPENDENCIES: * Graphviz (http://www.graphviz.org) ==
|
38
|
-
TODO:
|
39
|
-
|
38
|
+
TODO: == INSTALL: * sudo gem install graphviz_r * if you want to use this in
|
39
|
+
ruby on rails * script/plugin install
|
40
40
|
http://technohippy.net/svn/repos/graphviz_r/trunk/vendor/plugins/rdot ==
|
41
41
|
LICENSE: (The MIT License)"
|
42
42
|
autorequire:
|
@@ -92,5 +92,5 @@ dependencies:
|
|
92
92
|
-
|
93
93
|
- ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: 1.
|
95
|
+
version: 1.2.0
|
96
96
|
version:
|