rdoc-f95 0.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 (71) hide show
  1. data/History.txt +4 -0
  2. data/Manifest.txt +79 -0
  3. data/PostInstall.txt +7 -0
  4. data/README.rdoc +147 -0
  5. data/Rakefile +28 -0
  6. data/bin/rdoc-f95 +70 -0
  7. data/lib/rdoc-f95.rb +306 -0
  8. data/lib/rdoc-f95/code_objects.rb +776 -0
  9. data/lib/rdoc-f95/diagram.rb +342 -0
  10. data/lib/rdoc-f95/dot.rb +249 -0
  11. data/lib/rdoc-f95/generator.rb +1088 -0
  12. data/lib/rdoc-f95/generator/chm.rb +113 -0
  13. data/lib/rdoc-f95/generator/chm/chm.rb +98 -0
  14. data/lib/rdoc-f95/generator/html.rb +370 -0
  15. data/lib/rdoc-f95/generator/html/hefss.rb +414 -0
  16. data/lib/rdoc-f95/generator/html/html.rb +708 -0
  17. data/lib/rdoc-f95/generator/html/kilmer.rb +418 -0
  18. data/lib/rdoc-f95/generator/html/one_page_html.rb +121 -0
  19. data/lib/rdoc-f95/generator/ri.rb +229 -0
  20. data/lib/rdoc-f95/generator/xhtml.rb +106 -0
  21. data/lib/rdoc-f95/generator/xhtml/ctop.xsl +1318 -0
  22. data/lib/rdoc-f95/generator/xhtml/mathml.xsl +42 -0
  23. data/lib/rdoc-f95/generator/xhtml/pmathml.xsl +612 -0
  24. data/lib/rdoc-f95/generator/xhtml/pmathmlcss.xsl +872 -0
  25. data/lib/rdoc-f95/generator/xhtml/xhtml.rb +732 -0
  26. data/lib/rdoc-f95/generator/xml.rb +120 -0
  27. data/lib/rdoc-f95/generator/xml/rdf.rb +113 -0
  28. data/lib/rdoc-f95/generator/xml/xml.rb +111 -0
  29. data/lib/rdoc-f95/install.rb +166 -0
  30. data/lib/rdoc-f95/markup.rb +506 -0
  31. data/lib/rdoc-f95/markup/formatter.rb +14 -0
  32. data/lib/rdoc-f95/markup/fragments.rb +337 -0
  33. data/lib/rdoc-f95/markup/inline.rb +361 -0
  34. data/lib/rdoc-f95/markup/install.rb +57 -0
  35. data/lib/rdoc-f95/markup/lines.rb +152 -0
  36. data/lib/rdoc-f95/markup/mathml_wrapper.rb +91 -0
  37. data/lib/rdoc-f95/markup/preprocess.rb +71 -0
  38. data/lib/rdoc-f95/markup/sample/rdoc2latex.rb +16 -0
  39. data/lib/rdoc-f95/markup/sample/sample.rb +42 -0
  40. data/lib/rdoc-f95/markup/to_flow.rb +185 -0
  41. data/lib/rdoc-f95/markup/to_html.rb +357 -0
  42. data/lib/rdoc-f95/markup/to_html_crossref.rb +123 -0
  43. data/lib/rdoc-f95/markup/to_latex.rb +328 -0
  44. data/lib/rdoc-f95/markup/to_test.rb +50 -0
  45. data/lib/rdoc-f95/markup/to_xhtml_texparser.rb +234 -0
  46. data/lib/rdoc-f95/options.rb +745 -0
  47. data/lib/rdoc-f95/parsers/parse_c.rb +775 -0
  48. data/lib/rdoc-f95/parsers/parse_f95.rb +2499 -0
  49. data/lib/rdoc-f95/parsers/parse_rb.rb +2587 -0
  50. data/lib/rdoc-f95/parsers/parse_simple.rb +39 -0
  51. data/lib/rdoc-f95/parsers/parserfactory.rb +99 -0
  52. data/lib/rdoc-f95/ri.rb +2 -0
  53. data/lib/rdoc-f95/ri/cache.rb +188 -0
  54. data/lib/rdoc-f95/ri/descriptions.rb +147 -0
  55. data/lib/rdoc-f95/ri/display.rb +244 -0
  56. data/lib/rdoc-f95/ri/driver.rb +435 -0
  57. data/lib/rdoc-f95/ri/formatter.rb +603 -0
  58. data/lib/rdoc-f95/ri/paths.rb +105 -0
  59. data/lib/rdoc-f95/ri/reader.rb +106 -0
  60. data/lib/rdoc-f95/ri/util.rb +81 -0
  61. data/lib/rdoc-f95/ri/writer.rb +64 -0
  62. data/lib/rdoc-f95/stats.rb +23 -0
  63. data/lib/rdoc-f95/template.rb +64 -0
  64. data/lib/rdoc-f95/tokenstream.rb +33 -0
  65. data/lib/rdoc-f95/usage.rb +210 -0
  66. data/script/console +10 -0
  67. data/script/destroy +14 -0
  68. data/script/generate +14 -0
  69. data/test/test_helper.rb +3 -0
  70. data/test/test_rdoc-f95.rb +11 -0
  71. metadata +156 -0
@@ -0,0 +1,342 @@
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-f95/dot'
8
+
9
+ module RDocF95
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
+ @html_suffix = ".html"
44
+ if @options.mathml
45
+ @html_suffix = ".xhtml"
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Draw the diagrams. We traverse the files, drawing a diagram for each. We
51
+ # also traverse each top-level class and module in that file drawing a
52
+ # diagram for these too.
53
+
54
+ def draw
55
+ unless @options.quiet
56
+ $stderr.print "Diagrams: "
57
+ $stderr.flush
58
+ end
59
+
60
+ @info.each_with_index do |i, file_count|
61
+ @done_modules = {}
62
+ @local_names = find_names(i)
63
+ @global_names = []
64
+ @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
65
+ 'fontname' => FONT,
66
+ 'fontsize' => '8',
67
+ 'bgcolor' => 'lightcyan1',
68
+ 'compound' => 'true')
69
+
70
+ # it's a little hack %) i'm too lazy to create a separate class
71
+ # for default node
72
+ graph << DOT::Node.new('name' => 'node',
73
+ 'fontname' => FONT,
74
+ 'color' => 'black',
75
+ 'fontsize' => 8)
76
+
77
+ i.modules.each do |mod|
78
+ draw_module(mod, graph, true, i.file_relative_name)
79
+ end
80
+ add_classes(i, graph, i.file_relative_name)
81
+
82
+ i.diagram = convert_to_png("f_#{file_count}", graph)
83
+
84
+ # now go through and document each top level class and
85
+ # module independently
86
+ i.modules.each_with_index do |mod, count|
87
+ @done_modules = {}
88
+ @local_names = find_names(mod)
89
+ @global_names = []
90
+
91
+ @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel',
92
+ 'fontname' => FONT,
93
+ 'fontsize' => '8',
94
+ 'bgcolor' => 'lightcyan1',
95
+ 'compound' => 'true')
96
+
97
+ graph << DOT::Node.new('name' => 'node',
98
+ 'fontname' => FONT,
99
+ 'color' => 'black',
100
+ 'fontsize' => 8)
101
+ draw_module(mod, graph, true)
102
+ mod.diagram = convert_to_png("m_#{file_count}_#{count}",
103
+ graph)
104
+ end
105
+ end
106
+ $stderr.puts unless @options.quiet
107
+ end
108
+
109
+ private
110
+
111
+ def find_names(mod)
112
+ return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} +
113
+ mod.modules.collect{|m| find_names(m)}.flatten
114
+ end
115
+
116
+ def find_full_name(name, mod)
117
+ full_name = name.dup
118
+ return full_name if @local_names.include?(full_name)
119
+ mod_path = mod.full_name.split('::')[0..-2]
120
+ unless mod_path.nil?
121
+ until mod_path.empty?
122
+ full_name = mod_path.pop + '::' + full_name
123
+ return full_name if @local_names.include?(full_name)
124
+ end
125
+ end
126
+ return name
127
+ end
128
+
129
+ def draw_module(mod, graph, toplevel = false, file = nil)
130
+ return if @done_modules[mod.full_name] and not toplevel
131
+
132
+ @counter += 1
133
+ url = mod.http_url("classes").sub(/\.html$/, @html_suffix)
134
+ m = DOT::Subgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}",
135
+ 'label' => mod.name,
136
+ 'fontname' => FONT,
137
+ 'color' => 'blue',
138
+ 'style' => 'filled',
139
+ 'URL' => %{"#{url}"},
140
+ 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3')
141
+
142
+ @done_modules[mod.full_name] = m
143
+ add_classes(mod, m, file)
144
+ graph << m
145
+
146
+ unless mod.includes.empty?
147
+ mod.includes.each do |inc|
148
+ m_full_name = find_full_name(inc.name, mod)
149
+ if @local_names.include?(m_full_name)
150
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
151
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
152
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}",
153
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
154
+ else
155
+ unless @global_names.include?(m_full_name)
156
+ path = m_full_name.split("::")
157
+ url = File.join('classes', *path) + @html_suffix
158
+ @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
159
+ 'shape' => 'box',
160
+ 'label' => "#{m_full_name}",
161
+ 'URL' => %{"#{url}"})
162
+ @global_names << m_full_name
163
+ end
164
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
165
+ 'to' => "#{mod.full_name.gsub( /:/,'_' )}",
166
+ 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}")
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def add_classes(container, graph, file = nil )
173
+
174
+ use_fileboxes = @options.fileboxes
175
+
176
+ files = {}
177
+
178
+ # create dummy node (needed if empty and for module includes)
179
+ if container.full_name
180
+ graph << DOT::Node.new('name' => "#{container.full_name.gsub( /:/,'_' )}",
181
+ 'label' => "",
182
+ 'width' => (container.classes.empty? and
183
+ container.modules.empty?) ?
184
+ '0.75' : '0.01',
185
+ 'height' => '0.01',
186
+ 'shape' => 'plaintext')
187
+ end
188
+
189
+ container.classes.each_with_index do |cl, cl_index|
190
+ last_file = cl.in_files[-1].file_relative_name
191
+
192
+ if use_fileboxes && !files.include?(last_file)
193
+ @counter += 1
194
+ files[last_file] =
195
+ DOT::Subgraph.new('name' => "cluster_#{@counter}",
196
+ 'label' => "#{last_file}",
197
+ 'fontname' => FONT,
198
+ 'color'=>
199
+ last_file == file ? 'red' : 'black')
200
+ end
201
+
202
+ next if cl.name == 'Object' || cl.name[0,2] == "<<"
203
+
204
+ url = cl.http_url("classes").sub(/\.html$/, @html_suffix)
205
+
206
+ label = cl.name.dup
207
+ if use_fileboxes && cl.in_files.length > 1
208
+ label << '\n[' +
209
+ cl.in_files.collect {|i|
210
+ i.file_relative_name
211
+ }.sort.join( '\n' ) +
212
+ ']'
213
+ end
214
+
215
+ attrs = {
216
+ 'name' => "#{cl.full_name.gsub( /:/, '_' )}",
217
+ 'fontcolor' => 'black',
218
+ 'style'=>'filled',
219
+ 'color'=>'palegoldenrod',
220
+ 'label' => label,
221
+ 'shape' => 'ellipse',
222
+ 'URL' => %{"#{url}"}
223
+ }
224
+
225
+ c = DOT::Node.new(attrs)
226
+
227
+ if use_fileboxes
228
+ files[last_file].push c
229
+ else
230
+ graph << c
231
+ end
232
+ end
233
+
234
+ if use_fileboxes
235
+ files.each_value do |val|
236
+ graph << val
237
+ end
238
+ end
239
+
240
+ unless container.classes.empty?
241
+ container.classes.each_with_index do |cl, cl_index|
242
+ cl.includes.each do |m|
243
+ m_full_name = find_full_name(m.name, cl)
244
+ if @local_names.include?(m_full_name)
245
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
246
+ 'to' => "#{cl.full_name.gsub( /:/,'_' )}",
247
+ 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}")
248
+ else
249
+ unless @global_names.include?(m_full_name)
250
+ path = m_full_name.split("::")
251
+ url = File.join('classes', *path) + @html_suffix
252
+ @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}",
253
+ 'shape' => 'box',
254
+ 'label' => "#{m_full_name}",
255
+ 'URL' => %{"#{url}"})
256
+ @global_names << m_full_name
257
+ end
258
+ @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}",
259
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
260
+ end
261
+ end
262
+
263
+ sclass = cl.superclass
264
+ next if sclass.nil? || sclass == 'Object'
265
+ sclass_full_name = find_full_name(sclass,cl)
266
+ unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name)
267
+ path = sclass_full_name.split("::")
268
+ url = File.join('classes', *path) + @html_suffix
269
+ @global_graph << DOT::Node.new('name' => "#{sclass_full_name.gsub( /:/, '_' )}",
270
+ 'label' => sclass_full_name,
271
+ 'URL' => %{"#{url}"})
272
+ @global_names << sclass_full_name
273
+ end
274
+ @global_graph << DOT::Edge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}",
275
+ 'to' => "#{cl.full_name.gsub( /:/, '_')}")
276
+ end
277
+ end
278
+
279
+ container.modules.each do |submod|
280
+ draw_module(submod, graph)
281
+ end
282
+
283
+ end
284
+
285
+ def convert_to_png(file_base, graph)
286
+ str = graph.to_s
287
+ return @diagram_cache[str] if @diagram_cache[str]
288
+ op_type = @options.image_format
289
+ dotfile = File.join(DOT_PATH, file_base)
290
+ src = dotfile + ".dot"
291
+ dot = dotfile + "." + op_type
292
+
293
+ unless @options.quiet
294
+ $stderr.print "."
295
+ $stderr.flush
296
+ end
297
+
298
+ File.open(src, 'w+' ) do |f|
299
+ f << str << "\n"
300
+ end
301
+
302
+ system "dot", "-T#{op_type}", src, "-o", dot
303
+
304
+ # Now construct the imagemap wrapper around
305
+ # that png
306
+
307
+ ret = wrap_in_image_map(src, dot)
308
+ @diagram_cache[str] = ret
309
+ return ret
310
+ end
311
+
312
+ ##
313
+ # Extract the client-side image map from dot, and use it to generate the
314
+ # imagemap proper. Return the whole <map>..<img> combination, suitable for
315
+ # inclusion on the page
316
+
317
+ def wrap_in_image_map(src, dot)
318
+ res = %{<map id="map" name="map">\n}
319
+ dot_map = `dot -Tismap #{src}`
320
+ dot_map.split($/).each do |area|
321
+ unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/
322
+ $stderr.puts "Unexpected output from dot:\n#{area}"
323
+ return nil
324
+ end
325
+
326
+ xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i]
327
+ url, area_name = $5, $6
328
+
329
+ res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" }
330
+ res << %{ href="#{url}" alt="#{area_name}" />\n}
331
+ end
332
+ res << "</map>\n"
333
+ # map_file = src.sub(/.dot/, '.map')
334
+ # system("dot -Timap #{src} -o #{map_file}")
335
+ res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">}
336
+ return res
337
+ end
338
+
339
+ end
340
+
341
+ end
342
+
@@ -0,0 +1,249 @@
1
+ module RDocF95; end
2
+
3
+ module RDocF95::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
+