rdoc 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rdoc might be problematic. Click here for more details.
- data/History.txt +13 -0
- data/Manifest.txt +61 -0
- data/README.txt +34 -0
- data/Rakefile +10 -0
- data/bin/rdoc +22 -0
- data/bin/ri +6 -0
- data/lib/rdoc.rb +277 -0
- data/lib/rdoc/code_objects.rb +776 -0
- data/lib/rdoc/diagram.rb +338 -0
- data/lib/rdoc/dot.rb +249 -0
- data/lib/rdoc/generator.rb +1048 -0
- data/lib/rdoc/generator/chm.rb +113 -0
- data/lib/rdoc/generator/chm/chm.rb +98 -0
- data/lib/rdoc/generator/html.rb +370 -0
- data/lib/rdoc/generator/html/hefss.rb +414 -0
- data/lib/rdoc/generator/html/html.rb +704 -0
- data/lib/rdoc/generator/html/kilmer.rb +418 -0
- data/lib/rdoc/generator/html/one_page_html.rb +121 -0
- data/lib/rdoc/generator/ri.rb +229 -0
- data/lib/rdoc/generator/xml.rb +120 -0
- data/lib/rdoc/generator/xml/rdf.rb +113 -0
- data/lib/rdoc/generator/xml/xml.rb +111 -0
- data/lib/rdoc/markup.rb +473 -0
- data/lib/rdoc/markup/attribute_manager.rb +274 -0
- data/lib/rdoc/markup/formatter.rb +14 -0
- data/lib/rdoc/markup/fragments.rb +337 -0
- data/lib/rdoc/markup/inline.rb +101 -0
- data/lib/rdoc/markup/lines.rb +152 -0
- data/lib/rdoc/markup/preprocess.rb +71 -0
- data/lib/rdoc/markup/to_flow.rb +185 -0
- data/lib/rdoc/markup/to_html.rb +353 -0
- data/lib/rdoc/markup/to_html_crossref.rb +86 -0
- data/lib/rdoc/markup/to_latex.rb +328 -0
- data/lib/rdoc/markup/to_test.rb +50 -0
- data/lib/rdoc/options.rb +616 -0
- data/lib/rdoc/parsers/parse_c.rb +775 -0
- data/lib/rdoc/parsers/parse_f95.rb +1841 -0
- data/lib/rdoc/parsers/parse_rb.rb +2584 -0
- data/lib/rdoc/parsers/parse_simple.rb +40 -0
- data/lib/rdoc/parsers/parserfactory.rb +99 -0
- data/lib/rdoc/rdoc.rb +277 -0
- data/lib/rdoc/ri.rb +4 -0
- data/lib/rdoc/ri/cache.rb +188 -0
- data/lib/rdoc/ri/descriptions.rb +150 -0
- data/lib/rdoc/ri/display.rb +274 -0
- data/lib/rdoc/ri/driver.rb +452 -0
- data/lib/rdoc/ri/formatter.rb +616 -0
- data/lib/rdoc/ri/paths.rb +102 -0
- data/lib/rdoc/ri/reader.rb +106 -0
- data/lib/rdoc/ri/util.rb +81 -0
- data/lib/rdoc/ri/writer.rb +68 -0
- data/lib/rdoc/stats.rb +25 -0
- data/lib/rdoc/template.rb +64 -0
- data/lib/rdoc/tokenstream.rb +33 -0
- data/test/test_rdoc_c_parser.rb +261 -0
- data/test/test_rdoc_markup.rb +613 -0
- data/test/test_rdoc_markup_attribute_manager.rb +224 -0
- data/test/test_rdoc_ri_attribute_formatter.rb +42 -0
- data/test/test_rdoc_ri_default_display.rb +295 -0
- data/test/test_rdoc_ri_formatter.rb +318 -0
- data/test/test_rdoc_ri_overstrike_formatter.rb +69 -0
- metadata +134 -0
data/lib/rdoc/diagram.rb
ADDED
@@ -0,0 +1,338 @@
|
|
1
|
+
# A wonderful hack by to draw package diagrams using the dot package.
|
2
|
+
# Originally written by Jah, team Enticla.
|
3
|
+
#
|
4
|
+
# You must have the V1.7 or later in your path
|
5
|
+
# http://www.research.att.com/sw/tools/graphviz/
|
6
|
+
|
7
|
+
require 'rdoc/dot'
|
8
|
+
|
9
|
+
module RDoc
|
10
|
+
|
11
|
+
##
|
12
|
+
# Draw a set of diagrams representing the modules and classes in the
|
13
|
+
# system. We draw one diagram for each file, and one for each toplevel
|
14
|
+
# class or module. This means there will be overlap. However, it also
|
15
|
+
# means that you'll get better context for objects.
|
16
|
+
#
|
17
|
+
# To use, simply
|
18
|
+
#
|
19
|
+
# d = Diagram.new(info) # pass in collection of top level infos
|
20
|
+
# d.draw
|
21
|
+
#
|
22
|
+
# The results will be written to the +dot+ subdirectory. The process
|
23
|
+
# also sets the +diagram+ attribute in each object it graphs to
|
24
|
+
# the name of the file containing the image. This can be used
|
25
|
+
# by output generators to insert images.
|
26
|
+
|
27
|
+
class Diagram
|
28
|
+
|
29
|
+
FONT = "Arial"
|
30
|
+
|
31
|
+
DOT_PATH = "dot"
|
32
|
+
|
33
|
+
##
|
34
|
+
# Pass in the set of top level objects. The method also creates the
|
35
|
+
# subdirectory to hold the images
|
36
|
+
|
37
|
+
def initialize(info, options)
|
38
|
+
@info = info
|
39
|
+
@options = options
|
40
|
+
@counter = 0
|
41
|
+
FileUtils.mkdir_p(DOT_PATH)
|
42
|
+
@diagram_cache = {}
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Draw the diagrams. We traverse the files, drawing a diagram for each. We
|
47
|
+
# also traverse each top-level class and module in that file drawing a
|
48
|
+
# diagram for these too.
|
49
|
+
|
50
|
+
def draw
|
51
|
+
unless @options.quiet
|
52
|
+
$stderr.print "Diagrams: "
|
53
|
+
$stderr.flush
|
54
|
+
end
|
55
|
+
|
56
|
+
@info.each_with_index do |i, file_count|
|
57
|
+
@done_modules = {}
|
58
|
+
@local_names = find_names(i)
|
59
|
+
@global_names = []
|
60
|
+
@global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
|
61
|
+
'fontname' => FONT,
|
62
|
+
'fontsize' => '8',
|
63
|
+
'bgcolor' => 'lightcyan1',
|
64
|
+
'compound' => 'true')
|
65
|
+
|
66
|
+
# it's a little hack %) i'm too lazy to create a separate class
|
67
|
+
# for default node
|
68
|
+
graph << DOT::Node.new('name' => 'node',
|
69
|
+
'fontname' => FONT,
|
70
|
+
'color' => 'black',
|
71
|
+
'fontsize' => 8)
|
72
|
+
|
73
|
+
i.modules.each do |mod|
|
74
|
+
draw_module(mod, graph, true, i.file_relative_name)
|
75
|
+
end
|
76
|
+
add_classes(i, graph, i.file_relative_name)
|
77
|
+
|
78
|
+
i.diagram = convert_to_png("f_#{file_count}", graph)
|
79
|
+
|
80
|
+
# now go through and document each top level class and
|
81
|
+
# module independently
|
82
|
+
i.modules.each_with_index do |mod, count|
|
83
|
+
@done_modules = {}
|
84
|
+
@local_names = find_names(mod)
|
85
|
+
@global_names = []
|
86
|
+
|
87
|
+
@global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
|
88
|
+
'fontname' => FONT,
|
89
|
+
'fontsize' => '8',
|
90
|
+
'bgcolor' => 'lightcyan1',
|
91
|
+
'compound' => 'true')
|
92
|
+
|
93
|
+
graph << DOT::Node.new('name' => 'node',
|
94
|
+
'fontname' => FONT,
|
95
|
+
'color' => 'black',
|
96
|
+
'fontsize' => 8)
|
97
|
+
draw_module(mod, graph, true)
|
98
|
+
mod.diagram = convert_to_png("m_#{file_count}_#{count}",
|
99
|
+
graph)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
$stderr.puts unless @options.quiet
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def find_names(mod)
|
108
|
+
return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
|
109
|
+
mod.modules.collect{|m| find_names(m)}.flatten
|
110
|
+
end
|
111
|
+
|
112
|
+
def find_full_name(name, mod)
|
113
|
+
full_name = name.dup
|
114
|
+
return full_name if @local_names.include?(full_name)
|
115
|
+
mod_path = mod.full_name.split('::')[0..-2]
|
116
|
+
unless mod_path.nil?
|
117
|
+
until mod_path.empty?
|
118
|
+
full_name = mod_path.pop + '::' + full_name
|
119
|
+
return full_name if @local_names.include?(full_name)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
return name
|
123
|
+
end
|
124
|
+
|
125
|
+
def draw_module(mod, graph, toplevel = false, file = nil)
|
126
|
+
return if @done_modules[mod.full_name] and not toplevel
|
127
|
+
|
128
|
+
@counter += 1
|
129
|
+
url = mod.http_url("classes")
|
130
|
+
m = DOT::Subgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
|
131
|
+
'label' => mod.name,
|
132
|
+
'fontname' => FONT,
|
133
|
+
'color' => 'blue',
|
134
|
+
'style' => 'filled',
|
135
|
+
'URL' => %{"#{url}"},
|
136
|
+
'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
|
137
|
+
|
138
|
+
@done_modules[mod.full_name] = m
|
139
|
+
add_classes(mod, m, file)
|
140
|
+
graph << m
|
141
|
+
|
142
|
+
unless mod.includes.empty?
|
143
|
+
mod.includes.each do |inc|
|
144
|
+
m_full_name = find_full_name(inc.name, mod)
|
145
|
+
if @local_names.include?(m_full_name)
|
146
|
+
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
|
147
|
+
'to' => "#{mod.full_name.gsub( /:/,'_' )}",
|
148
|
+
'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
|
149
|
+
'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
|
150
|
+
else
|
151
|
+
unless @global_names.include?(m_full_name)
|
152
|
+
path = m_full_name.split("::")
|
153
|
+
url = File.join('classes', *path) + ".html"
|
154
|
+
@global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
|
155
|
+
'shape' => 'box',
|
156
|
+
'label' => "#{m_full_name}",
|
157
|
+
'URL' => %{"#{url}"})
|
158
|
+
@global_names << m_full_name
|
159
|
+
end
|
160
|
+
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
|
161
|
+
'to' => "#{mod.full_name.gsub( /:/,'_' )}",
|
162
|
+
'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def add_classes(container, graph, file = nil )
|
169
|
+
|
170
|
+
use_fileboxes = @options.fileboxes
|
171
|
+
|
172
|
+
files = {}
|
173
|
+
|
174
|
+
# create dummy node (needed if empty and for module includes)
|
175
|
+
if container.full_name
|
176
|
+
graph << DOT::Node.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
|
177
|
+
'label' => "",
|
178
|
+
'width' => (container.classes.empty? and
|
179
|
+
container.modules.empty?) ?
|
180
|
+
'0.75' : '0.01',
|
181
|
+
'height' => '0.01',
|
182
|
+
'shape' => 'plaintext')
|
183
|
+
end
|
184
|
+
|
185
|
+
container.classes.each_with_index do |cl, cl_index|
|
186
|
+
last_file = cl.in_files[-1].file_relative_name
|
187
|
+
|
188
|
+
if use_fileboxes && !files.include?(last_file)
|
189
|
+
@counter += 1
|
190
|
+
files[last_file] =
|
191
|
+
DOT::Subgraph.new('name' => "cluster_#{@counter}",
|
192
|
+
'label' => "#{last_file}",
|
193
|
+
'fontname' => FONT,
|
194
|
+
'color'=>
|
195
|
+
last_file == file ? 'red' : 'black')
|
196
|
+
end
|
197
|
+
|
198
|
+
next if cl.name == 'Object' || cl.name[0,2] == "<<"
|
199
|
+
|
200
|
+
url = cl.http_url("classes")
|
201
|
+
|
202
|
+
label = cl.name.dup
|
203
|
+
if use_fileboxes && cl.in_files.length > 1
|
204
|
+
label << '\n[' +
|
205
|
+
cl.in_files.collect {|i|
|
206
|
+
i.file_relative_name
|
207
|
+
}.sort.join( '\n' ) +
|
208
|
+
']'
|
209
|
+
end
|
210
|
+
|
211
|
+
attrs = {
|
212
|
+
'name' => "#{cl.full_name.gsub( /:/, '_' )}",
|
213
|
+
'fontcolor' => 'black',
|
214
|
+
'style'=>'filled',
|
215
|
+
'color'=>'palegoldenrod',
|
216
|
+
'label' => label,
|
217
|
+
'shape' => 'ellipse',
|
218
|
+
'URL' => %{"#{url}"}
|
219
|
+
}
|
220
|
+
|
221
|
+
c = DOT::Node.new(attrs)
|
222
|
+
|
223
|
+
if use_fileboxes
|
224
|
+
files[last_file].push c
|
225
|
+
else
|
226
|
+
graph << c
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
if use_fileboxes
|
231
|
+
files.each_value do |val|
|
232
|
+
graph << val
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
unless container.classes.empty?
|
237
|
+
container.classes.each_with_index do |cl, cl_index|
|
238
|
+
cl.includes.each do |m|
|
239
|
+
m_full_name = find_full_name(m.name, cl)
|
240
|
+
if @local_names.include?(m_full_name)
|
241
|
+
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
|
242
|
+
'to' => "#{cl.full_name.gsub( /:/,'_' )}",
|
243
|
+
'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
|
244
|
+
else
|
245
|
+
unless @global_names.include?(m_full_name)
|
246
|
+
path = m_full_name.split("::")
|
247
|
+
url = File.join('classes', *path) + ".html"
|
248
|
+
@global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
|
249
|
+
'shape' => 'box',
|
250
|
+
'label' => "#{m_full_name}",
|
251
|
+
'URL' => %{"#{url}"})
|
252
|
+
@global_names << m_full_name
|
253
|
+
end
|
254
|
+
@global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
|
255
|
+
'to' => "#{cl.full_name.gsub( /:/, '_')}")
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
sclass = cl.superclass
|
260
|
+
next if sclass.nil? || sclass == 'Object'
|
261
|
+
sclass_full_name = find_full_name(sclass,cl)
|
262
|
+
unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
|
263
|
+
path = sclass_full_name.split("::")
|
264
|
+
url = File.join('classes', *path) + ".html"
|
265
|
+
@global_graph << DOT::Node.new('name' => "#{sclass_full_name.gsub( /:/, '_' )}",
|
266
|
+
'label' => sclass_full_name,
|
267
|
+
'URL' => %{"#{url}"})
|
268
|
+
@global_names << sclass_full_name
|
269
|
+
end
|
270
|
+
@global_graph << DOT::Edge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
|
271
|
+
'to' => "#{cl.full_name.gsub( /:/, '_')}")
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
container.modules.each do |submod|
|
276
|
+
draw_module(submod, graph)
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
def convert_to_png(file_base, graph)
|
282
|
+
str = graph.to_s
|
283
|
+
return @diagram_cache[str] if @diagram_cache[str]
|
284
|
+
op_type = @options.image_format
|
285
|
+
dotfile = File.join(DOT_PATH, file_base)
|
286
|
+
src = dotfile + ".dot"
|
287
|
+
dot = dotfile + "." + op_type
|
288
|
+
|
289
|
+
unless @options.quiet
|
290
|
+
$stderr.print "."
|
291
|
+
$stderr.flush
|
292
|
+
end
|
293
|
+
|
294
|
+
File.open(src, 'w+' ) do |f|
|
295
|
+
f << str << "\n"
|
296
|
+
end
|
297
|
+
|
298
|
+
system "dot", "-T#{op_type}", src, "-o", dot
|
299
|
+
|
300
|
+
# Now construct the imagemap wrapper around
|
301
|
+
# that png
|
302
|
+
|
303
|
+
ret = wrap_in_image_map(src, dot)
|
304
|
+
@diagram_cache[str] = ret
|
305
|
+
return ret
|
306
|
+
end
|
307
|
+
|
308
|
+
##
|
309
|
+
# Extract the client-side image map from dot, and use it to generate the
|
310
|
+
# imagemap proper. Return the whole <map>..<img> combination, suitable for
|
311
|
+
# inclusion on the page
|
312
|
+
|
313
|
+
def wrap_in_image_map(src, dot)
|
314
|
+
res = %{<map id="map" name="map">\n}
|
315
|
+
dot_map = `dot -Tismap #{src}`
|
316
|
+
dot_map.split($/).each do |area|
|
317
|
+
unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
|
318
|
+
$stderr.puts "Unexpected output from dot:\n#{area}"
|
319
|
+
return nil
|
320
|
+
end
|
321
|
+
|
322
|
+
xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
|
323
|
+
url, area_name = $5, $6
|
324
|
+
|
325
|
+
res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
|
326
|
+
res << %{ href="#{url}" alt="#{area_name}" />\n}
|
327
|
+
end
|
328
|
+
res << "</map>\n"
|
329
|
+
# map_file = src.sub(/.dot/, '.map')
|
330
|
+
# system("dot -Timap #{src} -o #{map_file}")
|
331
|
+
res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
|
332
|
+
return res
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
data/lib/rdoc/dot.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
module RDoc; end
|
2
|
+
|
3
|
+
module RDoc::DOT
|
4
|
+
|
5
|
+
TAB = ' '
|
6
|
+
TAB2 = TAB * 2
|
7
|
+
|
8
|
+
# options for node declaration
|
9
|
+
NODE_OPTS = [
|
10
|
+
'bgcolor',
|
11
|
+
'color',
|
12
|
+
'fontcolor',
|
13
|
+
'fontname',
|
14
|
+
'fontsize',
|
15
|
+
'height',
|
16
|
+
'width',
|
17
|
+
'label',
|
18
|
+
'layer',
|
19
|
+
'rank',
|
20
|
+
'shape',
|
21
|
+
'shapefile',
|
22
|
+
'style',
|
23
|
+
'URL',
|
24
|
+
]
|
25
|
+
|
26
|
+
# options for edge declaration
|
27
|
+
EDGE_OPTS = [
|
28
|
+
'color',
|
29
|
+
'decorate',
|
30
|
+
'dir',
|
31
|
+
'fontcolor',
|
32
|
+
'fontname',
|
33
|
+
'fontsize',
|
34
|
+
'id',
|
35
|
+
'label',
|
36
|
+
'layer',
|
37
|
+
'lhead',
|
38
|
+
'ltail',
|
39
|
+
'minlen',
|
40
|
+
'style',
|
41
|
+
'weight'
|
42
|
+
]
|
43
|
+
|
44
|
+
# options for graph declaration
|
45
|
+
GRAPH_OPTS = [
|
46
|
+
'bgcolor',
|
47
|
+
'center',
|
48
|
+
'clusterrank',
|
49
|
+
'color',
|
50
|
+
'compound',
|
51
|
+
'concentrate',
|
52
|
+
'fillcolor',
|
53
|
+
'fontcolor',
|
54
|
+
'fontname',
|
55
|
+
'fontsize',
|
56
|
+
'label',
|
57
|
+
'layerseq',
|
58
|
+
'margin',
|
59
|
+
'mclimit',
|
60
|
+
'nodesep',
|
61
|
+
'nslimit',
|
62
|
+
'ordering',
|
63
|
+
'orientation',
|
64
|
+
'page',
|
65
|
+
'rank',
|
66
|
+
'rankdir',
|
67
|
+
'ranksep',
|
68
|
+
'ratio',
|
69
|
+
'size',
|
70
|
+
'style',
|
71
|
+
'URL'
|
72
|
+
]
|
73
|
+
|
74
|
+
# a root class for any element in dot notation
|
75
|
+
class SimpleElement
|
76
|
+
attr_accessor :name
|
77
|
+
|
78
|
+
def initialize( params = {} )
|
79
|
+
@label = params['name'] ? params['name'] : ''
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_s
|
83
|
+
@name
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# an element that has options ( node, edge or graph )
|
88
|
+
class Element < SimpleElement
|
89
|
+
#attr_reader :parent
|
90
|
+
attr_accessor :name, :options
|
91
|
+
|
92
|
+
def initialize( params = {}, option_list = [] )
|
93
|
+
super( params )
|
94
|
+
@name = params['name'] ? params['name'] : nil
|
95
|
+
@parent = params['parent'] ? params['parent'] : nil
|
96
|
+
@options = {}
|
97
|
+
option_list.each{ |i|
|
98
|
+
@options[i] = params[i] if params[i]
|
99
|
+
}
|
100
|
+
@options['label'] ||= @name if @name != 'node'
|
101
|
+
end
|
102
|
+
|
103
|
+
def each_option
|
104
|
+
@options.each{ |i| yield i }
|
105
|
+
end
|
106
|
+
|
107
|
+
def each_option_pair
|
108
|
+
@options.each_pair{ |key, val| yield key, val }
|
109
|
+
end
|
110
|
+
|
111
|
+
#def parent=( thing )
|
112
|
+
# @parent.delete( self ) if defined?( @parent ) and @parent
|
113
|
+
# @parent = thing
|
114
|
+
#end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# this is used when we build nodes that have shape=record
|
119
|
+
# ports don't have options :)
|
120
|
+
class Port < SimpleElement
|
121
|
+
attr_accessor :label
|
122
|
+
|
123
|
+
def initialize( params = {} )
|
124
|
+
super( params )
|
125
|
+
@name = params['label'] ? params['label'] : ''
|
126
|
+
end
|
127
|
+
def to_s
|
128
|
+
( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# node element
|
133
|
+
class Node < Element
|
134
|
+
|
135
|
+
def initialize( params = {}, option_list = NODE_OPTS )
|
136
|
+
super( params, option_list )
|
137
|
+
@ports = params['ports'] ? params['ports'] : []
|
138
|
+
end
|
139
|
+
|
140
|
+
def each_port
|
141
|
+
@ports.each{ |i| yield i }
|
142
|
+
end
|
143
|
+
|
144
|
+
def << ( thing )
|
145
|
+
@ports << thing
|
146
|
+
end
|
147
|
+
|
148
|
+
def push ( thing )
|
149
|
+
@ports.push( thing )
|
150
|
+
end
|
151
|
+
|
152
|
+
def pop
|
153
|
+
@ports.pop
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s( t = '' )
|
157
|
+
|
158
|
+
label = @options['shape'] != 'record' && @ports.length == 0 ?
|
159
|
+
@options['label'] ?
|
160
|
+
t + TAB + "label = \"#{@options['label']}\"\n" :
|
161
|
+
'' :
|
162
|
+
t + TAB + 'label = "' + " \\\n" +
|
163
|
+
t + TAB2 + "#{@options['label']}| \\\n" +
|
164
|
+
@ports.collect{ |i|
|
165
|
+
t + TAB2 + i.to_s
|
166
|
+
}.join( "| \\\n" ) + " \\\n" +
|
167
|
+
t + TAB + '"' + "\n"
|
168
|
+
|
169
|
+
t + "#{@name} [\n" +
|
170
|
+
@options.to_a.collect{ |i|
|
171
|
+
i[1] && i[0] != 'label' ?
|
172
|
+
t + TAB + "#{i[0]} = #{i[1]}" : nil
|
173
|
+
}.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
|
174
|
+
label +
|
175
|
+
t + "]\n"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# subgraph element is the same to graph, but has another header in dot
|
180
|
+
# notation
|
181
|
+
class Subgraph < Element
|
182
|
+
|
183
|
+
def initialize( params = {}, option_list = GRAPH_OPTS )
|
184
|
+
super( params, option_list )
|
185
|
+
@nodes = params['nodes'] ? params['nodes'] : []
|
186
|
+
@dot_string = 'subgraph'
|
187
|
+
end
|
188
|
+
|
189
|
+
def each_node
|
190
|
+
@nodes.each{ |i| yield i }
|
191
|
+
end
|
192
|
+
|
193
|
+
def << ( thing )
|
194
|
+
@nodes << thing
|
195
|
+
end
|
196
|
+
|
197
|
+
def push( thing )
|
198
|
+
@nodes.push( thing )
|
199
|
+
end
|
200
|
+
|
201
|
+
def pop
|
202
|
+
@nodes.pop
|
203
|
+
end
|
204
|
+
|
205
|
+
def to_s( t = '' )
|
206
|
+
hdr = t + "#{@dot_string} #{@name} {\n"
|
207
|
+
|
208
|
+
options = @options.to_a.collect{ |name, val|
|
209
|
+
val && name != 'label' ?
|
210
|
+
t + TAB + "#{name} = #{val}" :
|
211
|
+
name ? t + TAB + "#{name} = \"#{val}\"" : nil
|
212
|
+
}.compact.join( "\n" ) + "\n"
|
213
|
+
|
214
|
+
nodes = @nodes.collect{ |i|
|
215
|
+
i.to_s( t + TAB )
|
216
|
+
}.join( "\n" ) + "\n"
|
217
|
+
hdr + options + nodes + t + "}\n"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# this is graph
|
222
|
+
class Digraph < Subgraph
|
223
|
+
def initialize( params = {}, option_list = GRAPH_OPTS )
|
224
|
+
super( params, option_list )
|
225
|
+
@dot_string = 'digraph'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# this is edge
|
230
|
+
class Edge < Element
|
231
|
+
attr_accessor :from, :to
|
232
|
+
def initialize( params = {}, option_list = EDGE_OPTS )
|
233
|
+
super( params, option_list )
|
234
|
+
@from = params['from'] ? params['from'] : nil
|
235
|
+
@to = params['to'] ? params['to'] : nil
|
236
|
+
end
|
237
|
+
|
238
|
+
def to_s( t = '' )
|
239
|
+
t + "#{@from} -> #{to} [\n" +
|
240
|
+
@options.to_a.collect{ |i|
|
241
|
+
i[1] && i[0] != 'label' ?
|
242
|
+
t + TAB + "#{i[0]} = #{i[1]}" :
|
243
|
+
i[1] ? t + TAB + "#{i[0]} = \"#{i[1]}\"" : nil
|
244
|
+
}.compact.join( "\n" ) + "\n" + t + "]\n"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
249
|
+
|