dotr 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.
- 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
|
+
|