gv 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/gv.svg)](https://badge.fury.io/rb/gv)
|
2
|
+
[![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/furunkel/gv/master/frames)
|
3
|
+
[![Build Status](https://travis-ci.org/furunkel/gv.svg?branch=master)](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
|
-
![
|
41
|
+
![Result](https://raw.githubusercontent.com/furunkel/gv/master/spec/render.png)
|
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:
|