rcodetools 0.4.0.0
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/CHANGES +18 -0
- data/README +34 -0
- data/README.emacs +54 -0
- data/README.method_analysis +13 -0
- data/README.vim +84 -0
- data/README.xmpfilter +202 -0
- data/Rakefile +123 -0
- data/Rakefile.method_analysis +30 -0
- data/THANKS +6 -0
- data/bin/rct-complete +37 -0
- data/bin/rct-doc +50 -0
- data/bin/rct-meth-args +392 -0
- data/bin/xmpfilter +75 -0
- data/icicles-rcodetools.el +31 -0
- data/lib/method_analyzer.rb +107 -0
- data/lib/rcodetools/completion.rb +282 -0
- data/lib/rcodetools/doc.rb +176 -0
- data/lib/rcodetools/options.rb +83 -0
- data/lib/rcodetools/xmpfilter.rb +208 -0
- data/lib/rcodetools/xmptestunitfilter.rb +197 -0
- data/rcodetools.el +162 -0
- data/rcodetools.vim +118 -0
- data/setup.rb +1585 -0
- data/test/data/add_markers-input.rb +2 -0
- data/test/data/add_markers-output.rb +2 -0
- data/test/data/bindings-input.rb +26 -0
- data/test/data/bindings-output.rb +31 -0
- data/test/data/completion-input.rb +1 -0
- data/test/data/completion-output.rb +2 -0
- data/test/data/completion_emacs-input.rb +1 -0
- data/test/data/completion_emacs-output.rb +5 -0
- data/test/data/completion_emacs_icicles-input.rb +1 -0
- data/test/data/completion_emacs_icicles-output.rb +5 -0
- data/test/data/doc-input.rb +1 -0
- data/test/data/doc-output.rb +1 -0
- data/test/data/method_analyzer-data.rb +33 -0
- data/test/data/method_args.data.rb +106 -0
- data/test/data/no_warnings-input.rb +3 -0
- data/test/data/no_warnings-output.rb +4 -0
- data/test/data/refe-input.rb +1 -0
- data/test/data/refe-output.rb +1 -0
- data/test/data/ri-input.rb +1 -0
- data/test/data/ri-output.rb +1 -0
- data/test/data/ri_emacs-input.rb +1 -0
- data/test/data/ri_emacs-output.rb +1 -0
- data/test/data/ri_vim-input.rb +1 -0
- data/test/data/ri_vim-output.rb +1 -0
- data/test/data/rspec-input.rb +48 -0
- data/test/data/rspec-output.rb +52 -0
- data/test/data/rspec_poetry-input.rb +48 -0
- data/test/data/rspec_poetry-output.rb +52 -0
- data/test/data/simple_annotation-input.rb +8 -0
- data/test/data/simple_annotation-output.rb +8 -0
- data/test/data/unit_test-input.rb +50 -0
- data/test/data/unit_test-output.rb +52 -0
- data/test/data/unit_test_poetry-input.rb +50 -0
- data/test/data/unit_test_poetry-output.rb +52 -0
- data/test/test_completion.rb +467 -0
- data/test/test_doc.rb +403 -0
- data/test/test_functional.rb +18 -0
- data/test/test_method_analyzer.rb +99 -0
- data/test/test_method_args.rb +134 -0
- data/test/test_run.rb +41 -0
- data/test/test_xmpfilter.rb +36 -0
- data/test/test_xmptestunitfilter.rb +84 -0
- metadata +139 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
# Rake tasks to generate TAGS and gather method analysis information.
|
3
|
+
# See README.method_analysis for more information.
|
4
|
+
|
5
|
+
## my standard Rakefile
|
6
|
+
task :tags => "TAGS"
|
7
|
+
|
8
|
+
desc "Generate method_analysis by ruby -rmethod_analyzer."
|
9
|
+
task :analyze => [:_prepare_method_analyze, :tags] do
|
10
|
+
at_exit { sh "ls -l method_analysis" }
|
11
|
+
end
|
12
|
+
|
13
|
+
task :_prepare_method_analyze do
|
14
|
+
ENV['METHOD_ANALYZER_FORMAT']="marshal"
|
15
|
+
sh "rm -f method_analysis"
|
16
|
+
puts "generating method_analysis"
|
17
|
+
end
|
18
|
+
|
19
|
+
## application-specific Rakefile (RTtool)
|
20
|
+
task :analyze do
|
21
|
+
sh "ruby -Ilib -rmethod_analyzer test/test.rb"
|
22
|
+
sh "ruby -Ilib -rmethod_analyzer test/test-rt2html-lib.rb"
|
23
|
+
sh "ruby -Ilib -rmethod_analyzer test/test-rtparser.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
file "TAGS" => FileList["lib/rt/*.rb"] do
|
27
|
+
sh "method_args.rb -t lib/rt/*.rb > TAGS"
|
28
|
+
end
|
29
|
+
|
30
|
+
|
data/THANKS
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Some names forgotten, tell me if you care :) -- mfp
|
2
|
+
|
3
|
+
rubikitch
|
4
|
+
* expanded xmp3.rb (a previous version of xmpfilter.rb) to support RSpec expectations
|
5
|
+
* wrote the elisp magic to use xmpfilter.rb with emacs
|
6
|
+
* made the 100% accurate, editor-independent completion system
|
data/bin/rct-complete
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#! /home/batsman/usr//bin/ruby
|
2
|
+
require 'rcodetools/xmpfilter'
|
3
|
+
require 'rcodetools/completion'
|
4
|
+
require 'rcodetools/options'
|
5
|
+
|
6
|
+
options = DEFAULT_OPTIONS
|
7
|
+
klass = XMPCompletionFilter
|
8
|
+
|
9
|
+
opts = OptionParser.new do |opts|
|
10
|
+
opts.extend OptionHandler
|
11
|
+
opts.set_banner
|
12
|
+
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "Modes:"
|
15
|
+
opts.on("-C", "--completion", "List completion candidates.(default)") do
|
16
|
+
klass = XMPCompletionFilter
|
17
|
+
end
|
18
|
+
opts.on("--completion-emacs", "Generate completion code for Emacs.") do
|
19
|
+
klass = XMPCompletionEmacsFilter
|
20
|
+
end
|
21
|
+
opts.on("--completion-emacs-icicles", "Generate completion code for Emacs/Icicles.") do
|
22
|
+
klass = XMPCompletionEmacsIciclesFilter
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.handle_position options
|
26
|
+
opts.handle_interpreter options
|
27
|
+
opts.handle_misc options
|
28
|
+
end
|
29
|
+
|
30
|
+
set_extra_opts options
|
31
|
+
opts.parse!(ARGV)
|
32
|
+
|
33
|
+
targetcode = ARGF.read
|
34
|
+
Dir.chdir options[:wd] if options[:wd]
|
35
|
+
|
36
|
+
# Do the job. dispatched by klass.
|
37
|
+
puts klass.run(targetcode, options)
|
data/bin/rct-doc
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#! /home/batsman/usr//bin/ruby
|
2
|
+
require 'rcodetools/xmpfilter'
|
3
|
+
require 'rcodetools/doc'
|
4
|
+
require 'rcodetools/options'
|
5
|
+
|
6
|
+
options = DEFAULT_OPTIONS
|
7
|
+
klass = XMPDocFilter
|
8
|
+
|
9
|
+
opts = OptionParser.new do |opts|
|
10
|
+
opts.extend OptionHandler
|
11
|
+
opts.set_banner
|
12
|
+
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "Modes:"
|
15
|
+
opts.on("-D", "--doc", "Print callee method with class.(default)") do
|
16
|
+
klass = XMPDocFilter
|
17
|
+
end
|
18
|
+
opts.on("--refe", "Refe callee method.") do
|
19
|
+
klass = XMPReFeFilter
|
20
|
+
end
|
21
|
+
opts.on("--ri", "Ri callee method.") do
|
22
|
+
klass = XMPRiFilter
|
23
|
+
end
|
24
|
+
opts.on("--ri-emacs", "Generate ri code for emacs.") do
|
25
|
+
klass = XMPRiEmacsFilter
|
26
|
+
end
|
27
|
+
opts.on("--ri-vim", "Generate ri code for vim.") do
|
28
|
+
klass = XMPRiVimFilter
|
29
|
+
end
|
30
|
+
opts.separator ""
|
31
|
+
opts.on("--use-method-analyzer", "") do |n|
|
32
|
+
options[:use_method_analyzer] = true
|
33
|
+
end
|
34
|
+
opts.on("--filename=FILENAME") do |n|
|
35
|
+
options[:filename] = File.expand_path n
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.handle_position options
|
39
|
+
opts.handle_interpreter options
|
40
|
+
opts.handle_misc options
|
41
|
+
end
|
42
|
+
|
43
|
+
set_extra_opts options
|
44
|
+
opts.parse!(ARGV)
|
45
|
+
|
46
|
+
targetcode = ARGF.read
|
47
|
+
Dir.chdir options[:wd] if options[:wd]
|
48
|
+
|
49
|
+
# Do the job. dispatched by klass.
|
50
|
+
puts klass.run(targetcode, options)
|
data/bin/rct-meth-args
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright (c)
|
3
|
+
# 2006 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
|
4
|
+
# 2006 rubikitch <rubikitch@ruby-lang.org> http://www.rubyist.net/~rubikitch/
|
5
|
+
#
|
6
|
+
# Use and distribution subject to the same conditions as Ruby.
|
7
|
+
|
8
|
+
|
9
|
+
$VERBOSE = nil
|
10
|
+
$__method_args_off = true
|
11
|
+
|
12
|
+
if ARGV.empty?
|
13
|
+
puts <<EOF
|
14
|
+
rct-meth-args [-i] [-m] [-c] [-n] <file> [<file> ...]
|
15
|
+
|
16
|
+
-i omit instance methods defined in classes
|
17
|
+
-m omit instance methods defined in modules
|
18
|
+
-c omit class methods
|
19
|
+
|
20
|
+
-n print the filename and line number with output lines
|
21
|
+
-t generate TAGS output
|
22
|
+
|
23
|
+
The given files will be #require()d in order.
|
24
|
+
Examples:
|
25
|
+
ruby method_args.rb complex
|
26
|
+
ruby method_args.rb thread
|
27
|
+
ruby method_args.rb -c rubygems
|
28
|
+
EOF
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
|
32
|
+
def debugprint(*args)
|
33
|
+
$stderr.puts(*args) if $DEBUG
|
34
|
+
end
|
35
|
+
|
36
|
+
module MethodArgs
|
37
|
+
MAX_ARGS = 20
|
38
|
+
|
39
|
+
private
|
40
|
+
def needed_args(arity)
|
41
|
+
arity >= 0 ? arity : arity.abs - 1
|
42
|
+
end
|
43
|
+
|
44
|
+
def with_tracer
|
45
|
+
begin
|
46
|
+
set_trace_func @__trace_func
|
47
|
+
yield
|
48
|
+
ensure
|
49
|
+
set_trace_func nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module LineContents
|
54
|
+
# usage: Contents[filename][lineno]
|
55
|
+
Contents = Hash.new{ |h,k|
|
56
|
+
sum = 0
|
57
|
+
# Unshift is needed because lineno starts with 1.
|
58
|
+
h[k] = File.readlines(k).map{ |line| [line.chomp, sum+=line.length] }.unshift nil
|
59
|
+
}
|
60
|
+
def content
|
61
|
+
Contents[filename][lineno.to_i][0] rescue ""
|
62
|
+
end
|
63
|
+
|
64
|
+
def byte
|
65
|
+
Contents[filename][lineno.to_i-1][1] rescue 0
|
66
|
+
end
|
67
|
+
|
68
|
+
def arg_desc
|
69
|
+
# TODO
|
70
|
+
desc = content[ /\((.+)\)/]
|
71
|
+
desc.gsub!(/(\S),(\S)/, '\1, \2')
|
72
|
+
desc.gsub!(/(\S)=(\S)/, '\1 = \2')
|
73
|
+
desc
|
74
|
+
end
|
75
|
+
|
76
|
+
FileNameCache = Hash.new{ |h,k| h[k] = File.expand_path(k) }
|
77
|
+
def full_filename
|
78
|
+
FileNameCache[filename]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Location < Struct.new(:filename, :lineno)
|
83
|
+
include LineContents
|
84
|
+
end
|
85
|
+
|
86
|
+
class Printer
|
87
|
+
def method_info(io, x)
|
88
|
+
prefix = x[:location] ? "#{x[:location].filename}:#{x[:location].lineno}:" : ""
|
89
|
+
io.puts "#{prefix}#{x[:fullname]}#{x[:arg_desc]}" unless x[:klass].to_s == "<Struct>"
|
90
|
+
end
|
91
|
+
|
92
|
+
def included_location(io, x)
|
93
|
+
x[:callsite] and io.print "#{x[:callsite].filename}:#{x[:callsite].lineno}:"
|
94
|
+
io.puts([ x[:comment], x[:mod], x[:flag], x[:self] ].join(" "))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class TagsPrinter
|
99
|
+
def initialize
|
100
|
+
@previous_filename = ""
|
101
|
+
end
|
102
|
+
|
103
|
+
def output_filename(filename)
|
104
|
+
if @previous_filename != filename
|
105
|
+
@previous_filename = filename
|
106
|
+
puts "\cl"
|
107
|
+
puts "#{filename},0"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_info(io, x)
|
112
|
+
return unless x[:location]
|
113
|
+
output_filename x[:location].full_filename
|
114
|
+
io.puts "#{x[:location].content}::#{x[:fullname]}#{x[:location].lineno},#{x[:location].byte}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def included_location(io, x)
|
118
|
+
return unless x[:callsite]
|
119
|
+
output_filename x[:callsite].full_filename
|
120
|
+
io.puts "#{x[:callsite].content}::#{x[:mod]}#{x[:callsite].lineno},#{x[:callsite].byte}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def _print_method_info(klass, is_singleton, meth, arg_desc=nil, location=nil, with_location=$__with_location, printer=$__printer)
|
125
|
+
arg_desc ||= "(...)"
|
126
|
+
arg_desc = " " + arg_desc unless arg_desc.empty?
|
127
|
+
flag = is_singleton ? "." : "#"
|
128
|
+
x = { :arg_desc => arg_desc, :klass => klass,
|
129
|
+
:fullname => [klass, flag, meth].join,
|
130
|
+
:location => with_location && location,
|
131
|
+
}
|
132
|
+
printer.method_info $>, x
|
133
|
+
end
|
134
|
+
|
135
|
+
def output_attr_info(klass, object, meth, is_singleton, location)
|
136
|
+
arg_desc = meth.to_s =~ /=$/ ? "(value)" : ""
|
137
|
+
_print_method_info klass, is_singleton, meth, arg_desc, location
|
138
|
+
end
|
139
|
+
|
140
|
+
def output_method_info(klass, object, meth, is_singleton = false, by_attr = nil, by_define_method = nil)
|
141
|
+
return if $__method_args_off
|
142
|
+
file = line = params = values = nil
|
143
|
+
location = nil
|
144
|
+
begin
|
145
|
+
unless %w[initialize].include?(meth.to_s)
|
146
|
+
if is_singleton
|
147
|
+
return if class << klass; private_instance_methods(true) end.include?(meth.to_s)
|
148
|
+
else
|
149
|
+
return if class << object; private_instance_methods(true) end.include?(meth.to_s)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
rescue TypeError # no virtual class
|
153
|
+
end
|
154
|
+
|
155
|
+
arity = is_singleton ? object.method(meth).arity : klass.instance_method(meth).arity
|
156
|
+
@__trace_func = lambda{|event, file, line, id, binding, classname|
|
157
|
+
begin
|
158
|
+
debugprint "!EVENT: #{event} #{classname}##{id}, #{file} #{line}"
|
159
|
+
debugprint "(#{self} #{meth})"
|
160
|
+
if event[/call/] && classname == self && id == meth
|
161
|
+
location = Location.new(file, line) if event == 'call'
|
162
|
+
debugprint "EVENT: #{event} #{classname}##{id}"
|
163
|
+
throw :done
|
164
|
+
end
|
165
|
+
rescue Exception
|
166
|
+
nil # rcov workaround
|
167
|
+
end
|
168
|
+
}
|
169
|
+
if by_define_method
|
170
|
+
_print_method_info klass, is_singleton, meth, nil, by_define_method
|
171
|
+
return
|
172
|
+
end
|
173
|
+
|
174
|
+
if by_attr
|
175
|
+
output_attr_info klass, object, meth, is_singleton, by_attr
|
176
|
+
return
|
177
|
+
end
|
178
|
+
|
179
|
+
catch(:done) do
|
180
|
+
begin
|
181
|
+
with_tracer { object.send(meth, *(0...needed_args(arity))) }
|
182
|
+
rescue Exception
|
183
|
+
nil # rcov workaround
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
arg_desc = location.arg_desc
|
188
|
+
|
189
|
+
_print_method_info klass, is_singleton, meth, arg_desc, location
|
190
|
+
rescue Exception
|
191
|
+
debugprint "GOT EXCEPTION while processing #{klass} #{meth}"
|
192
|
+
debugprint $!.class
|
193
|
+
debugprint $!.message
|
194
|
+
debugprint $!.backtrace
|
195
|
+
_print_method_info klass, is_singleton, meth, nil, location
|
196
|
+
ensure
|
197
|
+
set_trace_func(nil)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Object
|
202
|
+
include MethodArgs
|
203
|
+
end
|
204
|
+
|
205
|
+
class Module
|
206
|
+
class CallSite # compatible with Location
|
207
|
+
include MethodArgs::LineContents
|
208
|
+
def initialize(caller_string)
|
209
|
+
@filename, @lineno, @in = caller_string.split(/:/)
|
210
|
+
end
|
211
|
+
attr_reader :filename, :lineno
|
212
|
+
end
|
213
|
+
|
214
|
+
def _method_defined_with(ivar, caller, &block)
|
215
|
+
begin
|
216
|
+
instance_variable_set(ivar, caller)
|
217
|
+
yield
|
218
|
+
ensure
|
219
|
+
instance_variable_set(ivar, false)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def with_attr(caller, &block)
|
224
|
+
_method_defined_with :@by_attr, caller, &block
|
225
|
+
end
|
226
|
+
|
227
|
+
def with_define_method(caller, &block)
|
228
|
+
_method_defined_with :@by_define_method, caller, &block
|
229
|
+
end
|
230
|
+
|
231
|
+
[ :attr, :attr_reader, :attr_writer, :attr_accessor ].each do |meth|
|
232
|
+
attr = instance_method(meth)
|
233
|
+
define_method(meth) do |*args|
|
234
|
+
with_attr(CallSite.new(caller(1)[0])){ attr.bind(self).call(*args) }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
alias :__define_method_orig :define_method
|
239
|
+
def define_method(*args, &body)
|
240
|
+
with_define_method(CallSite.new(caller(1)[0])){ __define_method_orig(*args, &body) }
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
new_args = []
|
246
|
+
omissions = {}
|
247
|
+
$__with_location = false
|
248
|
+
$__printer = Printer.new
|
249
|
+
|
250
|
+
ARGV.each do |arg|
|
251
|
+
case arg
|
252
|
+
when "-n"; $__with_location = true
|
253
|
+
when "-t"; $__printer = TagsPrinter.new; $__with_location = true
|
254
|
+
when /-./; omissions[$&] = true
|
255
|
+
else new_args << arg
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
ARGV.replace new_args
|
260
|
+
|
261
|
+
class Object
|
262
|
+
ALLOCATOR = Hash.new{|h,k| h[k] = k.allocate }.merge(Fixnum=>1,
|
263
|
+
Bignum=>10000000000000000,
|
264
|
+
Float=>1.1,
|
265
|
+
Symbol=>:method_args_tmp,
|
266
|
+
Binding=>binding,
|
267
|
+
UnboundMethod=>instance_method(:object_id),
|
268
|
+
Method=>method(:object_id),
|
269
|
+
Proc=>lambda{},
|
270
|
+
Continuation=>callcc{|c|c},
|
271
|
+
Thread=>Thread.new{},
|
272
|
+
FalseClass=>false,
|
273
|
+
TrueClass=>true,
|
274
|
+
NilClass=>nil,
|
275
|
+
Struct=>Struct.new(:a).new(1))
|
276
|
+
|
277
|
+
# ABSTRACT_CLASSES = %w[Digest::Base ]
|
278
|
+
def self.method_added(meth)
|
279
|
+
# if ABSTRACT_CLASSES.include? self.to_s
|
280
|
+
# _print_method_info self, false, meth
|
281
|
+
# return
|
282
|
+
# end
|
283
|
+
begin
|
284
|
+
o = ALLOCATOR[self]
|
285
|
+
rescue Exception # for abstract classes
|
286
|
+
nil
|
287
|
+
end
|
288
|
+
output_method_info(self, o, meth, false, @by_attr, @by_define_method)
|
289
|
+
end
|
290
|
+
end unless omissions["-i"]
|
291
|
+
|
292
|
+
class Module
|
293
|
+
method_added = instance_method(:method_added)
|
294
|
+
define_method(:method_added) do |meth|
|
295
|
+
begin
|
296
|
+
if instance_of? Module
|
297
|
+
o = Object.new
|
298
|
+
o.extend(self)
|
299
|
+
output_method_info(self, o, meth, false, @by_attr, @by_define_method)
|
300
|
+
end
|
301
|
+
method_added.bind(self).call(meth)
|
302
|
+
rescue Exception
|
303
|
+
_print_method_info self, false, meth
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end unless omissions["-m"]
|
307
|
+
|
308
|
+
|
309
|
+
class Class
|
310
|
+
def singleton_method_added(meth)
|
311
|
+
by_attr = class << self; @by_attr; end
|
312
|
+
by_define_method = class << self; @by_define_method; end
|
313
|
+
output_method_info(self, self, meth, true, by_attr, by_define_method)
|
314
|
+
rescue Exception
|
315
|
+
_print_method_info self, true, meth
|
316
|
+
end
|
317
|
+
end unless omissions["-c"]
|
318
|
+
|
319
|
+
#### include / extend
|
320
|
+
class Module
|
321
|
+
def _print_included_location(mod, caller_string, comment, flag, with_location=$__with_location, printer=$__printer)
|
322
|
+
if caller_string # nil when the class is defined by C
|
323
|
+
x = { :comment => comment, :mod => mod, :flag => flag, :self => self,
|
324
|
+
:callsite => with_location && CallSite.new(caller_string)
|
325
|
+
}
|
326
|
+
printer.included_location $>, x
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
undef_method :included
|
331
|
+
def included(mod)
|
332
|
+
_print_included_location mod, caller(0)[1], "include", "<="
|
333
|
+
end
|
334
|
+
|
335
|
+
undef_method :extended
|
336
|
+
def extended(mod)
|
337
|
+
_print_included_location mod, caller(0)[1], "extend", "<-" if Module === mod
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
#### inheritance
|
342
|
+
class Class
|
343
|
+
undef :inherited
|
344
|
+
def inherited(klass, caller_level=0)
|
345
|
+
_print_included_location klass, caller(caller_level)[1], "class", "<" unless self == Object
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
#### Struct inspection
|
350
|
+
class << Struct
|
351
|
+
def to_s
|
352
|
+
orig = super
|
353
|
+
if orig =~ /<Class/
|
354
|
+
mems = instance_methods(false).select{ |m| m.to_s !~ /=$/}.sort.join(",")
|
355
|
+
mems.empty? ? "<Struct>" : "<Struct: #{mems}>"
|
356
|
+
else
|
357
|
+
orig
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def inherited(klass)
|
362
|
+
if self == Struct
|
363
|
+
@inherited_delay = lambda{ super(klass, 3) }
|
364
|
+
else
|
365
|
+
super(klass, 1)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
@@struct_initializing = false
|
370
|
+
def method_added(meth)
|
371
|
+
super unless @@struct_initializing
|
372
|
+
end
|
373
|
+
|
374
|
+
orig_new = instance_method(:new)
|
375
|
+
define_method(:new) do |*args|
|
376
|
+
begin
|
377
|
+
@@struct_initializing = true
|
378
|
+
orig_new.bind(self).call(*args)
|
379
|
+
ensure
|
380
|
+
@@struct_initializing = false
|
381
|
+
@inherited_delay[]
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
386
|
+
|
387
|
+
$__method_args_off = false
|
388
|
+
|
389
|
+
ARGV.each{|x| require x}
|
390
|
+
|
391
|
+
$__method_args_off = true
|
392
|
+
# END { puts "zzzz OK" }
|