rdot 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/.yardopts +9 -0
  2. data/README.md +152 -0
  3. data/bin/rdot +531 -0
  4. data/lib/rdot.rb +762 -0
  5. metadata +67 -0
@@ -0,0 +1,762 @@
1
+ # encoding: utf-8
2
+
3
+ require 'is/monkey/sandbox'
4
+ require 'is/monkey/namespace'
5
+
6
+ # @private
7
+ class Module
8
+
9
+ def module_scope
10
+ m = self.inspect
11
+ if m[0...8] == '#<Class:'
12
+ s = m.rindex '>'
13
+ v = m[8...s]
14
+ begin
15
+ value = sandbox { eval v }
16
+ return [value, :class]
17
+ rescue
18
+ return [nil, nil]
19
+ end
20
+ else
21
+ return [self, :instance]
22
+ end
23
+ end
24
+
25
+ alias :rdot_old_attr :attr
26
+ alias :rdot_old_attr_reader :attr_reader
27
+ alias :rdot_old_attr_writer :attr_writer
28
+ alias :rdot_old_attr_accessor :attr_accessor
29
+
30
+ def parse_caller clr
31
+ clr.each do |s|
32
+ if s.include?('`<module:') || s.include?('`<class:') ||
33
+ s.include?("`singletonclass'") || s.include?('`block in <')
34
+ a = s.split(':')
35
+ begin
36
+ return [a[0], a[1].to_i]
37
+ rescue
38
+ end
39
+ end
40
+ end
41
+ return nil
42
+ end
43
+
44
+ def attr *names
45
+ RDot.register_attribute *module_scope, names, '[r]', parse_caller(caller)
46
+ rdot_old_attr *names
47
+ end
48
+
49
+ def attr_reader *names
50
+ RDot.register_attribute *module_scope, names, '[r]', parse_caller(caller)
51
+ rdot_old_attr_reader *names
52
+ end
53
+
54
+ def attr_writer *names
55
+ RDot.register_attribute *module_scope, names, '[w]', parse_caller(caller)
56
+ rdot_old_attr_writer *names
57
+ end
58
+
59
+ def attr_accessor *names
60
+ RDot.register_attribute *module_scope, names, '[rw]', parse_caller(caller)
61
+ rdot_old_attr_accessor *names
62
+ end
63
+
64
+ private :module_scope, :parse_caller, :attr, :attr_reader, :attr_writer,
65
+ :attr_accessor, :rdot_old_attr, :rdot_old_attr_accessor,
66
+ :rdot_old_attr_reader, :rdot_old_attr_writer
67
+
68
+ end
69
+
70
+ module RDot
71
+
72
+ VERSION = '1.0.4'
73
+
74
+ class << self
75
+
76
+ # @private
77
+ def register_attribute mod, scope, names, access, source
78
+ @attributes ||= {}
79
+ @attributes[mod] ||= {}
80
+ @attributes[mod][scope] ||= {}
81
+ names.each do |name|
82
+ @attributes[mod][scope][name.intern] = {
83
+ :access => access,
84
+ :source => source
85
+ }
86
+ end
87
+ end
88
+
89
+ # @private
90
+ def get_file file
91
+ src = File.expand_path file
92
+ $:.each do |dir|
93
+ dir = File.expand_path dir
94
+ len = dir.length
95
+ if src[0...len] == dir
96
+ src = src[len..-1]
97
+ if src[0] == '/'
98
+ src = src[1..-1]
99
+ end
100
+ return src
101
+ end
102
+ end
103
+ return file
104
+ end
105
+
106
+ # @private
107
+ def get_method_object mod, scope, name
108
+ case scope
109
+ when :instance
110
+ mod.instance_method(name)
111
+ when :class
112
+ mod.singleton_class.instance_method(name)
113
+ end
114
+ end
115
+
116
+ # @private
117
+ def add_method acc, mod, scope, visibility, name, opts
118
+ m = get_method_object mod, scope, name
119
+ src = m.source_location
120
+ obj = {}
121
+ nm = name.to_s
122
+ nm = nm[0..-2] if nm[-1] == '='
123
+ nm = nm.intern
124
+ if @attributes && @attributes[mod] && @attributes[mod][scope] &&
125
+ @attributes[mod][scope][nm]
126
+ src = @attributes[mod][scope][nm][:source]
127
+ obj[:access] = @attributes[mod][scope][nm][:access]
128
+ end
129
+ if src
130
+ obj[:file] = get_file src[0]
131
+ obj[:line] = src[1]
132
+ if opts[:exclude_files]
133
+ opts[:exclude_files].each do |f|
134
+ if File.expand_path(f) == File.expand_path(src[0])
135
+ return nil
136
+ end
137
+ end
138
+ end
139
+ if opts[:filter_files]
140
+ opts[:filter_files].each do |f|
141
+ if File.expand_path(f) != File.expand_path(src[0])
142
+ return nil
143
+ end
144
+ end
145
+ end
146
+ elsif opts[:filter_files]
147
+ return nil
148
+ end
149
+ acc[scope] ||= {}
150
+ acc[scope][visibility] ||= {}
151
+ if opts[:select_attributes] && obj[:access]
152
+ obj[:signature] = nm.to_s + ' ' + obj[:access]
153
+ acc[scope][visibility][:attributes] ||= {}
154
+ acc[scope][visibility][:attributes][nm] = obj
155
+ else
156
+ if ! opts[:hide_arguments]
157
+ ltr = 'a'
158
+ obj[:signature] = name.to_s + '(' + m.parameters.map do |q, n|
159
+ nm = n || ltr
160
+ ltr = ltr.succ
161
+ case q
162
+ when :req
163
+ nm
164
+ when :opt
165
+ "#{nm} = <…>"
166
+ when :rest
167
+ "*#{nm}"
168
+ when :block
169
+ "&#{nm}"
170
+ end
171
+ end.join(', ') + ')'
172
+ else
173
+ obj[:signature] = name.to_s + '()'
174
+ end
175
+ acc[scope][visibility][:methods] ||= {}
176
+ acc[scope][visibility][:methods][name] = obj
177
+ end
178
+ return obj
179
+ end
180
+
181
+ # @private
182
+ def get_module mod, opts
183
+ result = {}
184
+ result[:module] = mod
185
+ incs = mod.included_modules - [mod]
186
+ exts = mod.singleton_class.included_modules - Module.included_modules
187
+ if Class === mod
188
+ exts -= Class.included_modules
189
+ result[:superclass] = mod.superclass && mod.superclass.inspect || nil
190
+ if mod.superclass
191
+ incs -= mod.superclass.included_modules
192
+ exts -= mod.superclass.singleton_class.included_modules
193
+ end
194
+ end
195
+ incs.dup.each { |d| incs -= d.included_modules }
196
+ exts.dup.each { |d| exts -= d.included_modules }
197
+ result[:included] = incs.map &:inspect
198
+ result[:extended] = exts.map &:inspect
199
+ result[:nested] = mod.namespace && mod.namespace.inspect || nil
200
+ if opts[:no_scan]
201
+ return result
202
+ end
203
+ if ! opts[:hide_constants]
204
+ result[:constants] = {}
205
+ mod.constants(false).each do |c|
206
+ next if mod == Object && c == :Config
207
+ if (auto = mod.autoload?(c))
208
+ result[:constants][c] = 'auto:' + get_file(auto)
209
+ elsif mod.const_defined? c
210
+ result[:constants][c] = mod.const_get(c).class.inspect
211
+ else
212
+ result[:constants][c] = 'undefined'
213
+ end
214
+ end
215
+ end
216
+ if ! opts[:hide_methods]
217
+ mod.public_instance_methods(false).each do |m|
218
+ add_method result, mod, :instance, :public, m, opts
219
+ end
220
+ mod.singleton_class.public_instance_methods(false).each do |m|
221
+ add_method result, mod, :class, :public, m, opts
222
+ end
223
+ if opts[:show_protected]
224
+ mod.protected_instance_methods(false).each do |m|
225
+ add_method result, mod, :instance, :protected, m, opts
226
+ end
227
+ mod.singleton_class.protected_instance_methods(false).each do |m|
228
+ add_method result, mod, :class, :protected, m, opts
229
+ end
230
+ end
231
+ if opts[:show_private]
232
+ mod.private_instance_methods(false).each do |m|
233
+ add_method result, mod, :instance, :private, m, opts
234
+ end
235
+ mod.singleton_class.private_instance_methods(false).each do |m|
236
+ add_method result, mod, :class, :private, m, opts
237
+ end
238
+ end
239
+ end
240
+ result
241
+ end
242
+
243
+ # @private
244
+ def add_module acc, mod, opts
245
+ if opts[:exclude_classes]
246
+ opts[:exclude_classes].each do |c|
247
+ return nil if mod <= c
248
+ end
249
+ end
250
+ if opts[:filter_classes]
251
+ opts[:filter_classes].each do |c|
252
+ return nil unless mod <= c
253
+ end
254
+ end
255
+ if opts[:exclude_namespaces]
256
+ opts[:exclude_namespaces].each do |n|
257
+ return nil if mod == n || mod.in?(n)
258
+ end
259
+ end
260
+ if opts[:filter_namespaces]
261
+ opts[:filter_namespaces].each do |n|
262
+ return nil unless mod == n || mod.in?(n)
263
+ end
264
+ end
265
+ if opts[:filter_global]
266
+ return nil unless mod.global?
267
+ end
268
+ acc[mod.inspect] = get_module mod, opts
269
+ end
270
+
271
+ # Make hash with data of objectspace.
272
+ #
273
+ # @param [Hash] opts Options (see also {dot} & {diff}).
274
+ # @option opts [Array<Class>] :exclude_classes don't add classes listed and
275
+ # their descendants;
276
+ # @option opts [Array<String>] :exclude_files don't add methods defined in
277
+ # files listed;
278
+ # @option opts [Array<Module>] :exclude_namespaces don't add classes/modules
279
+ # listed and their inners;
280
+ # @option opts [Array<Class>] :filter_classes add only classes listed and
281
+ # their descendants;
282
+ # @option opts [Array<String>] :filter_files add only methods defined in
283
+ # files listed;
284
+ # @option opts [Boolean] :filter_global add only classes/modules in global
285
+ # namespace;
286
+ # @option opts [Array<Module>] :filter_namespaces add only classes/modules
287
+ # listed and their inners;
288
+ # @option opts [Boolean] :hide_arguments don't add arguments info to
289
+ # methods' names;
290
+ # @option opts [Boolean] :hide_constants don't add constants' data;
291
+ # @option opts [Boolean] :hide_methods don't add methods' data;
292
+ # @option opts [Boolean] :select_attributes make attributes data instead
293
+ # getter and setter methods;
294
+ # @option opts [Boolean] :show_private add data of private methods;
295
+ # @option opts [Boolean] :show_protected add data of protected methods.
296
+ # @return [Hash] Objectspace.
297
+ def snapshot opts = {}
298
+ opts = defaults.merge opts
299
+ result = {}
300
+ ObjectSpace.each_object(Module) { |m| add_module result, m, opts }
301
+ result
302
+ end
303
+
304
+ # @private
305
+ def diff_module base, other, opts
306
+ if ! other
307
+ return base.merge :new => true
308
+ end
309
+ if opts[:show_preloaded]
310
+ return base
311
+ end
312
+ result = {}
313
+ result[:module] = base[:module]
314
+ result[:superclass] = base[:superclass]
315
+ result[:nested] = base[:nested]
316
+ result[:included] = base[:included] - other[:included]
317
+ result[:extended] = base[:extended] - other[:extended]
318
+ result[:constants] = {}
319
+ if base[:constants]
320
+ base[:constants].each do |c|
321
+ if base[:constants][c] != other[:constants][c]
322
+ result[:constants][c] = base[:constants][c]
323
+ end
324
+ end
325
+ end
326
+ [:class, :instance].each do |s|
327
+ [:public, :protected, :private].each do |v|
328
+ [:attributes, :methods].each do |k|
329
+ if base[s] && base[s][v] && base[s][v][k]
330
+ base[s][v][k].each do |n, m|
331
+ unless other[s] && other[s][v] && other[s][v][k] &&
332
+ other[s][v][k][n] &&
333
+ other[s][v][k][n][:file] == m[:file] &&
334
+ other[s][v][k][n][:line] == m[:line]
335
+ result[s] ||= {}
336
+ result[s][v] ||= {}
337
+ result[s][v][k] ||= {}
338
+ result[s][v][k][n] = m
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
345
+ if result[:included].empty? && result[:extended].empty? &&
346
+ result[:constants].empty? && result[:class].nil? &&
347
+ result[:instance].nil?
348
+ nil
349
+ else
350
+ if result[:module] == Object
351
+ result[:included] << 'Kernel' if ! result[:included].include?(Kernel)
352
+ end
353
+ result
354
+ end
355
+ end
356
+
357
+ # Make diff between two objectspaces.
358
+ #
359
+ # @param [Hash] base 'New' objectspace.
360
+ # @param [Hash] other 'Old' objectspace
361
+ # @param [Hash] opts Options (see also {snapshot} & {dot}).
362
+ # @option opts [Boolean] :show_preloaded if true old classes/modules will not
363
+ # deleted from difference.
364
+ # @return [Hash] Difference objectspace.
365
+ def diff base, other, opts = {}
366
+ opts = defaults.merge opts
367
+ if other == nil
368
+ return base
369
+ end
370
+ result = {}
371
+ base.each do |n, m|
372
+ d = diff_module m, other[n], opts
373
+ result[n] = d if d
374
+ end
375
+ result
376
+ end
377
+
378
+ # @private
379
+ def find_module space, name
380
+ return space[name] if space[name]
381
+ begin
382
+ mod = sandbox { eval name }
383
+ return get_module(mod, :no_scan => true)
384
+ rescue
385
+ end
386
+ nil
387
+ end
388
+
389
+ # Default values for {dot} options.
390
+ #
391
+ # @return [Hash] see source for details or {dot} for description.
392
+ def defaults
393
+ {
394
+ :graph_fontname => 'sans-serif',
395
+ :graph_fontsize => 24,
396
+ :graph_label => 'RDot Graph',
397
+ :node_fontname => 'monospace',
398
+ :node_fontsize => 9,
399
+ :color_class => '#BBFFBB',
400
+ :color_class_preloaded => '#CCEECC',
401
+ :color_class_core => '#DDFF99',
402
+ :color_exception => '#FFBBBB',
403
+ :color_exception_preloaded => '#EECCCC',
404
+ :color_exception_core => '#FFDD99',
405
+ :color_module => '#BBBBFF',
406
+ :color_module_preloaded => '#CCCCEE',
407
+ :color_module_core => '#99DDFF',
408
+ :color_protected => '#EEEEEE',
409
+ :color_private => '#DDDDDD',
410
+ :color_inherited => '#0000FF',
411
+ :color_included => '#00AAFF',
412
+ :color_extended => '#AA00FF',
413
+ :color_nested => '#EEEEEE'
414
+ }
415
+ end
416
+
417
+ # @private
418
+ def node_name name
419
+ 'node_' + name.gsub(/\W/, '_')
420
+ end
421
+
422
+ # @private
423
+ def module_stage m
424
+ if @preset.include? m[:module]
425
+ :core
426
+ elsif m[:new]
427
+ :new
428
+ else
429
+ :old
430
+ end
431
+ end
432
+
433
+ # @private
434
+ def node_color m, opts
435
+ mod = m[:module]
436
+ stg = module_stage m
437
+ if Class === mod
438
+ if mod <= Exception
439
+ case stg
440
+ when :core
441
+ opts[:color_exception_core]
442
+ when :old
443
+ opts[:color_exception_preloaded]
444
+ when :new
445
+ opts[:color_exception]
446
+ end
447
+ else
448
+ case stg
449
+ when :core
450
+ opts[:color_class_core]
451
+ when :old
452
+ opts[:color_class_preloaded]
453
+ when :new
454
+ opts[:color_class]
455
+ end
456
+ end
457
+ else
458
+ case stg
459
+ when :core
460
+ opts[:color_module_core]
461
+ when :old
462
+ opts[:color_module_preloaded]
463
+ when :new
464
+ opts[:color_module]
465
+ end
466
+ end
467
+ end
468
+
469
+ # @private
470
+ def module_kind m
471
+ stg = module_stage m
472
+ if Class === m[:module]
473
+ if m[:module] <= Exception
474
+ "[#{stg}] exception"
475
+ else
476
+ "[#{stg}] class"
477
+ end
478
+ else
479
+ "[#{stg}] module"
480
+ end
481
+ end
482
+
483
+ # @private
484
+ def escape s
485
+ s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;').gsub("\n", '\n')
486
+ end
487
+
488
+ # @private
489
+ def dot_constants m
490
+ result = []
491
+ if m[:constants]
492
+ m[:constants].sort.each do |n, c|
493
+ result << "#{escape(n.to_s)} &lt;#{escape(c)}&gt;"
494
+ end
495
+ end
496
+ if result.size != 0
497
+ '<TR><TD ROWSPAN="' + result.size.to_s +
498
+ '" ALIGN="RIGHT" VALIGN="TOP">const</TD><TD COLSPAN="3" ALIGN="LEFT">' +
499
+ result.join('</TD></TR><TR><TD COLSPAN="3" ALIGN="LEFT">') +
500
+ '</TD></TR>'
501
+ else
502
+ ''
503
+ end
504
+ end
505
+
506
+ # @private
507
+ def dot_scope m, scope, opts
508
+ result = []
509
+ if m[scope]
510
+ if m[scope][:public]
511
+ if m[scope][:public][:attributes]
512
+ m[scope][:public][:attributes].sort.each do |n, a|
513
+ result << '<TD ALIGN="LEFT">' + escape(a[:signature]) +
514
+ '</TD><TD ALIGN="RIGHT">' + escape(a[:file].to_s) +
515
+ '</TD><TD ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
516
+ end
517
+ end
518
+ if m[scope][:public][:methods]
519
+ m[scope][:public][:methods].sort.each do |n, a|
520
+ result << '<TD ALIGN="LEFT">' + escape(a[:signature]) +
521
+ '</TD><TD ALIGN="RIGHT">' + escape(a[:file].to_s) +
522
+ '</TD><TD ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
523
+ end
524
+ end
525
+ end
526
+ if m[scope][:protected]
527
+ if m[scope][:protected][:attributes]
528
+ m[scope][:protected][:attributes].sort.each do |n, a|
529
+ result << '<TD BGCOLOR="' + opts[:color_protected] +
530
+ '" ALIGN="LEFT">' + escape(a[:signature]) +
531
+ '</TD><TD BGCOLOR="' + opts[:color_protected] +
532
+ '" ALIGN="RIGHT">' + escape(a[:file].to_s) +
533
+ '</TD><TD BGCOLOR="' + opts[:color_protected] +
534
+ '" ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
535
+ end
536
+ end
537
+ if m[scope][:protected][:methods]
538
+ m[scope][:protected][:methods].sort.each do |n, a|
539
+ result << '<TD BGCOLOR="' + opts[:color_protected] +
540
+ '" ALIGN="LEFT">' + escape(a[:signature]) +
541
+ '</TD><TD BGCOLOR="' + opts[:color_protected] +
542
+ '" ALIGN="RIGHT">' + escape(a[:file].to_s) +
543
+ '</TD><TD BGCOLOR="' + opts[:color_protected] +
544
+ '" ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
545
+ end
546
+ end
547
+ end
548
+ if m[scope][:private]
549
+ if m[scope][:private][:attributes]
550
+ m[scope][:private][:attributes].sort.each do |n, a|
551
+ result << '<TD BGCOLOR="' + opts[:color_private] +
552
+ '" ALIGN="LEFT">' + escape(a[:signature]) +
553
+ '</TD><TD BGCOLOR="' + opts[:color_private] +
554
+ '" ALIGN="RIGHT">' + escape(a[:file].to_s) +
555
+ '</TD><TD BGCOLOR="' + opts[:color_private] +
556
+ '" ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
557
+ end
558
+ end
559
+ if m[scope][:private][:methods]
560
+ m[scope][:private][:methods].sort.each do |n, a|
561
+ result << '<TD BGCOLOR="' + opts[:color_private] +
562
+ '" ALIGN="LEFT">' + escape(a[:signature]) +
563
+ '</TD><TD BGCOLOR="' + opts[:color_private] +
564
+ '" ALIGN="RIGHT">' + escape(a[:file].to_s) +
565
+ '</TD><TD BGCOLOR="' + opts[:color_private] +
566
+ '" ALIGN="RIGHT">' + a[:line].to_s + '</TD>'
567
+ end
568
+ end
569
+ end
570
+ end
571
+ if result.size != 0
572
+ '<TR><TD ROWSPAN="' + result.size.to_s +
573
+ '" ALIGN="RIGHT" VALIGN="TOP">' + scope.to_s + '</TD>' +
574
+ result.join('</TR><TR>') + '</TR>'
575
+ else
576
+ ''
577
+ end
578
+ end
579
+
580
+ # @private
581
+ def node_label name, m, opts
582
+ result = []
583
+ result << '<TABLE CELLBORDER="0" CELLSPACING="0">'
584
+ result << '<TR>'
585
+ result << '<TD ALIGN="RIGHT" BGCOLOR="' + node_color(m, opts) + '">'
586
+ result << '<B>'
587
+ result << module_kind(m)
588
+ result << '</B>'
589
+ result << '</TD>'
590
+ result << '<TD COLSPAN="3" ALIGN="LEFT" BGCOLOR="' +
591
+ node_color(m, opts) + '">'
592
+ result << '<B>'
593
+ result << escape(name)
594
+ result << '</B>'
595
+ result << '</TD>'
596
+ result << '</TR>'
597
+ result << dot_constants(m)
598
+ result << dot_scope(m, :class, opts)
599
+ result << dot_scope(m, :instance, opts)
600
+ result << '</TABLE>'
601
+ result.join ''
602
+ end
603
+
604
+ # @private
605
+ def dot_module space, name, m, opts
606
+ if m == nil
607
+ $stderr.puts "Warning: nil module by name \"#{name}\"!"
608
+ return nil
609
+ end
610
+ if @processed.include?(m[:module])
611
+ return nil
612
+ else
613
+ @processed << m[:module]
614
+ end
615
+ result = []
616
+ result << node_name(name) + '['
617
+ result << ' label=<' + node_label(name, m, opts) + '>'
618
+ result << '];'
619
+ if m[:nested] && ! opts[:hide_nested]
620
+ ns = find_module space, m[:nested]
621
+ result << dot_module(space, m[:nested], ns, opts)
622
+ @nested << node_name(m[:nested]) + ' -> ' + node_name(name) + ';'
623
+ end
624
+ if ! opts[:hide_extended]
625
+ m[:extended].each do |e|
626
+ ext = find_module space, e
627
+ result << dot_module(space, e, ext, opts)
628
+ @extended << node_name(e) + ' -> ' + node_name(name) + ';'
629
+ end
630
+ end
631
+ if ! opts[:hide_included]
632
+ m[:included].each do |i|
633
+ next if m[:module].name == 'CMath' && i == 'Math'
634
+ inc = find_module space, i
635
+ result << dot_module(space, i, inc, opts)
636
+ @included << node_name(i) + ' -> ' + node_name(name) + ';'
637
+ end
638
+ end
639
+ if m[:superclass]
640
+ spc = find_module space, m[:superclass]
641
+ result << dot_module(space, m[:superclass], spc, opts)
642
+ @inherited << node_name(m[:superclass]) + ' -> ' + node_name(name) + ';'
643
+ end
644
+ result.join "\n "
645
+ end
646
+
647
+ # Make .dot text from snapshot or difference.
648
+ #
649
+ # @param [Hash] space Snapshot or difference objectspace.
650
+ # @param [Hash] opts Options (see also {snapshot} & {diff}). This Hash will
651
+ # be merged over default values (see {defaults}).
652
+ # @option opts [String] :color_class class node title background color;
653
+ # @option opts [String] :color_class_core core class node title background
654
+ # color;
655
+ # @option opts [String] :color_class_preloaded preloaded class node title
656
+ # background color;
657
+ # @option opts [String] :color_exception exception node title background
658
+ # color;
659
+ # @option opts [String] :color_exception_core core exception node title
660
+ # background color;
661
+ # @option opts [String] :color_exception_preloaded preloaded exception node
662
+ # title background color;
663
+ # @option opts [String] :color_extended color of 'extended' edges;
664
+ # @option opts [String] :color_included color of 'included' edges;
665
+ # @option opts [String] :color_inherited color of 'inherited' edges;
666
+ # @option opts [String] :color_module module node title background color;
667
+ # @option opts [String] :color_module_core core module node title background
668
+ # color;
669
+ # @option opts [String] :color_module_preloaded preloaded module title
670
+ # background color;
671
+ # @option opts [String] :color_nested color of 'nested' edges;
672
+ # @option opts [String] :color_private background color for private methods;
673
+ # @option opts [String] :color_protected background color for protected
674
+ # methods;
675
+ # @option opts [String] :graph_fontname font name of graph title;
676
+ # @option opts [Numeric] :graph_fontsize font size of graph title;
677
+ # @option opts [String] :graph_label graph title;
678
+ # @option opts [Boolean] :hide_extended if true hide 'extended' edges;
679
+ # @option opts [Boolean] :hide_included if true hide 'included' edges;
680
+ # @option opts [Boolean] :hide_nested if true hide 'nested' edges;
681
+ # @option opts [String] :node_fontname font name of class/module nodes;
682
+ # @option opts [Numeric] :node_fontsize font size of class/module nodes.
683
+ # @return [String] Text of .dot-file.
684
+ def dot space, opts = {}
685
+ opts = defaults.merge opts
686
+ result = []
687
+ result << 'digraph graph_RDot{'
688
+ result << ' graph['
689
+ result << ' rankdir=LR,'
690
+ result << ' splines=true,'
691
+ result << ' labelloc=t,'
692
+ result << ' fontname="' + opts[:graph_fontname] + '",'
693
+ result << ' fontsize=' + opts[:graph_fontsize].to_s + ','
694
+ result << ' label="' + opts[:graph_label] + '"'
695
+ result << ' ];'
696
+ result << ' node['
697
+ result << ' shape=plaintext,'
698
+ result << ' fontname="' + opts[:node_fontname] + '",'
699
+ result << ' fontsize=' + opts[:node_fontsize].to_s + ''
700
+ result << ' ];'
701
+ result << ' edge['
702
+ result << ' dir=back,'
703
+ result << ' arrowtail=vee,'
704
+ result << ' penwidth=0.5, arrowsize=0.5'
705
+ result << ' ];'
706
+ @processed = []
707
+ @nested = []
708
+ @extended = []
709
+ @included = []
710
+ @inherited = []
711
+ space.each do |n, m|
712
+ mm = dot_module space, n, m, opts
713
+ result << ' ' + mm if mm
714
+ end
715
+ result << ' subgraph subNested{'
716
+ result << ' edge['
717
+ result << ' color="' + opts[:color_nested] + '",'
718
+ result << ' weight=8,'
719
+ result << ' minlen=0'
720
+ result << ' ];'
721
+ result << ' ' + @nested.join("\n ")
722
+ result << ' }'
723
+ result << ' subgraph subExtended{'
724
+ result << ' edge['
725
+ result << ' color="' + opts[:color_extended] + '",'
726
+ result << ' weight=1,'
727
+ result << ' minlen=0'
728
+ result << ' ];'
729
+ result << ' ' + @extended.join("\n ")
730
+ result << ' }'
731
+ result << ' subgraph subIncluded{'
732
+ result << ' edge['
733
+ result << ' color="' + opts[:color_included] + '",'
734
+ result << ' weight=2,'
735
+ result << ' minlen=1'
736
+ result << ' ];'
737
+ result << ' ' + @included.join("\n ")
738
+ result << ' }'
739
+ result << ' subgraph subInherited{'
740
+ result << ' edge['
741
+ result << ' color="' + opts[:color_inherited] + '",'
742
+ result << ' weight=4,'
743
+ result << ' minlen=1'
744
+ result << ' ];'
745
+ result << ' ' + @inherited.join("\n ")
746
+ result << ' }'
747
+ result << '}'
748
+ result.join "\n"
749
+ end
750
+
751
+ private :get_file, :get_method_object, :get_module, :add_method,
752
+ :add_module, :diff_module, :find_module, :dot_module, :node_name,
753
+ :node_color, :node_label, :module_kind, :dot_constants, :dot_scope,
754
+ :module_stage, :escape
755
+
756
+ end
757
+
758
+ @preset = []
759
+ ObjectSpace.each_object(Module) { |m| @preset << m if m != ::RDot }
760
+
761
+ end
762
+