dotr 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/dotr.rb +145 -0
- data/test/dotr_test.rb +111 -0
- metadata +46 -0
data/lib/dotr.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
module DotR
|
2
|
+
# This class represents a directed graph that can be rendered to a graphics
|
3
|
+
# image using its #diagram method.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
# d = DotR::Digraph.new do |g|
|
7
|
+
# g.node('node1') do |n|
|
8
|
+
# n.label = "MyLabel"
|
9
|
+
# n.fontsize="8"
|
10
|
+
# end
|
11
|
+
# g.node('node2', :label => 'SecondNode') do |n|
|
12
|
+
# n.connection('node1', :label => 'relates to')
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# File.open('diagram.png', 'w') { |f| f.write(d.diagram(:png)) }
|
16
|
+
class Digraph
|
17
|
+
attr_accessor :name
|
18
|
+
# Create a new Digraph. If a block is provided it will be called with
|
19
|
+
# the graph as a parameter.
|
20
|
+
def initialize(name="diagram", &block)
|
21
|
+
@name = name
|
22
|
+
@nodes = []
|
23
|
+
yield self if block
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a Node in the graph with a given name. If a block is provided
|
27
|
+
# it will be called with the node as a parameter, for convenient adding of
|
28
|
+
# connections or specification of style attributes.
|
29
|
+
def node(name, style={}, &block)
|
30
|
+
@nodes << Node.new(name, style, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create a connection in the graph between two nodes with the given names.
|
34
|
+
# If a block is provided # it will be called with the connection as a parameter,
|
35
|
+
# for convenient specification of styles.
|
36
|
+
#
|
37
|
+
# Nodes with the given names are implicitly created if they have not
|
38
|
+
# been explicitly added. See Node#connection
|
39
|
+
def connection(from_node_name, to_node_name, style={}, &block)
|
40
|
+
node(from_node_name) do |n|
|
41
|
+
n.connection(to_node_name, style, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the dot input script equivalent of this digraph
|
46
|
+
def to_s
|
47
|
+
script = []
|
48
|
+
render_to(script)
|
49
|
+
script.flatten.join("\n") + "\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Render the diagram to a graphic image, returning the raw image data as
|
53
|
+
# a string.
|
54
|
+
#
|
55
|
+
# Possible values for +format+ include +:svg+, +:png+, +:ps+, +:gif+
|
56
|
+
def diagram(format=:png)
|
57
|
+
Tempfile.open("diag") do |input|
|
58
|
+
input.write(self.to_s)
|
59
|
+
input.flush
|
60
|
+
return IO.popen("dot -T#{format} #{input.path}") { |dot| dot.read }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def render_to(output_lines)
|
67
|
+
output_lines << "digraph \"#{name}\" {"
|
68
|
+
@nodes.each { |node| node.render_to(output_lines) }
|
69
|
+
output_lines << '}'
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
module Styled # :nodoc:
|
77
|
+
def method_missing(method, *args)
|
78
|
+
return super unless /^(\w+)=$/ =~ method.to_s && args.size == 1
|
79
|
+
style_attrs()[$1.to_sym] = args.first
|
80
|
+
end
|
81
|
+
|
82
|
+
def style
|
83
|
+
return '' if style_attrs.empty?
|
84
|
+
" [" + style_attrs.keys.sort_by { |k| k.to_s }.map { |k| "#{k}=\"#{style_attrs[k]}\"" }.join(',') + "]"
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def style_attrs
|
90
|
+
@style_attrs ||= {}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Represents a node in a digraph. Instances of this class are created by calling
|
95
|
+
# Digraph#node.
|
96
|
+
#
|
97
|
+
# Specify styles on this object by setting instance attributes.
|
98
|
+
# Possible attributes include 'label' and 'shape'; the attribute names and
|
99
|
+
# possible values correspond to the style attributes described in the 'dot' manual.
|
100
|
+
class Node
|
101
|
+
include Styled
|
102
|
+
attr_reader :name
|
103
|
+
|
104
|
+
def initialize(name, style={}, &block) #:nodoc:
|
105
|
+
@name = name
|
106
|
+
@connections = []
|
107
|
+
self.label = name
|
108
|
+
style_attrs.update(style)
|
109
|
+
yield self if block
|
110
|
+
end
|
111
|
+
|
112
|
+
def connection(other_node_name, style={}, &block)
|
113
|
+
@connections << Connection.new(self.name, other_node_name, style, &block)
|
114
|
+
end
|
115
|
+
|
116
|
+
def render_to(output_lines) #:nodoc:
|
117
|
+
output_lines << "\"#{self.name}\"" + style + ";"
|
118
|
+
@connections.each { |c| c.render_to(output_lines) }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Represents a connection between two nodes in a digraph.
|
123
|
+
# Instances of this class are created by calling Digraph#connection or
|
124
|
+
# Node#connection.
|
125
|
+
#
|
126
|
+
# Specify styles on this object by setting instance attributes.
|
127
|
+
# Possible attributes include 'label' and 'shape'; the attribute names and
|
128
|
+
# possible values correspond to the style attributes described in the 'dot' manual.
|
129
|
+
class Connection
|
130
|
+
include Styled
|
131
|
+
attr_reader :from_name, :to_name
|
132
|
+
|
133
|
+
def initialize(from_name, to_name, style={}, &block) #:nodoc:
|
134
|
+
@from_name = from_name
|
135
|
+
@to_name = to_name
|
136
|
+
style_attrs.update(style)
|
137
|
+
yield self if block
|
138
|
+
end
|
139
|
+
|
140
|
+
def render_to(output_lines) # :nodoc:
|
141
|
+
output_lines << "\"#{from_name}\" -> \"#{to_name}\"" + style + ";"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
data/test/dotr_test.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'dotr'
|
5
|
+
|
6
|
+
class DotRDigraphTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_empty_digraph
|
9
|
+
d = DotR::Digraph.new('myname')
|
10
|
+
assert_digraph_equals <<-END, d
|
11
|
+
digraph "myname" {
|
12
|
+
}
|
13
|
+
END
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_single_node
|
17
|
+
d = DotR::Digraph.new do |graph|
|
18
|
+
graph.node('somenode')
|
19
|
+
end
|
20
|
+
assert_digraph_equals <<-END, d
|
21
|
+
digraph "diagram" {
|
22
|
+
"somenode" [label="somenode"];
|
23
|
+
}
|
24
|
+
END
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_connection_with_node
|
28
|
+
d = DotR::Digraph.new do |graph|
|
29
|
+
graph.node 'somenode' do |node|
|
30
|
+
node.connection('someothernode')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
assert_digraph_equals <<-END, d
|
34
|
+
digraph "diagram" {
|
35
|
+
"somenode" [label="somenode"];
|
36
|
+
"somenode" -> "someothernode";
|
37
|
+
}
|
38
|
+
END
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_connection_without_node
|
42
|
+
d = DotR::Digraph.new do |graph|
|
43
|
+
graph.connection('somenode', 'someothernode')
|
44
|
+
end
|
45
|
+
assert_digraph_equals <<-END, d
|
46
|
+
digraph "diagram" {
|
47
|
+
"somenode" [label="somenode"];
|
48
|
+
"somenode" -> "someothernode";
|
49
|
+
}
|
50
|
+
END
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_can_style_nodes_with_block
|
54
|
+
d = DotR::Digraph.new do |graph|
|
55
|
+
graph.node('somenode') do |node|
|
56
|
+
node.label = "MyLabel"
|
57
|
+
node.fontsize="8"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
assert_digraph_equals <<-END, d
|
61
|
+
digraph "diagram" {
|
62
|
+
"somenode" [fontsize="8",label="MyLabel"];
|
63
|
+
}
|
64
|
+
END
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_can_style_connections_with_block
|
68
|
+
d = DotR::Digraph.new do |graph|
|
69
|
+
graph.connection('somenode', 'someothernode') do |node|
|
70
|
+
node.label = "MyLabel"
|
71
|
+
node.fontsize="8"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
assert_digraph_equals <<-END, d
|
75
|
+
digraph "diagram" {
|
76
|
+
"somenode" [label="somenode"];
|
77
|
+
"somenode" -> "someothernode" [fontsize="8",label="MyLabel"];
|
78
|
+
}
|
79
|
+
END
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_can_style_nodes_with_hash
|
83
|
+
d = DotR::Digraph.new do |graph|
|
84
|
+
graph.node('somenode', :label => "MyLabel", :fontsize => 8)
|
85
|
+
end
|
86
|
+
assert_digraph_equals <<-END, d
|
87
|
+
digraph "diagram" {
|
88
|
+
"somenode" [fontsize="8",label="MyLabel"];
|
89
|
+
}
|
90
|
+
END
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_can_style_connections_with_hash
|
94
|
+
d = DotR::Digraph.new do |graph|
|
95
|
+
graph.connection('somenode', 'someothernode', :label => "MyLabel", :fontsize => 8)
|
96
|
+
end
|
97
|
+
assert_digraph_equals <<-END, d
|
98
|
+
digraph "diagram" {
|
99
|
+
"somenode" [label="somenode"];
|
100
|
+
"somenode" -> "someothernode" [fontsize="8",label="MyLabel"];
|
101
|
+
}
|
102
|
+
END
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def assert_digraph_equals expected, digraph
|
108
|
+
assert_equal(expected.strip.gsub(/^\s+/, ''), digraph.to_s.strip)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: dotr
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2006-07-14 00:00:00 +02:00
|
8
|
+
summary: Produce directed graph images using the 'dot' utility.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email:
|
12
|
+
homepage:
|
13
|
+
rubyforge_project:
|
14
|
+
description: ""
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/dotr.rb
|
32
|
+
- test/dotr_test.rb
|
33
|
+
test_files: []
|
34
|
+
|
35
|
+
rdoc_options: []
|
36
|
+
|
37
|
+
extra_rdoc_files: []
|
38
|
+
|
39
|
+
executables: []
|
40
|
+
|
41
|
+
extensions: []
|
42
|
+
|
43
|
+
requirements:
|
44
|
+
- none
|
45
|
+
dependencies: []
|
46
|
+
|