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.
Files changed (3) hide show
  1. data/lib/dotr.rb +145 -0
  2. data/test/dotr_test.rb +111 -0
  3. metadata +46 -0
@@ -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
+
@@ -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
+