gviz 0.0.6 → 0.0.7

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.
@@ -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