gviz 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,7 @@
2
2
  class Gviz
3
3
  end
4
4
 
5
+ # +Graph+ fuction is a shortcut method of `Gviz#graph`.
5
6
  def Graph(name=:G, type=:digraph, &blk)
6
7
  Gviz.new(name, type).graph(&blk)
7
8
  end
@@ -11,16 +11,22 @@ class Gviz
11
11
  @ranks = []
12
12
  end
13
13
 
14
+ # Access to all defined node objects.
14
15
  def nodeset
15
16
  @nodes.values
16
17
  end
17
18
 
19
+ # Access to all defined edge objects.
18
20
  def edgeset
19
21
  @edges.values
20
22
  end
21
23
 
24
+ # Define a node or update a node attributes.
25
+ # The first argument is `id` of the node which must a symbol form.
26
+ #
27
+ # node(:a, color:'red', shape:'circle')
28
+ #
22
29
  def node(id, attrs={})
23
- raise ArgumentError, '`id` must a symbol' unless id.is_a?(Symbol)
24
30
  Node[id, attrs].tap do |node|
25
31
  if exist = @nodes[node.id]
26
32
  exist.attrs.update(node.attrs)
@@ -30,11 +36,29 @@ class Gviz
30
36
  end
31
37
  end
32
38
 
39
+ # Difine a edge or update a edge attributes.
40
+ # The first argument is `id` of the edge, which is a symbol or string constructed
41
+ # from two nodes joined with '_'(underscore).
42
+ #
43
+ # edge(:a_b, color:'red')
44
+ #
45
+ # The corresponding nodes will be defined if these are not exist.
46
+ #
47
+ # When `id` includes '*'(asterisk), multiple edges are updated.
48
+ #
49
+ # add(:a => [:b, :c])
50
+ # edge(:a_*, arrowhead:'none')
51
+ #
52
+ # is equivalent to:
53
+ #
54
+ # edge(:a_b, arrowhead:'none')
55
+ # edge(:a_c, arrowhead:'none')
33
56
  def edge(id, attrs={})
34
- unless id.match(/._./)
35
- raise ArgumentError, '`id` must a symbol in which node names joined with "_"'
57
+ if md = id.match(/\*/)
58
+ return multiple_edge(md, attrs)
36
59
  end
37
- Edge[id.intern, attrs].tap do |edge|
60
+
61
+ Edge[id, attrs].tap do |edge|
38
62
  if exist = @edges[id.intern]
39
63
  exist.attrs.update(edge.attrs)
40
64
  else
@@ -44,18 +68,28 @@ class Gviz
44
68
  end
45
69
  end
46
70
 
71
+ # Define all nodes attributes.
47
72
  def nodes(attrs)
48
73
  @gnode_attrs.update(attrs)
49
74
  end
50
75
 
76
+ # Define all edges attributes.
51
77
  def edges(attrs)
52
78
  @gedge_attrs.update(attrs)
53
79
  end
54
80
 
81
+ # Define graph global attributes.
55
82
  def global(attrs)
56
83
  @graph_attrs.update(attrs)
57
84
  end
58
85
 
86
+ # Define subgraph.
87
+ #
88
+ # subgraph do
89
+ # global label:sub1
90
+ # add :a => :b
91
+ # end
92
+ #
59
93
  def subgraph(&blk)
60
94
  Gviz.new("cluster#{subgraphs.size}", :subgraph).tap do |graph|
61
95
  subgraphs << graph
@@ -63,18 +97,52 @@ class Gviz
63
97
  end
64
98
  end
65
99
 
100
+ # +graph+ is a shorcut method.
101
+ #
102
+ # gv = Gviz.new
103
+ # gv.graph do
104
+ # add :a => :b
105
+ # node :a, color:'red'
106
+ # end
107
+ #
108
+ # is equivalent to:
109
+ #
110
+ # gv = Gviz.new
111
+ # gv.add :a => :b
112
+ # gv.node :a, color:'red'
113
+ #
66
114
  def graph(&blk)
67
115
  instance_eval(&blk)
68
116
  self
69
117
  end
70
118
 
119
+ # Define nodes or routes(node-edge combinaitons).
120
+ # When an argument is a symbol, a node is defined.
121
+ #
122
+ # add :a, :b
123
+ #
124
+ # is equivalent to:
125
+ #
126
+ # node :a
127
+ # node :b
128
+ #
129
+ # When an argument is a hash, edges are defined.
130
+ #
131
+ # add :a => [:b, :c], :c => :d
132
+ #
133
+ # is equivalent to:
134
+ #
135
+ # edge :a_b
136
+ # edge :a_c
137
+ # edge :c_d
138
+ #
71
139
  def add(*nodes_or_routes)
72
140
  nodes_or_routes.each do |unit|
73
141
  case unit
74
142
  when Hash
75
143
  unit.each do |sts, eds|
76
144
  Array(sts).product(Array(eds))
77
- .each { |st, ed| edge([st,ed].join('_').intern) }
145
+ .each { |st, ed| edge "#{st}_#{ed}" }
78
146
  end
79
147
  when Symbol
80
148
  node(unit)
@@ -86,6 +154,11 @@ class Gviz
86
154
  end
87
155
  alias :route :add
88
156
 
157
+ # Define a rank to nodes.
158
+ # :same, :min, :max, :source and :sink are acceptable types.
159
+ #
160
+ # rank(:same, :a, :b)
161
+ #
89
162
  def rank(type, *nodes)
90
163
  types = [:same, :min, :max, :source, :sink]
91
164
  unless types.include?(type)
@@ -133,6 +206,8 @@ class Gviz
133
206
  result.join("\n")
134
207
  end
135
208
 
209
+ # Create a graphviz dot file. When an image type is specified,
210
+ # the image is also created.
136
211
  def save(path, type=nil)
137
212
  File.open("#{path}.dot", "w") { |f| f.puts self }
138
213
  system "dot -T#{type} #{path}.dot -o #{path}.#{type}" if type
@@ -151,4 +226,10 @@ class Gviz
151
226
  arr = attrs.map { |k, v| %(#{k}="#{v}").gsub("\n", "\\n") }
152
227
  join ? '[' + arr.join(',') + ']' : arr
153
228
  end
229
+
230
+ def multiple_edge(md, attrs)
231
+ st = [md.pre_match, md.post_match].detect { |e| !e.empty? }.delete('_')
232
+ edges = edgeset.select { |edge| edge.nodes.include? st.intern }
233
+ edges.each { |eg| edge eg.id, attrs }
234
+ end
154
235
  end
@@ -1,7 +1,7 @@
1
1
  class Gviz::Edge < Struct.new(:id, :attrs)
2
2
  attr_reader :st, :ed, :seq, :st_port, :ed_port
3
3
  def initialize(id, attrs={})
4
- raise ArgumentError, "`attrs` must a hash" unless attrs.is_a?(Hash)
4
+ raise ArgumentError, "edge `attrs` must a hash" unless attrs.is_a?(Hash)
5
5
  id, @st, @ed, @seq, @st_port, @ed_port = parse_id(id)
6
6
  super(id, attrs)
7
7
  end
@@ -18,11 +18,16 @@ class Gviz::Edge < Struct.new(:id, :attrs)
18
18
 
19
19
  private
20
20
  def parse_id(id)
21
- raise ArgumentError, "id must not include other than words or a colon" if id.match(/[^\w:]/)
21
+ unless id.match(/._./)
22
+ raise ArgumentError, 'edge `id` must a symbol in which node names joined with "_"'
23
+ end
24
+ if id.match(/[^\d\w:-]/)
25
+ raise ArgumentError, "edge `id` must not include other than ASCII words, colon or minus"
26
+ end
22
27
  st, ed, seq = "#{id}".split('_')
23
28
  st, st_port = st.split(':').map(&:intern)
24
29
  ed, ed_port = ed.split(':').map(&:intern)
25
- id = (seq ? [st, ed, seq] : [st, ed]).join('_').intern
30
+ id = seq ? :"#{st}_#{ed}_#{seq}" : :"#{st}_#{ed}"
26
31
  [id, st, ed, seq.to_i, st_port, ed_port]
27
32
  end
28
33
  end
@@ -1,6 +1,7 @@
1
1
  class Gviz::Node < Struct.new(:id, :attrs)
2
2
  def initialize(id, attrs={})
3
- raise ArgumentError, "`id` must not include underscores" if id.match(/_/)
3
+ raise ArgumentError, 'node `id` must a symbol' unless id.is_a?(Symbol)
4
+ raise ArgumentError, "node `id` must not include underscores" if id.match(/_/)
4
5
  super
5
6
  end
6
7
 
@@ -4,3 +4,9 @@ class Numeric
4
4
  unit * (tgr.max - tgr.min) + tgr.min
5
5
  end
6
6
  end
7
+
8
+ class Object
9
+ def to_id
10
+ to_s.hash.to_s.intern
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  class Gviz
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -58,10 +58,30 @@ describe Gviz::Edge do
58
58
  its(:attrs) { should eq opts }
59
59
  end
60
60
 
61
+ context "when a number string passed" do
62
+ subject { Gviz::Edge.new('1234_1235') }
63
+ its(:id) { should be :'1234_1235' }
64
+ its(:st) { should be :'1234' }
65
+ its(:ed) { should be :'1235' }
66
+ end
67
+
68
+ context "when a string with minus sign" do
69
+ subject { Gviz::Edge.new('-123_124') }
70
+ its(:id) { should be :'-123_124' }
71
+ its(:st) { should be :'-123' }
72
+ its(:ed) { should be :'124' }
73
+ end
74
+
61
75
  context "when a string with other than words or colon passed" do
62
76
  it "raise an error" do
63
77
  ->{ Gviz::Edge.new('a!b_c') }.should raise_error(ArgumentError)
64
78
  end
65
79
  end
80
+
81
+ context "when a symbol without underscore passed" do
82
+ it "raise an error" do
83
+ ->{ Gviz::Edge.new(:abc) }.should raise_error(ArgumentError)
84
+ end
85
+ end
66
86
  end
67
87
  end
@@ -10,13 +10,6 @@ describe Gviz::Node do
10
10
  its(:attrs) { should be_empty }
11
11
  end
12
12
 
13
- context "when only a string passed" do
14
- subject { Gviz::Node.new('a') }
15
- it { should be_a_instance_of Gviz::Node }
16
- its(:id) { should eq 'a' }
17
- its(:attrs) { should be_empty }
18
- end
19
-
20
13
  context "when a symbol and hash options passed" do
21
14
  opts = { shape:'circle', style:'filled' }
22
15
  subject { Gviz::Node.new(:a, opts) }
@@ -29,6 +22,12 @@ describe Gviz::Node do
29
22
  ->{ Gviz::Node.new(:a_b) }.should raise_error(ArgumentError)
30
23
  end
31
24
  end
25
+
26
+ context "when a string passed" do
27
+ it "raise an error" do
28
+ ->{ Gviz::Node.new('a') }.should raise_error(ArgumentError)
29
+ end
30
+ end
32
31
  end
33
32
 
34
33
  describe "#to_s" do
@@ -36,6 +36,17 @@ describe Gviz do
36
36
  it { should be { color:'blue', label:'hello', shape:'box' } }
37
37
  end
38
38
 
39
+ context "update node attrs which node is created by add method" do
40
+ before do
41
+ name = 'hello'
42
+ gv.add(:a => name.to_id)
43
+ gv.node(name.to_id, color:'red')
44
+ gv.nodeset.map(&:id)
45
+ end
46
+ subject { gv.nodeset.map(&:attrs) }
47
+ it { should eql [{}, {color:'red'}] }
48
+ end
49
+
39
50
  context "when pass a string" do
40
51
  it "raise an error" do
41
52
  ->{ gv.node('a') }.should raise_error(ArgumentError)
@@ -74,6 +85,25 @@ describe Gviz do
74
85
  subject { gv.edgeset }
75
86
  its(:size) { should eql 2 }
76
87
  end
88
+
89
+ context "when a string with *(asterisk) passed" do
90
+ before do
91
+ gv.add(:a => [:b, :c], :c => :d)
92
+ gv.edge('a_*', color:'red')
93
+ end
94
+ subject { gv.edgeset.map(&:attrs) }
95
+ it { should eql [{ color:'red' }, { color:'red' }, {}] }
96
+ end
97
+
98
+ context "when a string with *(asterisk) passed 2" do
99
+ before do
100
+ gv.add(:main => [:a, :b], :main2 => :sub)
101
+ gv.add(:main => :sub)
102
+ gv.edge('*_main', color:'red')
103
+ end
104
+ subject { gv.edgeset.map(&:attrs) }
105
+ it { should eql [{color:'red'}, {color:'red'}, {}, {color:'red'}] }
106
+ end
77
107
  end
78
108
 
79
109
  describe "#add" do
@@ -1,33 +1,59 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
2
3
 
3
4
  describe Numeric do
4
- context "norm" do
5
- it "normalize a integer into range of 0.0-1.0" do
6
- 5.norm(0..10).should eql 0.5
7
- 2.norm(0..10).should eql 0.2
8
- 0.norm(0..10).should eql 0.0
9
- 10.norm(0..10).should eql 1.0
10
- 5.norm(5..10).should eql 0.0
11
- 10.norm(5..10).should eql 1.0
12
- 7.norm(5..10).should eql 0.4
5
+ describe "#norm" do
6
+ context "normalize into 0.0-1.0" do
7
+ it "works for integer" do
8
+ 5.norm(0..10).should eql 0.5
9
+ 2.norm(0..10).should eql 0.2
10
+ 0.norm(0..10).should eql 0.0
11
+ 10.norm(0..10).should eql 1.0
12
+ 5.norm(5..10).should eql 0.0
13
+ 10.norm(5..10).should eql 1.0
14
+ 7.norm(5..10).should eql 0.4
15
+ end
16
+
17
+ it "works for float" do
18
+ 2.5.norm(0..10).should eql 0.25
19
+ 7.4.norm(0..10).should eql 0.74
20
+ 0.0.norm(0..10).should eql 0.0
21
+ 10.0.norm(0..10).should eql 1.0
22
+ 15.0.norm(10..20).should eql 0.5
23
+ end
13
24
  end
14
-
15
- it "normalize a float into range of 0.0-1.0" do
16
- 2.5.norm(0..10).should eql 0.25
17
- 7.4.norm(0..10).should eql 0.74
18
- 0.0.norm(0..10).should eql 0.0
19
- 10.0.norm(0..10).should eql 1.0
20
- 15.0.norm(10..20).should eql 0.5
25
+
26
+ context "normalize into other than 0.0-1.0" do
27
+ it "works for integer" do
28
+ 5.norm(0..10, 0..20).should eql 10.0
29
+ 2.norm(0..10, 10..20).should eql 12.0
30
+ 0.norm(0..10, 10..15).should eql 10.0
31
+ 10.norm(0..10, 10..15).should eql 15.0
32
+ 5.norm(0..10, 10..15).should eql 12.5
33
+ end
34
+
35
+ it "works for float" do
36
+ 2.5.norm(0..10, 0..5).should eql 1.25
37
+ 2.5.norm(0..10, 5..10).should eql 6.25
38
+ end
21
39
  end
40
+ end
41
+ end
42
+
43
+ describe Object do
44
+ describe "#to_id" do
45
+ describe "return a uniq symbol for node id" do
46
+ context "for integer" do
47
+ subject { 10.to_id }
48
+ it { should be_a_kind_of Symbol }
49
+ it { should be 10.to_id }
50
+ end
22
51
 
23
- it "can take target range other than 0.0-1.0" do
24
- 5.norm(0..10, 0..20).should eql 10.0
25
- 2.norm(0..10, 10..20).should eql 12.0
26
- 0.norm(0..10, 10..15).should eql 10.0
27
- 10.norm(0..10, 10..15).should eql 15.0
28
- 5.norm(0..10, 10..15).should eql 12.5
29
- 2.5.norm(0..10, 0..5).should eql 1.25
30
- 2.5.norm(0..10, 5..10).should eql 6.25
52
+ context "for unicode" do
53
+ subject { "グラフ".to_id }
54
+ it { should be_a_kind_of Symbol }
55
+ it { should be "グラフ".to_id }
56
+ end
31
57
  end
32
58
  end
33
- end
59
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gviz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-14 00:00:00.000000000 Z
12
+ date: 2012-10-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec