rcodetools 0.4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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" }
|