rdot 1.0.4
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/.yardopts +9 -0
- data/README.md +152 -0
- data/bin/rdot +531 -0
- data/lib/rdot.rb +762 -0
- metadata +67 -0
data/lib/rdot.rb
ADDED
@@ -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('&', '&').gsub('<', '<').gsub('>', '>').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)} <#{escape(c)}>"
|
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
|
+
|