gv 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fdabad8208f3857d6f16f16b5db3d7e005def7a2
4
+ data.tar.gz: 5e9d9f1993de38f9e43b80ac61e3f8d75e37c9c4
5
+ SHA512:
6
+ metadata.gz: 8f74f1b224cf335ad1f9328379b1de48d176c169d82499faabe0fe9a2661791b62576dc7540d40719d1e79f4f474c2afbbf99a89263f6e8264b78609cecefaa8
7
+ data.tar.gz: 680ecfbd1bd2dd1269aaa43706eaa1f7513c1c9249c234e85bbabdeae61b426d05d2c428aba36043aaeb810c3f4f7049c3bc446e304cf810f8465b162f545ca1
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gv.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 furunkel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # GV
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'gv'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install gv
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/gv/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push 'lib'
6
+ t.pattern = "spec/*_spec.rb"
7
+ end
data/gv.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gv"
8
+ spec.version = GV::VERSION
9
+ spec.authors = ["furunkel"]
10
+ spec.email = ["julian@linux4you.it"]
11
+ spec.summary = %q{Graphviz for Ruby, using libgvc directly via FFI}
12
+ spec.homepage = "https://github.com/furunkel/gv"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "minitest"
23
+
24
+ spec.add_dependency "ffi"
25
+ end
data/lib/gv.rb ADDED
@@ -0,0 +1,226 @@
1
+ require 'gv/version'
2
+ require 'ffi'
3
+
4
+ module GV
5
+
6
+ module FFI
7
+ extend ::FFI::Library
8
+
9
+ ffi_lib 'gvc', 'cgraph', 'cgraph'
10
+
11
+ class AGraph < ::FFI::ManagedStruct
12
+ # dummy layout, only ever used by reference
13
+ layout :_1, :int,
14
+ :_2, :int
15
+
16
+ def self.release(ptr)
17
+ FFI.agclose(ptr) unless ptr.null?
18
+ end
19
+ end
20
+
21
+ typedef :pointer, :gvc
22
+ typedef :pointer, :ag_node
23
+ typedef :pointer, :ag_edge
24
+
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
+
33
+ attach_function :agmemread, [:string], AGraph.by_ref
34
+ attach_function :agopen, [:string, :long, :pointer], AGraph.by_ref
35
+ attach_function :agclose, [AGraph.by_ref], :int
36
+
37
+ attach_variable :Agundirected, :long
38
+ attach_variable :Agstrictundirected, :long
39
+ attach_variable :Agdirected, :long
40
+ attach_variable :Agstrictdirected, :long
41
+
42
+ attach_function :agnode, [AGraph.by_ref, :string, :int], :ag_node
43
+ attach_function :agedge, [AGraph.by_ref, :ag_node, :ag_node, :string, :int], :ag_edge
44
+ attach_function :agsubg, [AGraph.by_ref, :string, :int], :pointer
45
+
46
+ attach_function :agnameof, [:pointer], :string
47
+ attach_function :agraphof, [:pointer], :pointer
48
+
49
+ attach_function :agtail, [:ag_edge], :ag_node
50
+ attach_function :aghead, [:ag_edge], :ag_node
51
+ attach_function :agget, [:pointer, :string], :string
52
+
53
+ attach_function :agsafeset, [:pointer, :string, :string, :string], :pointer
54
+ attach_function :agstrdup_html, [:pointer, :string], :string
55
+ attach_function :agstrfree, [:pointer, :string], :int
56
+
57
+ attach_function :agisdirected, [AGraph.by_ref], :int
58
+ attach_function :agisstrict, [AGraph.by_ref], :int
59
+ end
60
+
61
+ class Component
62
+ @@gvc = FFI.gvContext()
63
+
64
+ attr_reader :graph
65
+
66
+ def hash
67
+ ptr.hash
68
+ end
69
+
70
+ def ==(other)
71
+ other.is_a?(Component) && ptr == other.ptr
72
+ end
73
+
74
+ alias :eql? :==
75
+
76
+ def name
77
+ FFI.agnameof ptr
78
+ end
79
+
80
+ def []=(attr, value)
81
+ FFI.agsafeset(ptr, attr.to_s, value.to_s, "")
82
+ end
83
+
84
+ def [](attr)
85
+ FFI.agget(ptr, attr.to_s)
86
+ end
87
+
88
+ protected
89
+ attr_reader :ptr
90
+ end
91
+
92
+ class Node < Component
93
+ def initialize(graph, name_or_ptr)
94
+ @graph = graph
95
+ case name_or_ptr
96
+ when String
97
+ @ptr = FFI.agnode(graph.ptr, name_or_ptr, 1)
98
+ else
99
+ @ptr = name_or_ptr
100
+ end
101
+ end
102
+ end
103
+
104
+ class Edge < Component
105
+ def initialize(graph, name, tail, head)
106
+ @graph = graph
107
+
108
+ @ptr = FFI.agedge(graph.ptr, tail.ptr, head.ptr, name, 1)
109
+ end
110
+
111
+ def head
112
+ Node.new @graph, FFI.aghead(ptr)
113
+ end
114
+
115
+ def tail
116
+ Node.new @graph, FFI.agtail(ptr)
117
+ end
118
+ end
119
+
120
+ class BaseGraph < Component
121
+
122
+ def node(name, attrs = {})
123
+ component Node, [name], attrs
124
+ end
125
+
126
+ def edge(name, tail, head, attrs = {})
127
+ component Edge, [name, tail, head], attrs
128
+ end
129
+
130
+ def graph(name, attrs = {})
131
+ graph = component SubGraph, [name], attrs
132
+ yield graph if block_given?
133
+
134
+ graph
135
+ end
136
+
137
+ def directed?
138
+ FFI.agisdirected(ptr) == 1
139
+ end
140
+
141
+ def strict?
142
+ FFI.agisstrict(ptr) == 1
143
+ end
144
+
145
+ private
146
+ def component(klass, args, attrs = {})
147
+ comp = klass.new self, *args
148
+
149
+ attrs.each do |attr, value|
150
+ comp[attr] = value
151
+ end
152
+
153
+ comp
154
+ end
155
+
156
+ end
157
+
158
+ class SubGraph < BaseGraph
159
+ def initialize(graph, name)
160
+ @graph = graph
161
+ @ptr = FFI.agsubg(graph.ptr, name, 1)
162
+ end
163
+ end
164
+
165
+ class Graph < BaseGraph
166
+
167
+ class << self
168
+ private :new
169
+ def open(name, type = :directed, strict = :normal)
170
+ ag_type = case [type, strict]
171
+ when [:directed, :normal] then FFI.Agdirected
172
+ when [:undirected, :normal] then FFI.Agundirected
173
+ when [:directed, :strict] then FFI.Agstrictdirected
174
+ when [:undirected, :strict] then FFI.Agstrictundirected
175
+ else
176
+ raise ArgumentError, "invalid graph type #{[type, strict]}"
177
+ end
178
+
179
+ graph = new(FFI.agopen(name, ag_type, ::FFI::Pointer::NULL))
180
+
181
+ if block_given?
182
+ yield graph
183
+ end
184
+
185
+ graph
186
+ end
187
+
188
+ def load(io)
189
+ data = if io.is_a? String
190
+ io
191
+ else
192
+ io.read
193
+ end
194
+ new FFI.agmemread(data)
195
+ end
196
+ end
197
+
198
+ def initialize(ptr)
199
+ @ptr = ptr
200
+ end
201
+
202
+ def write(filename, format = 'png', layout = 'dot')
203
+ FFI.gvLayout(@@gvc, ptr, layout.to_s)
204
+ FFI.gvRenderFilename(@@gvc, ptr, format.to_s, filename);
205
+ FFI.gvFreeLayout(@@gvc, ptr)
206
+ end
207
+
208
+ def render(format = 'png', layout = 'dot')
209
+ FFI.gvLayout(@@gvc, ptr, layout.to_s)
210
+
211
+ data_ptr = ::FFI::MemoryPointer.new(:pointer, 1)
212
+ len_ptr = ::FFI::MemoryPointer.new(:int, 1)
213
+
214
+ FFI.gvRenderData(@@gvc, ptr, format.to_s, data_ptr, len_ptr);
215
+ len = len_ptr.read_uint
216
+ data_ptr = data_ptr.read_pointer
217
+
218
+ data = data_ptr.read_string_length len
219
+
220
+ FFI.gvFreeRenderData(data_ptr)
221
+ FFI.gvFreeLayout(@@gvc, ptr)
222
+
223
+ data
224
+ end
225
+ end
226
+ end
data/lib/gv/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module GV
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,143 @@
1
+ require_relative 'spec_helper'
2
+
3
+ include GV
4
+
5
+ describe Graph do
6
+ describe :open do
7
+ it "creates a new graph" do
8
+ graph = Graph.open 'test'
9
+ graph.directed?.must_equal true
10
+ graph.strict?.must_equal false
11
+
12
+ graph = Graph.open 'test', :directed, :strict
13
+ graph.strict?.must_equal true
14
+ graph.directed?.must_equal true
15
+ end
16
+
17
+ it "takes a block" do
18
+ graph = Graph.open 'test' do |g|
19
+ g.directed?.must_equal true
20
+ g.strict?.must_equal false
21
+ end
22
+ end
23
+ end
24
+
25
+ describe :load do
26
+ it "loads graph from file" do
27
+ f = lambda do |f|
28
+ graph = Graph.load f
29
+ graph.directed?.must_equal true
30
+ graph.strict?.must_equal false
31
+ graph.name.must_equal 'g'
32
+ end
33
+
34
+ filename = File.join(__dir__, 'simple_graph.dot')
35
+ file = File.open filename
36
+ f.call file
37
+ file.close
38
+
39
+ f.call File.read(filename)
40
+ end
41
+ end
42
+
43
+ describe :node do
44
+ before do
45
+ @graph = Graph.open 'test'
46
+ end
47
+
48
+ it "creates a new node" do
49
+ @graph.node('test').must_be_kind_of Node
50
+ end
51
+
52
+ it "sets given attributes" do
53
+ @graph.node('test', color: 'green')[:color].must_equal 'green'
54
+ end
55
+ end
56
+
57
+ describe :graph do
58
+ before do
59
+ @graph = Graph.open 'test'
60
+ end
61
+
62
+ it "creates a new subgraph" do
63
+ @graph.graph('test').must_be_kind_of SubGraph
64
+ end
65
+
66
+ it "sets given attributes" do
67
+ @graph.graph('test', color: 'green')[:color].must_equal 'green'
68
+ end
69
+
70
+ it "takes a block" do
71
+ graph = @graph.graph('test') do |g|
72
+ g[:color] = 'green'
73
+ end
74
+ graph[:color].must_equal 'green'
75
+ end
76
+ end
77
+
78
+
79
+ describe :edge do
80
+ before do
81
+ @graph = Graph.open 'test'
82
+ @head = @graph.node 'head'
83
+ @tail = @graph.node 'tail'
84
+ end
85
+
86
+ it "creates a new edge" do
87
+ @graph.edge('test', @tail, @head).must_be_kind_of Edge
88
+ end
89
+
90
+ it "sets given attributes" do
91
+ @graph.edge('test', @tail, @head, color: 'green')[:color].must_equal 'green'
92
+ end
93
+ end
94
+
95
+ describe :write do
96
+ it "renders the graph to the given filename" do
97
+ graph = Graph.open 'test'
98
+ graph.edge 'e', graph.node('A'), graph.node('B')
99
+ filename = File.join Dir.tmpdir, Dir::Tmpname.make_tmpname(['gv_test', '.png'], nil)
100
+ graph.write filename
101
+ File.file?(filename).must_equal true
102
+ File.unlink filename
103
+ end
104
+ end
105
+
106
+ describe :render do
107
+ it "renders the graph to a string" do
108
+ graph = Graph.open 'test'
109
+ graph.edge 'e', graph.node('A'), graph.node('B', shape: 'polygon')
110
+ data = graph.render
111
+
112
+ graph = nil
113
+ GC.start
114
+ data.must_be_kind_of String
115
+
116
+ File.write 'spec/render.png', data
117
+ end
118
+ end
119
+ end
120
+
121
+ describe Edge do
122
+ before do
123
+ @graph = Graph.open 'test'
124
+ @head = @graph.node 'head'
125
+ @tail = @graph.node 'tail'
126
+ @edge = Edge.new @graph, 'test', @tail, @head
127
+ end
128
+
129
+ it 'gives access to head and tail' do
130
+ @edge.head.must_equal @head
131
+ @edge.tail.must_equal @tail
132
+ end
133
+
134
+ it 'gives access to name' do
135
+ @edge.name.must_equal 'test'
136
+ end
137
+
138
+ it 'gives access to attributes' do
139
+ @edge[:color].must_equal nil
140
+ @edge[:color] = 'green'
141
+ @edge[:color].must_equal 'green'
142
+ end
143
+ end
data/spec/render.png ADDED
Binary file
@@ -0,0 +1,6 @@
1
+ digraph g {
2
+ A1 -> B1;
3
+ A2 -> B2;
4
+ A3 -> B3;
5
+ }
6
+
@@ -0,0 +1,2 @@
1
+ require 'gv'
2
+ require 'minitest/autorun'
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - furunkel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ffi
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - julian@linux4you.it
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - gv.gemspec
82
+ - lib/gv.rb
83
+ - lib/gv/version.rb
84
+ - spec/graph_spec.rb
85
+ - spec/render.png
86
+ - spec/simple_graph.dot
87
+ - spec/spec_helper.rb
88
+ homepage: https://github.com/furunkel/gv
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Graphviz for Ruby, using libgvc directly via FFI
112
+ test_files:
113
+ - spec/graph_spec.rb
114
+ - spec/render.png
115
+ - spec/simple_graph.dot
116
+ - spec/spec_helper.rb