gv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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