gv 0.0.3 → 0.1.0
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 +4 -4
- data/.gemrelease +2 -0
- data/.travis.yml +8 -0
- data/README.md +6 -3
- data/Rakefile +8 -1
- data/gv.gemspec +1 -0
- data/lib/gv.rb +125 -60
- data/lib/gv/version.rb +1 -1
- data/spec/graph_spec.rb +5 -5
- data/spec/render.png +0 -0
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17e83e14df49d9b53bb2d2d32106be45b59bcfde
|
4
|
+
data.tar.gz: 71612086bd7676a972c8f2de30e7017024694d5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 721459c62dd6543cc5a8510bcfc08c1abe87232dbaa621417537646d5f4b3196164b03027345b2580e00814d2a9d2dc4b5bb62812ede5fc9394023c07f67d962
|
7
|
+
data.tar.gz: fc708e42515acb0102f8ab9bd4d6ebea51d5bd02c8069000b1345b4ecc9e175098d9355b067aa491ac6b8f1900dc046cde4d71ca3db7ee14095cbd1be7be7b34
|
data/.gemrelease
ADDED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
[](https://badge.fury.io/rb/gv)
|
2
|
+
[](http://rubydoc.info/github/furunkel/gv/master/frames)
|
3
|
+
[](https://travis-ci.org/furunkel/gv)
|
4
|
+
|
1
5
|
# GV
|
2
6
|
|
3
7
|
Ruby bindings for libgvc (Graphviz) using FFI.
|
@@ -25,8 +29,7 @@ Or install it yourself as:
|
|
25
29
|
require 'gv'
|
26
30
|
|
27
31
|
graph = GV::Graph.open 'g'
|
28
|
-
graph.edge 'e', graph.node('A'), graph.node('B', shape: 'polygon')
|
29
|
-
|
32
|
+
graph.edge 'e', graph.node('A'), graph.node('B', shape: 'polygon', label: graph.html('<b>bold</b>'))
|
30
33
|
# render to string
|
31
34
|
graph.render 'png'
|
32
35
|
|
@@ -35,7 +38,7 @@ graph.write 'result.png'
|
|
35
38
|
```
|
36
39
|
|
37
40
|
#### Result
|
38
|
-

|
39
42
|
|
40
43
|
### Load existing graph from `.dot` file:
|
41
44
|
```ruby
|
data/Rakefile
CHANGED
data/gv.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.7"
|
21
21
|
spec.add_development_dependency "rake", "~> 10.0"
|
22
22
|
spec.add_development_dependency "minitest"
|
23
|
+
spec.add_development_dependency "yard"
|
23
24
|
|
24
25
|
spec.add_dependency "ffi"
|
25
26
|
end
|
data/lib/gv.rb
CHANGED
@@ -3,45 +3,34 @@ require 'ffi'
|
|
3
3
|
|
4
4
|
module GV
|
5
5
|
|
6
|
-
module
|
7
|
-
extend
|
6
|
+
module LibCGraph
|
7
|
+
extend FFI::Library
|
8
8
|
|
9
|
-
ffi_lib '
|
10
|
-
|
11
|
-
class AGraph < ::FFI::ManagedStruct
|
12
|
-
# dummy layout, only ever used by reference
|
13
|
-
layout :_1, :int,
|
14
|
-
:_2, :int
|
9
|
+
ffi_lib 'cgraph'
|
15
10
|
|
11
|
+
class AGraph < FFI::AutoPointer
|
16
12
|
def self.release(ptr)
|
17
|
-
|
13
|
+
LibCGraph.agclose(ptr) unless ptr.null?
|
18
14
|
end
|
19
15
|
end
|
20
16
|
|
21
|
-
|
17
|
+
|
22
18
|
typedef :pointer, :ag_node
|
23
19
|
typedef :pointer, :ag_edge
|
24
20
|
|
25
|
-
attach_function :gvContext, [], :pointer
|
26
|
-
attach_function :gvFreeLayout, [:gvc, AGraph.by_ref], :int
|
27
|
-
attach_function :gvLayout, [:gvc, AGraph.by_ref, :string], :int
|
28
|
-
|
29
|
-
attach_function :gvRenderFilename, [:gvc, AGraph.by_ref, :string, :string], :int
|
30
|
-
attach_function :gvRenderData, [:gvc, AGraph.by_ref, :string, :pointer, :pointer], :int
|
31
|
-
attach_function :gvFreeRenderData, [:pointer], :void
|
32
21
|
|
33
|
-
attach_function :agmemread, [:string], AGraph
|
34
|
-
attach_function :agopen, [:string, :long, :pointer], AGraph
|
35
|
-
attach_function :agclose, [AGraph
|
22
|
+
attach_function :agmemread, [:string], AGraph
|
23
|
+
attach_function :agopen, [:string, :long, :pointer], AGraph
|
24
|
+
attach_function :agclose, [AGraph], :int
|
36
25
|
|
37
26
|
attach_variable :Agundirected, :long
|
38
27
|
attach_variable :Agstrictundirected, :long
|
39
28
|
attach_variable :Agdirected, :long
|
40
29
|
attach_variable :Agstrictdirected, :long
|
41
30
|
|
42
|
-
attach_function :agnode, [AGraph
|
43
|
-
attach_function :agedge, [AGraph
|
44
|
-
attach_function :agsubg, [AGraph
|
31
|
+
attach_function :agnode, [AGraph, :string, :int], :ag_node
|
32
|
+
attach_function :agedge, [AGraph, :ag_node, :ag_node, :string, :int], :ag_edge
|
33
|
+
attach_function :agsubg, [AGraph, :string, :int], :pointer
|
45
34
|
|
46
35
|
attach_function :agnameof, [:pointer], :string
|
47
36
|
attach_function :agraphof, [:pointer], :pointer
|
@@ -51,22 +40,44 @@ module GV
|
|
51
40
|
attach_function :agget, [:pointer, :string], :string
|
52
41
|
|
53
42
|
attach_function :agsafeset, [:pointer, :string, :string, :string], :pointer
|
54
|
-
attach_function :agstrdup_html, [AGraph
|
55
|
-
attach_function :agstrfree, [AGraph
|
43
|
+
attach_function :agstrdup_html, [AGraph, :string], :pointer
|
44
|
+
attach_function :agstrfree, [AGraph, :pointer], :int
|
56
45
|
|
57
|
-
attach_function :agisdirected, [AGraph
|
58
|
-
attach_function :agisstrict, [AGraph
|
46
|
+
attach_function :agisdirected, [AGraph], :int
|
47
|
+
attach_function :agisstrict, [AGraph], :int
|
59
48
|
end
|
49
|
+
private_constant :LibCGraph
|
50
|
+
|
51
|
+
module LibGVC
|
52
|
+
extend FFI::Library
|
53
|
+
ffi_lib 'gvc'
|
54
|
+
|
55
|
+
typedef :pointer, :gvc
|
56
|
+
|
57
|
+
attach_function :gvContext, [], :pointer
|
58
|
+
attach_function :gvFreeLayout, [:gvc, LibCGraph::AGraph], :int
|
59
|
+
attach_function :gvLayout, [:gvc, LibCGraph::AGraph, :string], :int
|
60
|
+
|
61
|
+
attach_function :gvRenderFilename, [:gvc, LibCGraph::AGraph, :string, :string], :int
|
62
|
+
attach_function :gvRenderData, [:gvc, LibCGraph::AGraph, :string, :pointer, :pointer], :int
|
63
|
+
attach_function :gvFreeRenderData, [:pointer], :void
|
64
|
+
end
|
65
|
+
private_constant :LibGVC
|
60
66
|
|
61
67
|
class Component
|
62
|
-
|
68
|
+
# @!visibility private
|
69
|
+
@@gvc = LibGVC.gvContext()
|
63
70
|
|
71
|
+
# @return [Graph, SubGraph] the graph this component belongs to
|
64
72
|
attr_reader :graph
|
65
73
|
|
74
|
+
# Creates an HTML label
|
75
|
+
# @param string [String] the HTML to parse
|
76
|
+
# @return [Object] a HTML label
|
66
77
|
def html(string)
|
67
|
-
ptr =
|
78
|
+
ptr = LibCGraph.agstrdup_html(graph.ptr, string)
|
68
79
|
string = ptr.read_string
|
69
|
-
|
80
|
+
LibCGraph.agstrfree graph.ptr, ptr
|
70
81
|
|
71
82
|
string
|
72
83
|
end
|
@@ -81,16 +92,25 @@ module GV
|
|
81
92
|
|
82
93
|
alias :eql? :==
|
83
94
|
|
95
|
+
# @return [String] the component's name
|
84
96
|
def name
|
85
|
-
|
97
|
+
LibCGraph.agnameof ptr
|
86
98
|
end
|
87
99
|
|
100
|
+
# Sets an attribute
|
101
|
+
# @param attr [Symbol, String] attribute name
|
102
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
103
|
+
# @param value [Object] attribute value
|
88
104
|
def []=(attr, value)
|
89
|
-
|
105
|
+
LibCGraph.agsafeset(ptr, attr.to_s, value.to_s, "")
|
90
106
|
end
|
91
107
|
|
108
|
+
# Retrieves the value of an attribute
|
109
|
+
# @param attr [Symbol, String] attribute name
|
110
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
111
|
+
# @return [Object] the attribute value
|
92
112
|
def [](attr)
|
93
|
-
|
113
|
+
LibCGraph.agget(ptr, attr.to_s)
|
94
114
|
end
|
95
115
|
|
96
116
|
protected
|
@@ -102,7 +122,7 @@ module GV
|
|
102
122
|
@graph = graph
|
103
123
|
case name_or_ptr
|
104
124
|
when String
|
105
|
-
@ptr =
|
125
|
+
@ptr = LibCGraph.agnode(graph.ptr, name_or_ptr, 1)
|
106
126
|
else
|
107
127
|
@ptr = name_or_ptr
|
108
128
|
end
|
@@ -113,28 +133,50 @@ module GV
|
|
113
133
|
def initialize(graph, name, tail, head)
|
114
134
|
@graph = graph
|
115
135
|
|
116
|
-
@ptr =
|
136
|
+
@ptr = LibCGraph.agedge(graph.ptr, tail.ptr, head.ptr, name, 1)
|
117
137
|
end
|
118
138
|
|
139
|
+
# @return [Node] the head node of the edge
|
119
140
|
def head
|
120
|
-
Node.new @graph,
|
141
|
+
Node.new @graph, LibCGraph.aghead(ptr)
|
121
142
|
end
|
122
143
|
|
144
|
+
# @return [Node] the tail node of the edge
|
123
145
|
def tail
|
124
|
-
Node.new @graph,
|
146
|
+
Node.new @graph, LibCGraph.agtail(ptr)
|
125
147
|
end
|
126
148
|
end
|
127
149
|
|
128
150
|
class BaseGraph < Component
|
129
151
|
|
152
|
+
# Creates a new node
|
153
|
+
# @param name [String] the name (identifier) of the node
|
154
|
+
# @param attrs [Hash{String, Symbol => Object}] the attributes
|
155
|
+
# to associate with this node
|
156
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
157
|
+
# @return [Node] the newly created node
|
130
158
|
def node(name, attrs = {})
|
131
159
|
component Node, [name], attrs
|
132
160
|
end
|
133
161
|
|
162
|
+
# Creates a new edge
|
163
|
+
# @param name [String] the name (identifier) of the edge
|
164
|
+
# @param tail [Node] the edge's tail node
|
165
|
+
# @param head [Node] the edge's head node
|
166
|
+
# @param attrs [Hash{String, Symbol => Object}] the attributes
|
167
|
+
# to associate with this edge
|
168
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
169
|
+
# @return [Edge] the newly created edge
|
134
170
|
def edge(name, tail, head, attrs = {})
|
135
171
|
component Edge, [name, tail, head], attrs
|
136
172
|
end
|
137
173
|
|
174
|
+
# Creates a new sub-graph
|
175
|
+
# @param name [String] the name (identifier) of the sub-graph
|
176
|
+
# @param attrs [Hash{String, Symbol => Object}] the attributes
|
177
|
+
# to associate with this sub-graph
|
178
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
179
|
+
# @return [SubGraph] the newly created sub-graph
|
138
180
|
def sub_graph(name, attrs = {})
|
139
181
|
graph = component SubGraph, [name], attrs
|
140
182
|
yield graph if block_given?
|
@@ -142,12 +184,14 @@ module GV
|
|
142
184
|
graph
|
143
185
|
end
|
144
186
|
|
187
|
+
# @return whether this graph is directed
|
145
188
|
def directed?
|
146
|
-
|
189
|
+
LibCGraph.agisdirected(ptr) == 1
|
147
190
|
end
|
148
191
|
|
192
|
+
# @return whether this graph is strict
|
149
193
|
def strict?
|
150
|
-
|
194
|
+
LibCGraph.agisstrict(ptr) == 1
|
151
195
|
end
|
152
196
|
|
153
197
|
private
|
@@ -166,7 +210,7 @@ module GV
|
|
166
210
|
class SubGraph < BaseGraph
|
167
211
|
def initialize(graph, name)
|
168
212
|
@graph = graph
|
169
|
-
@ptr =
|
213
|
+
@ptr = LibCGraph.agsubg(graph.ptr, name, 1)
|
170
214
|
end
|
171
215
|
end
|
172
216
|
|
@@ -174,17 +218,24 @@ module GV
|
|
174
218
|
|
175
219
|
class << self
|
176
220
|
private :new
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
221
|
+
|
222
|
+
# Creates a new graph
|
223
|
+
# @param type [:directed, :undirected] the graphs type
|
224
|
+
# @param strictness [:strict, :normal] the graphs strict type
|
225
|
+
# @see http://www.graphviz.org/doc/info/attrs.html Node, Edge and Graph Attributes
|
226
|
+
# @yieldparam graph [Graph] the newly created graph
|
227
|
+
# @return [Graph] the newly created graph
|
228
|
+
def open(name, type = :directed, strictness = :normal)
|
229
|
+
ag_type = case [type, strictness]
|
230
|
+
when [:directed, :normal] then LibCGraph.Agdirected
|
231
|
+
when [:undirected, :normal] then LibCGraph.Agundirected
|
232
|
+
when [:directed, :strict] then LibCGraph.Agstrictdirected
|
233
|
+
when [:undirected, :strict] then LibCGraph.Agstrictundirected
|
183
234
|
else
|
184
|
-
raise ArgumentError, "invalid graph type #{[type,
|
235
|
+
raise ArgumentError, "invalid graph type #{[type, strictness]}"
|
185
236
|
end
|
186
237
|
|
187
|
-
graph = new(
|
238
|
+
graph = new(LibCGraph.agopen(name, ag_type, FFI::Pointer::NULL))
|
188
239
|
|
189
240
|
if block_given?
|
190
241
|
yield graph
|
@@ -193,13 +244,16 @@ module GV
|
|
193
244
|
graph
|
194
245
|
end
|
195
246
|
|
247
|
+
# Loads a graph from a string of file
|
248
|
+
# @param io [IO, String] the resource to load from
|
249
|
+
# @return the newly loaded graph
|
196
250
|
def load(io)
|
197
251
|
data = if io.is_a? String
|
198
252
|
io
|
199
253
|
else
|
200
254
|
io.read
|
201
255
|
end
|
202
|
-
new
|
256
|
+
new LibCGraph.agmemread(data)
|
203
257
|
end
|
204
258
|
end
|
205
259
|
|
@@ -211,26 +265,37 @@ module GV
|
|
211
265
|
self
|
212
266
|
end
|
213
267
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
268
|
+
# Renders the graph to an images and saves the result to a file
|
269
|
+
# @param filename [String] the filename
|
270
|
+
# @param format [String] the image format to use, e.g. 'svg', 'pdf' etc.
|
271
|
+
# @param layout [String] the layout to use, e.g. 'dot' or 'neato' etc.
|
272
|
+
# @return [nil]
|
273
|
+
def save(filename, format = 'png', layout = 'dot')
|
274
|
+
LibGVC.gvLayout(@@gvc, ptr, layout.to_s)
|
275
|
+
LibGVC.gvRenderFilename(@@gvc, ptr, format.to_s, filename);
|
276
|
+
LibGVC.gvFreeLayout(@@gvc, ptr)
|
277
|
+
|
278
|
+
nil
|
218
279
|
end
|
219
280
|
|
281
|
+
# Renders the graph to an image and returns the result as a string
|
282
|
+
# @param format [String] the image format to use, e.g. 'svg', 'pdf' etc.
|
283
|
+
# @param layout [String] the layout to use, e.g. 'dot' or 'neato' etc.
|
284
|
+
# @return [String] the rendered graph in the given format
|
220
285
|
def render(format = 'png', layout = 'dot')
|
221
|
-
|
286
|
+
LibGVC.gvLayout(@@gvc, ptr, layout.to_s)
|
222
287
|
|
223
|
-
data_ptr =
|
224
|
-
len_ptr =
|
288
|
+
data_ptr = FFI::MemoryPointer.new(:pointer, 1)
|
289
|
+
len_ptr = FFI::MemoryPointer.new(:int, 1)
|
225
290
|
|
226
|
-
|
291
|
+
LibGVC.gvRenderData(@@gvc, ptr, format.to_s, data_ptr, len_ptr);
|
227
292
|
len = len_ptr.read_uint
|
228
293
|
data_ptr = data_ptr.read_pointer
|
229
294
|
|
230
|
-
data = data_ptr.
|
295
|
+
data = data_ptr.read_string len
|
231
296
|
|
232
|
-
|
233
|
-
|
297
|
+
LibGVC.gvFreeRenderData(data_ptr)
|
298
|
+
LibGVC.gvFreeLayout(@@gvc, ptr)
|
234
299
|
|
235
300
|
data
|
236
301
|
end
|
data/lib/gv/version.rb
CHANGED
data/spec/graph_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe Graph do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "takes a block" do
|
18
|
-
|
18
|
+
Graph.open 'test' do |g|
|
19
19
|
g.directed?.must_equal true
|
20
20
|
g.strict?.must_equal false
|
21
21
|
end
|
@@ -24,8 +24,8 @@ describe Graph do
|
|
24
24
|
|
25
25
|
describe :load do
|
26
26
|
it "loads graph from file" do
|
27
|
-
f = lambda do |
|
28
|
-
graph = Graph.load
|
27
|
+
f = lambda do |filename|
|
28
|
+
graph = Graph.load filename
|
29
29
|
graph.directed?.must_equal true
|
30
30
|
graph.strict?.must_equal false
|
31
31
|
graph.name.must_equal 'g'
|
@@ -92,12 +92,12 @@ describe Graph do
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
-
describe :
|
95
|
+
describe :save do
|
96
96
|
it "renders the graph to the given filename" do
|
97
97
|
graph = Graph.open 'test'
|
98
98
|
graph.edge 'e', graph.node('A'), graph.node('B')
|
99
99
|
filename = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname(['gv_test', '.png'], nil)
|
100
|
-
graph.
|
100
|
+
graph.save filename
|
101
101
|
File.file?(filename).must_equal true
|
102
102
|
File.unlink filename
|
103
103
|
end
|
data/spec/render.png
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- furunkel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: ffi
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,7 +87,9 @@ executables: []
|
|
73
87
|
extensions: []
|
74
88
|
extra_rdoc_files: []
|
75
89
|
files:
|
90
|
+
- ".gemrelease"
|
76
91
|
- ".gitignore"
|
92
|
+
- ".travis.yml"
|
77
93
|
- Gemfile
|
78
94
|
- LICENSE.txt
|
79
95
|
- README.md
|
@@ -105,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
121
|
version: '0'
|
106
122
|
requirements: []
|
107
123
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
124
|
+
rubygems_version: 2.5.1
|
109
125
|
signing_key:
|
110
126
|
specification_version: 4
|
111
127
|
summary: Graphviz for Ruby, using libgvc via FFI
|
@@ -114,3 +130,4 @@ test_files:
|
|
114
130
|
- spec/render.png
|
115
131
|
- spec/simple_graph.dot
|
116
132
|
- spec/spec_helper.rb
|
133
|
+
has_rdoc:
|