unroller 0.0.12 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme +12 -3
- data/lib/unroller.rb +579 -162
- metadata +2 -2
data/Readme
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
[<b>Home page</b>:] http://unroller.rubyforge.org/
|
4
4
|
[<b>Project site</b>:] http://rubyforge.org/projects/unroller/
|
5
|
+
[<b>Gem install</b>:] <tt>gem install unroller</tt>
|
5
6
|
[<b>Wiki</b>:] http://whynotwiki.com/Ruby_Unroller
|
6
7
|
[<b>Author</b>:] Tyler Rick
|
7
8
|
[<b>Copyright</b>:] 2007 QualitySmith, Inc.
|
8
|
-
[<b>License</b>:]
|
9
|
+
[<b>License</b>:] {GNU General Public License}[http://www.gnu.org/copyleft/gpl.html]
|
9
10
|
|
10
11
|
==Introduction / What it is
|
11
12
|
|
@@ -153,8 +154,15 @@ If you enable tracing from within that method, it will only show the calls that
|
|
153
154
|
So... you could always <tt>p caller(0)</tt> within your mystery function and then put a trace around one of the calls leading up to this call, a little further up the stack...
|
154
155
|
|
155
156
|
I know, wouldn't it be nice if you could just tell it, "Show me the execution traces for the 3 calls leading up to this call"? But alas, Unroller can only affect what happens _after_ it gets called, not before.
|
156
|
-
If I ever get around to writing an interactive tree-based UI for this, then I guess we could give the _impression_ of being able to show calls leading up to a certain call. But only by recording *all* calls and then hiding everything except the calls you're interested in...
|
157
|
+
*If I ever get around to writing an interactive tree-based UI for this, then I guess we could give the _impression_ of being able to show calls leading up to a certain call. But only by recording *all* calls and then hiding everything except the calls you're interested in...
|
158
|
+
*If it is completely reproduceable, then we could even do it without the tree-based UI. We could just keep track of the "executed line number" at which the interesting event occured (line_number_of_interest), then subtract however many lines we want to see leading up to that call (introductory_lines_count), re-run the script, and have it stop after (line_number_of_interest - introductory_lines_count) lines are executed.
|
157
159
|
|
160
|
+
===When did that constant get set the _first_ time??===
|
161
|
+
|
162
|
+
If you start getting errors like "warning: already initialized constant OPTIONS", then you may be wondering when it was first initialized (something the backtrace fails to tell you).
|
163
|
+
|
164
|
+
Here's one way you could try to answer that question...
|
165
|
+
Unroller::trace :line_matches => 'OPTIONS ='.to_re
|
158
166
|
|
159
167
|
===Usage in Rails
|
160
168
|
|
@@ -230,7 +238,8 @@ You're welcome to submit comments, ideas, and/or patches.
|
|
230
238
|
* Make a GUI interface that lets you quickly collapse/nodes nodes of the tree.
|
231
239
|
* :include_classes option in addition to :exclude_classes?
|
232
240
|
* Have some "presets" for what you might want to exclude if tracing an ActiveRecord request for example. In that case, you probably don't want to see the internals of any support code, like any methods from ActiveSupport.
|
233
|
-
* :
|
241
|
+
* :rails => true
|
242
|
+
* :preset => :Rails : Exclude ActiveSupport, Dependencies, etc.
|
234
243
|
* :preset => :ActiveSupport : Exclude ActiveRecord, etc.
|
235
244
|
* :preset => :'ActiveRecord high level' : excludes the lowel-level database stuff (like the individual adapter (SQLite, MySQL, ...).
|
236
245
|
* :preset => :'ActiveRecord low level'
|
data/lib/unroller.rb
CHANGED
@@ -4,6 +4,7 @@ require 'facets/core/module/namespace'
|
|
4
4
|
require 'facets/core/kernel/with'
|
5
5
|
require 'facets/core/kernel/set_with'
|
6
6
|
require 'facets/core/string/bracket'
|
7
|
+
require 'facets/core/kernel/singleton_class'
|
7
8
|
gem 'qualitysmith_extensions'
|
8
9
|
require 'qualitysmith_extensions/object/send_if_not_nil'
|
9
10
|
require 'qualitysmith_extensions/kernel/trap_chain'
|
@@ -11,6 +12,11 @@ require 'qualitysmith_extensions/kernel/capture_output'
|
|
11
12
|
require 'qualitysmith_extensions/string/with_knowledge_of_color'
|
12
13
|
require 'qualitysmith_extensions/exception/inspect_with_backtrace'
|
13
14
|
require 'qualitysmith_extensions/symbol/match'
|
15
|
+
require 'qualitysmith_extensions/module/alias_method_chain'
|
16
|
+
require 'qualitysmith_extensions/module/malias_method_chain'
|
17
|
+
require 'qualitysmith_extensions/module/attribute_accessors'
|
18
|
+
require 'qualitysmith_extensions/enumerable/select_until'
|
19
|
+
require 'qualitysmith_extensions/module/bool_attr_accessor'
|
14
20
|
gem 'colored'
|
15
21
|
require 'colored'
|
16
22
|
gem 'extensions'
|
@@ -26,8 +32,10 @@ require 'extensions/symbol' # to_proc
|
|
26
32
|
|
27
33
|
|
28
34
|
|
29
|
-
#
|
30
35
|
class Unroller
|
36
|
+
#-------------------------------------------------------------------------------------------------
|
37
|
+
# Helper classes
|
38
|
+
|
31
39
|
class Variables
|
32
40
|
def initialize(which, binding)
|
33
41
|
@variables = eval("#{which}_variables", binding).map { |variable|
|
@@ -48,6 +56,28 @@ class Unroller
|
|
48
56
|
end
|
49
57
|
@@instance = nil
|
50
58
|
|
59
|
+
Call = Struct.new(:file, :line_num, :klass, :name, :full_name)
|
60
|
+
# AKA stack frame?
|
61
|
+
|
62
|
+
class ClassExclusion
|
63
|
+
bool_attr_reader :recursive
|
64
|
+
attr_reader :regexp
|
65
|
+
def initialize(klass, *flags)
|
66
|
+
raise ArgumentError if !(Module === klass || String === klass || Symbol === klass || Regexp === klass)
|
67
|
+
klass = klass.name if Module === klass
|
68
|
+
@regexp =
|
69
|
+
Regexp === klass ?
|
70
|
+
klass :
|
71
|
+
/^#{klass.to_s}$/ # (Or should we escape it?)
|
72
|
+
@recursive = true if flags.include?(:recursive)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Helper classes
|
77
|
+
#-------------------------------------------------------------------------------------------------
|
78
|
+
|
79
|
+
|
80
|
+
|
51
81
|
def self.trace(options = {}, &block)
|
52
82
|
#puts 'called self.trace'
|
53
83
|
if @@instance and @@instance.tracing
|
@@ -56,6 +86,7 @@ class Unroller
|
|
56
86
|
#return if @@instance and @@instance.tracing
|
57
87
|
#yield if block_given?
|
58
88
|
else
|
89
|
+
self.display_style = options.delete(:display_style) if options.has_key?(:display_style)
|
59
90
|
@@instance = Unroller.new(options)
|
60
91
|
end
|
61
92
|
@@instance.trace &block
|
@@ -64,24 +95,32 @@ class Unroller
|
|
64
95
|
attr_accessor :depth
|
65
96
|
attr_reader :tracing
|
66
97
|
|
98
|
+
mattr_accessor :display_style
|
99
|
+
|
67
100
|
def initialize(options = {})
|
68
101
|
# Defaults
|
69
102
|
@condition = Proc.new { true } # Only trace if this condition is true. Useful if the place where you put your trace {} statement gets called a lot and you only want it to actually trace for some of those calls.
|
70
103
|
@initial_depth = 1 # ("Call stack") depth to start at. Actually, you'll probably want this set considerably lower than the current call stack depth, so that the indentation isn't way off the screen.
|
71
104
|
@max_lines = nil # Stop tracing (permanently) after we have produced @max_lines lines of output. If you don't know where to place the trace(false) and you just want it to stop on its own after so many lines, you could use this...
|
72
105
|
@max_depth = nil # Don't trace anything when the depth is greater than this threshold. (This is *relative* to the starting depth, so whatever level you start at is considered depth "1".)
|
106
|
+
@line_matches = nil # The source code for that line matches this regular expression
|
107
|
+
@presets = []
|
73
108
|
@exclude_classes = []
|
74
109
|
@include_classes = [] # These will override classes that have been excluded via exclude_classes. So if you both exclude and include a class, it will be included.
|
110
|
+
@exclude_methods = []
|
111
|
+
@include_methods = []
|
75
112
|
@show_args = true
|
76
113
|
@show_locals = false
|
77
114
|
@show_filename_and_line_numbers = true
|
78
|
-
@include_c_calls = false # Might be useful if you're debugging your own C extension. Otherwise, we
|
115
|
+
@include_c_calls = false # Might be useful if you're debugging your own C extension. Otherwise, we probably don't care about C-calls because we can't see the source for them anyway...
|
79
116
|
@strip_comments = true # :todo:
|
80
117
|
@use_full_path = false # :todo:
|
81
118
|
@screen_width = 150
|
82
119
|
@column_widths = [70]
|
83
120
|
@indent_step = ' ' + '|'.magenta + ' '
|
84
121
|
@column_separator = ' ' + '|'.yellow.bold + ' '
|
122
|
+
@always_show_raise_events = false
|
123
|
+
@show_file_load_errors = false
|
85
124
|
instance_variables.each do |v|
|
86
125
|
self.class.class_eval do
|
87
126
|
attr_accessor v.gsub!(/^@/, '')
|
@@ -90,13 +129,19 @@ class Unroller
|
|
90
129
|
|
91
130
|
# "Presets"
|
92
131
|
# Experimental -- subject to change a lot before it's finalized
|
93
|
-
[:
|
94
|
-
|
132
|
+
options[:presets] = options.delete(:debugging) if options.has_key?(:debugging)
|
133
|
+
options[:presets] = options.delete(:preset) if options.has_key?(:preset)
|
134
|
+
options[:presets] = [options[:presets]] unless options[:presets].is_a?(Array)
|
135
|
+
[:rails, :dependencies].each do |preset|
|
136
|
+
if options.has_key?(preset) || options[:presets].include?(preset)
|
95
137
|
options.delete(preset)
|
96
138
|
case preset
|
139
|
+
when :dependencies # Debugging ActiveSupport::Dependencies
|
140
|
+
@exclude_classes.concat [
|
141
|
+
/Gem/
|
142
|
+
].map {|e| ClassExclusion.new(e) }
|
97
143
|
when :rails
|
98
|
-
|
99
|
-
options[:exclude_classes].concat [
|
144
|
+
@exclude_classes.concat [
|
100
145
|
/Benchmark/,
|
101
146
|
/Gem/,
|
102
147
|
/Dependencies/,
|
@@ -110,33 +155,52 @@ class Unroller
|
|
110
155
|
/Class/,
|
111
156
|
/ActiveSupport/,
|
112
157
|
/ActiveSupport::Deprecation/,
|
113
|
-
/Pathname
|
114
|
-
|
158
|
+
/Pathname/,
|
159
|
+
/Object/,
|
160
|
+
/Symbol/,
|
161
|
+
/Kernel/,
|
162
|
+
/Inflector/,
|
163
|
+
/Webrick/
|
164
|
+
].map {|e| ClassExclusion.new(e) }
|
115
165
|
end
|
116
|
-
|
117
166
|
end
|
118
167
|
end
|
119
168
|
|
120
169
|
# Options
|
170
|
+
options[:max_lines] = options.delete(:head) if options.has_key?(:head)
|
121
171
|
options[:condition] = options.delete(:if) if options.has_key?(:if)
|
122
172
|
options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
|
123
173
|
options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
|
124
174
|
if options.has_key?(:exclude_classes)
|
125
|
-
# Coerce it into an
|
126
|
-
|
127
|
-
|
175
|
+
# Coerce it into an array of ClassExclusions
|
176
|
+
a = options.delete(:exclude_classes)
|
177
|
+
a = [a] unless a.is_a?(Array)
|
178
|
+
a.map! {|e| e = ClassExclusion.new(e) unless e.is_a?(ClassExclusion); e }
|
179
|
+
@exclude_classes.concat a
|
128
180
|
end
|
129
181
|
if options.has_key?(:include_classes)
|
130
182
|
# :todo:
|
131
183
|
end
|
184
|
+
if options.has_key?(:exclude_methods)
|
185
|
+
# Coerce it into an array of Regexp's
|
186
|
+
a = options.delete(:exclude_methods)
|
187
|
+
a = [a] unless a.is_a?(Array)
|
188
|
+
a.map! {|e| e = /^#{e}$/ unless e.is_a?(Regexp); e }
|
189
|
+
@exclude_methods.concat a
|
190
|
+
end
|
191
|
+
options[:line_matches] = options.delete(:line_matches) if options.has_key?(:line_matches)
|
132
192
|
set_with(options)
|
133
193
|
|
134
194
|
# Private
|
135
195
|
@call_stack = [] # Used to keep track of what method we're currently in so that when we hit a 'return' event we can display something useful.
|
136
196
|
# This is useful for two reasons:
|
137
|
-
# 1. Sometimes (and I don't know why), the code that gets shown for a 'return' event doesn't even look
|
197
|
+
# 1. Sometimes (and I don't know why), the code that gets shown for a 'return' event doesn't even look
|
198
|
+
# like it has anything to do with a return... Having the call stack lets us intelligently say 'returning from ...'
|
138
199
|
# 2. If we've been stuck in this method for a long time and we're really deep, the user has probably forgotten by now which method we are returning from
|
139
200
|
# (the filename may give some clue, but not enough), so this is likely to be a welcome reminder a lot of the time.
|
201
|
+
# Also using it to store line numbers, so that we can show the entire method definition each time we process a line,
|
202
|
+
# rather than just the current line itself.
|
203
|
+
# Its members are of type Call
|
140
204
|
@internal_depth = 0 # This is the "true" depth. It is incremented/decremented for *every* call/return, even those that we're not displaying. It is necessary for the implementation of "excluding_calls_made_within_unintersting_call", to detect when we get back to interesting land.
|
141
205
|
@depth = @initial_depth # This is the user-friendly depth. It only counts calls/returns that we *display*; it does not change when we enter into a call that we're not displaying (a "hidden" call).
|
142
206
|
@output_line = ''
|
@@ -187,6 +251,8 @@ class Unroller
|
|
187
251
|
begin # begin/rescue block
|
188
252
|
@event, @file, @line, @id, @binding, @klass =
|
189
253
|
event, file, line, id, binding, klass
|
254
|
+
line_num = line
|
255
|
+
current_call = Call.new(file, line, klass, id, fully_qualified_method)
|
190
256
|
|
191
257
|
# Sometimes klass is false and id is nil. Not sure why, but that's the way it is.
|
192
258
|
#printf "- (event=%8s) (klass=%10s) (id=%10s) (%s:%-2d)\n", event, klass, id, file, line #if klass.to_s == 'false'
|
@@ -202,96 +268,201 @@ class Unroller
|
|
202
268
|
return
|
203
269
|
end
|
204
270
|
|
205
|
-
case
|
271
|
+
case @@display_style
|
206
272
|
|
273
|
+
# To do: remove massive duplication with default display style
|
274
|
+
when :show_entire_method_body
|
207
275
|
|
276
|
+
case event
|
208
277
|
|
209
|
-
when 'call'
|
210
|
-
unless skip_line?
|
211
|
-
# :todo: use # instead of :: if klass.constantize.instance_methods.include?(id)
|
212
|
-
column sprintf(' ' + '+'.cyan + ' calling'.cyan + ' ' + '%s'.underline.cyan, fully_qualified_method), @column_widths[0]
|
213
|
-
newline
|
214
278
|
|
215
|
-
|
216
|
-
|
217
|
-
|
279
|
+
#zzz
|
280
|
+
when 'call'
|
281
|
+
unless skip_line?
|
282
|
+
column sprintf(' ' + '\\'.cyan + ' calling'.cyan + ' ' + '%s'.underline.cyan, fully_qualified_method), @column_widths[0]
|
283
|
+
newline
|
218
284
|
|
219
|
-
|
285
|
+
puts
|
286
|
+
header_before_code_for_entire_method(file, line_num)
|
287
|
+
do_show_locals if show_args
|
288
|
+
puts code_for_entire_method(file, line, @klass, @id, line, 0)
|
289
|
+
puts
|
220
290
|
|
221
|
-
|
291
|
+
@lines_output += 1
|
222
292
|
|
223
|
-
|
224
|
-
#puts "++ Increased depth to #{depth}"
|
293
|
+
@call_stack.push current_call
|
225
294
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
@internal_depth += 1
|
295
|
+
@depth += 1
|
296
|
+
end
|
297
|
+
@internal_depth += 1
|
230
298
|
|
231
299
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
300
|
+
when 'class'
|
301
|
+
when 'end'
|
302
|
+
when 'line'
|
303
|
+
unless skip_line?
|
304
|
+
# Show the state of the locals *before* executing the current line. (I might add the option to show it after instead/as well, but I don't think that would be easy...)
|
305
|
+
do_show_locals if show_locals
|
306
|
+
|
307
|
+
inside_of = @call_stack.last
|
308
|
+
if inside_of
|
309
|
+
unless @last_call == current_call # Without this, I was seeing two consecutive events for the exact same line. This seems to be necessary because 'if foo()' is treated as two 'line' events: one for 'foo()' and one for 'if' (?)...
|
310
|
+
puts
|
311
|
+
header_before_code_for_entire_method(file, line_num)
|
312
|
+
puts code_for_entire_method(inside_of.file, inside_of.line_num, @klass, @id, line, -1)
|
313
|
+
puts
|
314
|
+
end
|
315
|
+
else
|
316
|
+
column pretty_code_for(file, line, ' ', :bold), @column_widths[0]
|
317
|
+
file_column file, line
|
318
|
+
newline
|
319
|
+
end
|
320
|
+
|
321
|
+
@last_call = current_call
|
322
|
+
@lines_output += 1
|
323
|
+
end
|
324
|
+
|
238
325
|
|
239
|
-
column code_for(file, line, ' ', :bold), @column_widths[0]
|
240
|
-
file_column file, line
|
241
|
-
newline
|
242
326
|
|
243
|
-
|
244
|
-
end
|
327
|
+
when 'return'
|
245
328
|
|
329
|
+
@internal_depth -= 1
|
330
|
+
unless skip_line?
|
331
|
+
puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth-1 < 0
|
332
|
+
@depth -= 1 unless @depth == 0
|
333
|
+
#puts "-- Decreased depth to #{depth}"
|
334
|
+
returning_from = @call_stack.pop
|
246
335
|
|
336
|
+
column sprintf(' ' + '/'.cyan + ' returning from'.cyan + ' ' + '%s'.cyan, returning_from && returning_from.full_name), @column_widths[0]
|
337
|
+
newline
|
247
338
|
|
248
|
-
|
339
|
+
@lines_output += 1
|
340
|
+
end
|
249
341
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
342
|
+
# Did we just get out of an uninteresting call?? Are we back in interesting land again??
|
343
|
+
if @excluding_calls_made_within_unintersting_call and
|
344
|
+
@excluding_calls_made_within_unintersting_call == @internal_depth
|
345
|
+
puts "Yay, we're back in interesting land!"
|
346
|
+
@excluding_calls_made_within_unintersting_call = nil
|
347
|
+
end
|
256
348
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
#
|
262
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
349
|
+
|
350
|
+
|
351
|
+
when 'raise'
|
352
|
+
if !skip_line? or @always_show_raise_events
|
353
|
+
# We probably always want to see these (?)... Never skip displaying them, even if we are "too deep".
|
354
|
+
column "Raising an error (#{$!}) from #{klass}".red.bold, @column_widths[0]
|
355
|
+
newline
|
356
|
+
|
357
|
+
column pretty_code_for(file, line, ' ').red, @column_widths[0]
|
358
|
+
file_column file, line
|
359
|
+
newline
|
360
|
+
end
|
361
|
+
|
362
|
+
else
|
363
|
+
column sprintf("- (%8s) %10s %10s (%s:%-2d)", event, klass, id, file, line)
|
268
364
|
newline
|
365
|
+
end # case event
|
269
366
|
|
270
|
-
|
271
|
-
end
|
367
|
+
# End when :show_entire_method_body
|
272
368
|
|
273
|
-
|
274
|
-
|
275
|
-
@excluding_calls_made_within_unintersting_call == @internal_depth
|
276
|
-
puts "Yay, we're back in interesting land!"
|
277
|
-
@excluding_calls_made_within_unintersting_call = nil
|
278
|
-
end
|
369
|
+
else
|
370
|
+
case event
|
279
371
|
|
280
372
|
|
373
|
+
when 'call'
|
374
|
+
unless skip_line?
|
375
|
+
# :todo: use # instead of :: if klass.constantize.instance_methods.include?(id)
|
376
|
+
column sprintf(' ' + '+'.cyan + ' calling'.cyan + ' ' + '%s'.underline.cyan, fully_qualified_method), @column_widths[0]
|
377
|
+
newline
|
281
378
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
newline
|
379
|
+
column pretty_code_for(file, line, '/'.magenta, :green), @column_widths[0]
|
380
|
+
file_column file, line
|
381
|
+
newline
|
286
382
|
|
287
|
-
|
288
|
-
|
289
|
-
|
383
|
+
@lines_output += 1
|
384
|
+
|
385
|
+
@call_stack.push Call.new(file, line, klass, id, fully_qualified_method)
|
386
|
+
|
387
|
+
@depth += 1
|
388
|
+
#puts "++ Increased depth to #{depth}"
|
389
|
+
|
390
|
+
# The locals at this point will be simply be the arguments that were passed in to this method.
|
391
|
+
do_show_locals if show_args
|
392
|
+
end
|
393
|
+
@internal_depth += 1
|
394
|
+
|
395
|
+
|
396
|
+
when 'class'
|
397
|
+
when 'end'
|
398
|
+
when 'line'
|
399
|
+
unless skip_line?
|
400
|
+
# Show the state of the locals *before* executing the current line. (I might add the option to show it after instead/as well, but I don't think that would be easy...)
|
401
|
+
do_show_locals if show_locals
|
402
|
+
|
403
|
+
column pretty_code_for(file, line, ' ', :bold), @column_widths[0]
|
404
|
+
file_column file, line
|
405
|
+
newline
|
406
|
+
|
407
|
+
@lines_output += 1
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
|
412
|
+
when 'return'
|
413
|
+
|
414
|
+
@internal_depth -= 1
|
415
|
+
unless skip_line?
|
416
|
+
puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth-1 < 0
|
417
|
+
@depth -= 1 unless @depth == 0
|
418
|
+
#puts "-- Decreased depth to #{depth}"
|
419
|
+
returning_from = @call_stack.pop
|
420
|
+
|
421
|
+
code = pretty_code_for(file, line, '\\'.magenta, :green, suffix = " (returning from #{returning_from && returning_from.full_name})".green)
|
422
|
+
code = pretty_code_for(file, line, '\\'.magenta + " (returning from #{returning_from && returning_from.full_name})".green, :green) unless code =~ /return|end/
|
423
|
+
# I've seen some really weird statements listed as "return" statements.
|
424
|
+
# I'm not really sure *why* it thinks these are returns, but let's at least identify those lines for the user. Examples:
|
425
|
+
# * must_be_open!
|
426
|
+
# * @db = db
|
427
|
+
# * stmt = @statement_factory.new( self, sql )
|
428
|
+
# I think some of the time this happens it might be because people pass the wrong line number to eval (__LINE__ instead of __LINE__ + 1, for example), so the line number is just not accurate.
|
429
|
+
# But I don't know if that explains all such cases or not...
|
430
|
+
column code, @column_widths[0]
|
431
|
+
file_column file, line
|
432
|
+
newline
|
433
|
+
|
434
|
+
@lines_output += 1
|
435
|
+
end
|
436
|
+
|
437
|
+
# Did we just get out of an uninteresting call?? Are we back in interesting land again??
|
438
|
+
if @excluding_calls_made_within_unintersting_call and
|
439
|
+
@excluding_calls_made_within_unintersting_call == @internal_depth
|
440
|
+
puts "Yay, we're back in interesting land!"
|
441
|
+
@excluding_calls_made_within_unintersting_call = nil
|
442
|
+
end
|
443
|
+
|
444
|
+
|
445
|
+
|
446
|
+
when 'raise'
|
447
|
+
if !skip_line? or @always_show_raise_events
|
448
|
+
# We probably always want to see these (?)... Never skip displaying them, even if we are "too deep".
|
449
|
+
column "Raising an error (#{$!}) from #{klass}".red.bold, @column_widths[0]
|
450
|
+
newline
|
451
|
+
|
452
|
+
column pretty_code_for(file, line, ' ').red, @column_widths[0]
|
453
|
+
file_column file, line
|
454
|
+
newline
|
455
|
+
end
|
456
|
+
|
457
|
+
else
|
458
|
+
column sprintf("- (%8s) %10s %10s (%s:%-2d)", event, klass, id, file, line)
|
459
|
+
newline
|
460
|
+
end # case event
|
461
|
+
|
462
|
+
# End default display style
|
463
|
+
|
464
|
+
end # case @@display_style
|
290
465
|
|
291
|
-
else
|
292
|
-
column sprintf("- (%8s) %10s %10s (%s:%-2d)", event, klass, id, file, line)
|
293
|
-
newline
|
294
|
-
end
|
295
466
|
|
296
467
|
|
297
468
|
rescue Exception => exception
|
@@ -317,6 +488,9 @@ class Unroller
|
|
317
488
|
trace_off if block_given?
|
318
489
|
end # rescue/ensure block
|
319
490
|
end # def trace(&block)
|
491
|
+
class << self
|
492
|
+
alias_method :trace_on, :trace
|
493
|
+
end
|
320
494
|
|
321
495
|
def self.trace_off
|
322
496
|
if @@instance and @@instance.tracing
|
@@ -328,6 +502,26 @@ class Unroller
|
|
328
502
|
set_trace_func(nil)
|
329
503
|
end
|
330
504
|
|
505
|
+
#----------------------------------------------------------
|
506
|
+
def self.watch_for_added_methods(mod, filter = //, &block)
|
507
|
+
mod.singleton_class.instance_eval do
|
508
|
+
define_method :method_added_with_watch_for_added_methods do |name, *args|
|
509
|
+
if name.to_s =~ filter
|
510
|
+
puts "Method '#{name}' was defined at #{caller[0]}"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
alias_method_chain :method_added, :watch_for_added_methods, :create_target => true
|
514
|
+
end
|
515
|
+
|
516
|
+
yield if block_given?
|
517
|
+
|
518
|
+
# mod.class.instance_eval do
|
519
|
+
# alias_method :method_added, :method_added_without_watch_for_added_methods
|
520
|
+
# end
|
521
|
+
end
|
522
|
+
|
523
|
+
|
524
|
+
|
331
525
|
protected
|
332
526
|
#----------------------------------------------------------
|
333
527
|
# Helpers
|
@@ -344,7 +538,11 @@ protected
|
|
344
538
|
end
|
345
539
|
end
|
346
540
|
def skip_line?
|
347
|
-
@excluding_calls_made_within_unintersting_call or
|
541
|
+
@excluding_calls_made_within_unintersting_call or
|
542
|
+
!calling_method_in_an_interesting_class?(@klass.to_s) or
|
543
|
+
!calling_interesting_method?(@id.to_s) or
|
544
|
+
too_deep? or
|
545
|
+
!calling_interesting_line?
|
348
546
|
end
|
349
547
|
def too_deep?
|
350
548
|
# The + 1 is because if they're still at the initial depth (@depth - @initial_depth == 0), we want it treated as "depth 1" (1-based, for humans).
|
@@ -353,31 +551,41 @@ protected
|
|
353
551
|
def too_far?
|
354
552
|
@max_lines and (@lines_output > @max_lines)
|
355
553
|
end
|
356
|
-
def
|
357
|
-
( @exclude_classes + [
|
358
|
-
|
359
|
-
|
360
|
-
# Expect item to be in format [/class_name/, :recursive, any other flags...]
|
361
|
-
regexp = item.shift
|
362
|
-
recursive = item.include?(:recursive)
|
363
|
-
else
|
364
|
-
regexp = item
|
365
|
-
recursive = false
|
366
|
-
end
|
367
|
-
|
368
|
-
returning(class_name =~ regexp) do |uninteresting|
|
369
|
-
if uninteresting && recursive && @excluding_calls_made_within_unintersting_call.nil?
|
554
|
+
def calling_method_in_an_interesting_class?(class_name)
|
555
|
+
!( @exclude_classes + [ClassExclusion.new(self.class.name)] ).any? do |class_exclusion|
|
556
|
+
returning(class_name =~ class_exclusion.regexp) do |is_uninteresting|
|
557
|
+
if is_uninteresting && class_exclusion.recursive? && @excluding_calls_made_within_unintersting_call.nil?
|
370
558
|
puts "Turning tracing off until we get back to internal_depth #{@internal_depth} again because we're calling uninteresting #{class_name}:#{@id}"
|
371
559
|
@excluding_calls_made_within_unintersting_call = @internal_depth
|
372
560
|
end
|
373
561
|
end
|
374
562
|
end
|
375
563
|
end
|
564
|
+
def calling_interesting_method?(name)
|
565
|
+
!( @exclude_methods ).any? do |regexp|
|
566
|
+
returning(name =~ regexp) do |is_uninteresting|
|
567
|
+
if is_uninteresting && (recursive = implemented = false) && @excluding_calls_made_within_unintersting_call.nil?
|
568
|
+
puts "Turning tracing off until we get back to internal_depth #{@internal_depth} again because we're calling uninteresting #{@klass}:#{@id}"
|
569
|
+
@excluding_calls_made_within_unintersting_call = @internal_depth
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
def calling_interesting_line?
|
575
|
+
return true if @line_matches.nil? # No filter to apply
|
576
|
+
line = code_for(@file, @line) or return false
|
577
|
+
(line =~ @line_matches)
|
578
|
+
end
|
376
579
|
|
377
580
|
#----------------------------------------------------------
|
378
581
|
# Formatting stuff.
|
379
|
-
|
380
|
-
|
582
|
+
|
583
|
+
def indent(indent_adjustment = 0)
|
584
|
+
@indent_step * (@depth + indent_adjustment)
|
585
|
+
end
|
586
|
+
# The same thing, only just using whitespace.
|
587
|
+
def plain_indent(indent_adjustment = 0)
|
588
|
+
(' '*@indent_step.length_without_color) * (@depth + indent_adjustment)
|
381
589
|
end
|
382
590
|
|
383
591
|
def remaining_width
|
@@ -390,6 +598,10 @@ protected
|
|
390
598
|
raise ArgumentError if ![:allow, :chop_left, :chop_right].include?(column_overflow)
|
391
599
|
raise ArgumentError if width and !(width == :remainder or width.is_a?(Fixnum))
|
392
600
|
|
601
|
+
if width == :remainder
|
602
|
+
width = remaining_width()
|
603
|
+
end
|
604
|
+
|
393
605
|
if @column_counter == 0
|
394
606
|
@output_line << indent
|
395
607
|
width -= indent.length_without_color if width
|
@@ -397,10 +609,6 @@ protected
|
|
397
609
|
@output_line << @column_separator # So the columns won't be squashed up against each other
|
398
610
|
end
|
399
611
|
|
400
|
-
if width == :remainder
|
401
|
-
width = remaining_width()
|
402
|
-
end
|
403
|
-
|
404
612
|
if width
|
405
613
|
if column_overflow =~ /chop_/
|
406
614
|
#puts "width = #{width}"
|
@@ -435,26 +643,94 @@ protected
|
|
435
643
|
|
436
644
|
#----------------------------------------------------------
|
437
645
|
|
438
|
-
def
|
439
|
-
if file ==
|
440
|
-
# Can't really read the source from the 'eval' file, unfortunately!
|
441
|
-
return ' ' + prefix + ' ' + file.send_if_not_nil(color) + ''
|
442
|
-
end
|
443
|
-
|
444
|
-
line -= 1 # Adjust for the fact that line arg is 0-based, readlines line is 1-based
|
646
|
+
def code_for_file(file)
|
647
|
+
return nil if file == '(eval)' # Can't really read the source from the 'eval' file, unfortunately!
|
445
648
|
begin
|
446
|
-
|
447
|
-
line
|
448
|
-
|
649
|
+
lines = File.readlines(file)
|
650
|
+
lines.unshift '' # Adjust for the fact that readlines line is 0-based, but the line numbers we'll be dealing with are 1-based.
|
651
|
+
@files[file] ||= lines
|
652
|
+
@files[file]
|
449
653
|
rescue Errno::ENOENT
|
450
|
-
$stderr.puts( message = "Error Could not open #{file}" )
|
654
|
+
$stderr.puts( message = "Error: Could not open #{file}" ) if @show_file_load_errors
|
451
655
|
message
|
452
656
|
rescue Exception => exception
|
453
|
-
puts "Error
|
657
|
+
puts "Error in code_for_file(#{file}):"
|
454
658
|
puts exception.inspect
|
455
659
|
end
|
456
660
|
end
|
457
|
-
|
661
|
+
|
662
|
+
def code_for(file, line_num)
|
663
|
+
code_for_file = code_for_file(file)
|
664
|
+
return nil if code_for_file.nil?
|
665
|
+
|
666
|
+
begin
|
667
|
+
line_num = [code_for_file.size - 1, line_num].min # :todo: We should probably just return an error if it's out of range, rather than doing this min junk...
|
668
|
+
line = code_for_file[line_num].strip
|
669
|
+
rescue Exception => exception
|
670
|
+
puts "Error while getting code for #{file}:#{line_num}:"
|
671
|
+
puts exception.inspect
|
672
|
+
end
|
673
|
+
end
|
674
|
+
def pretty_code_for(file, line_num, prefix, color = nil, suffix = '')
|
675
|
+
if file == '(eval)'
|
676
|
+
return ' ' + prefix + ' ' + file.send_if_not_nil(color) + ''
|
677
|
+
end
|
678
|
+
|
679
|
+
' ' + prefix + ' ' +
|
680
|
+
code_for(file, line_num).to_s.send_if_not_nil(color) +
|
681
|
+
suffix
|
682
|
+
end
|
683
|
+
|
684
|
+
#----------------------------------------------------------
|
685
|
+
|
686
|
+
def code_for_entire_method(file, line_num, klass, method, line_to_highlight = nil, indent_adjustment = 0)
|
687
|
+
code_for_file = code_for_file(file)
|
688
|
+
return nil if code_for_file.nil?
|
689
|
+
|
690
|
+
output = ''
|
691
|
+
begin
|
692
|
+
|
693
|
+
line_num = [code_for_file.size - 1, line_num].min # :todo: We should probably just return an error if it's out of range, rather than doing this min junk...
|
694
|
+
first_line = code_for_file[line_num]
|
695
|
+
|
696
|
+
raise "Expected #{file}:#{line_num} to match /def.*#{method}/, but it was:\n#{first_line}" unless first_line =~ /(\s+)def/
|
697
|
+
first_line =~ /(\s+)def/ # Normal case (I *think*)
|
698
|
+
leading_whitespace = $1
|
699
|
+
|
700
|
+
# Look for ending 'end'
|
701
|
+
lines_containing_method =
|
702
|
+
(line_num .. line_num+30).
|
703
|
+
map {|i| [i, code_for_file[i]]}.
|
704
|
+
select_until(inclusive = true) do |line_num, line|
|
705
|
+
line =~ /^#{leading_whitespace}end/
|
706
|
+
end
|
707
|
+
|
708
|
+
common_indentation = lines_containing_method.map { |i, line|
|
709
|
+
line =~ /^(( )*)/; $1 ? $1.size : 0
|
710
|
+
}.min
|
711
|
+
lines_containing_method.map! do |line_num, line|
|
712
|
+
[line_num, line.sub(/^#{' ' * common_indentation}/, '')]
|
713
|
+
end
|
714
|
+
|
715
|
+
if line_to_highlight
|
716
|
+
lines_containing_method.map! do |line_num, line|
|
717
|
+
line_to_highlight == line_num ?
|
718
|
+
[line_num, plain_indent(indent_adjustment) + ' ' + ' -> '.green + line.bold] :
|
719
|
+
[line_num, plain_indent(indent_adjustment) + ' ' + ' ' + line]
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
output = lines_containing_method.
|
724
|
+
map {|line_num, line| line}.join
|
725
|
+
|
726
|
+
output
|
727
|
+
end
|
728
|
+
end # code_for_entire_method
|
729
|
+
|
730
|
+
def header_before_code_for_entire_method(file, line_num)
|
731
|
+
puts '' + "#{fully_qualified_method} (#{file}:#{line_num}):".magenta if show_filename_and_line_numbers
|
732
|
+
end
|
733
|
+
end # class Unroller
|
458
734
|
|
459
735
|
|
460
736
|
class String
|
@@ -481,11 +757,35 @@ class String
|
|
481
757
|
end
|
482
758
|
|
483
759
|
end
|
760
|
+
end # class String
|
761
|
+
|
762
|
+
|
763
|
+
# Make it really, really easy and concise, for those who like it that way.
|
764
|
+
module Kernel
|
765
|
+
def tron(*args)
|
766
|
+
Unroller::trace_on(*args)
|
767
|
+
end
|
768
|
+
def troff(*args)
|
769
|
+
Unroller::trace_off(*args)
|
770
|
+
end
|
484
771
|
end
|
485
772
|
|
486
773
|
|
487
774
|
|
488
775
|
|
776
|
+
|
777
|
+
|
778
|
+
|
779
|
+
|
780
|
+
|
781
|
+
|
782
|
+
|
783
|
+
|
784
|
+
|
785
|
+
|
786
|
+
|
787
|
+
|
788
|
+
|
489
789
|
# _____ _
|
490
790
|
# |_ _|__ ___| |_
|
491
791
|
# | |/ _ \/ __| __|
|
@@ -497,6 +797,12 @@ end
|
|
497
797
|
if $0 == __FILE__
|
498
798
|
require 'test/unit'
|
499
799
|
|
800
|
+
def herald(message)
|
801
|
+
puts message.ljust(130).bold.white.on_magenta
|
802
|
+
end
|
803
|
+
|
804
|
+
Unroller::display_style = nil #:show_entire_method_body
|
805
|
+
|
500
806
|
class TheTest < Test::Unit::TestCase
|
501
807
|
def test_1
|
502
808
|
end
|
@@ -505,20 +811,22 @@ if $0 == __FILE__
|
|
505
811
|
|
506
812
|
|
507
813
|
|
508
|
-
|
509
|
-
|
814
|
+
herald '-----------------------------------------------------------'
|
815
|
+
herald 'Can call trace_off even if not tracing'
|
816
|
+
herald '(Should see no output)'
|
510
817
|
Unroller::trace_off
|
511
818
|
|
512
|
-
|
513
|
-
|
819
|
+
herald '-----------------------------------------------------------'
|
820
|
+
herald 'Can call exclude even if not tracing'
|
821
|
+
herald '(Should see no output)'
|
514
822
|
Unroller::exclude
|
515
823
|
|
516
|
-
|
517
|
-
|
824
|
+
herald '-----------------------------------------------------------'
|
825
|
+
herald 'Testing return value (should print 3 in this case)'
|
518
826
|
puts Unroller::trace { 1 + 2 }
|
519
827
|
|
520
|
-
|
521
|
-
|
828
|
+
herald '-----------------------------------------------------------'
|
829
|
+
herald 'Simple test'
|
522
830
|
def jump!(how_high = 3)
|
523
831
|
how_high.times do
|
524
832
|
'jump!'
|
@@ -528,15 +836,15 @@ if $0 == __FILE__
|
|
528
836
|
jump!(2)
|
529
837
|
Unroller::trace_off
|
530
838
|
|
531
|
-
|
532
|
-
|
839
|
+
herald '-----------------------------------------------------------'
|
840
|
+
herald "Testing that this doesn't trace anything (condition == false proc)"
|
533
841
|
$trace = false
|
534
842
|
Unroller::trace(:condition => proc { $trace }) do
|
535
843
|
jump!
|
536
844
|
end
|
537
845
|
|
538
|
-
|
539
|
-
|
846
|
+
herald '-----------------------------------------------------------'
|
847
|
+
herald "Testing that this doesn't trace the inner method (method2), but does trace method1 and method3 (exclude)"
|
540
848
|
def method1; end
|
541
849
|
def method2
|
542
850
|
'stuff!'
|
@@ -550,8 +858,8 @@ if $0 == __FILE__
|
|
550
858
|
method3
|
551
859
|
end
|
552
860
|
|
553
|
-
|
554
|
-
|
861
|
+
herald '-----------------------------------------------------------'
|
862
|
+
herald "Testing that we can try to turn tracing on even if it's already on"
|
555
863
|
def method1
|
556
864
|
Unroller::trace
|
557
865
|
v = 'in method1'
|
@@ -565,8 +873,8 @@ if $0 == __FILE__
|
|
565
873
|
end
|
566
874
|
|
567
875
|
|
568
|
-
|
569
|
-
|
876
|
+
herald '-----------------------------------------------------------'
|
877
|
+
herald 'Test with block; very deep (test for over-wide columns)'
|
570
878
|
('a'..last='y').each do |method_name|
|
571
879
|
next_method_name = method_name.next unless method_name == last
|
572
880
|
eval <<-End, binding, __FILE__, __LINE__ + 1
|
@@ -579,8 +887,8 @@ if $0 == __FILE__
|
|
579
887
|
a
|
580
888
|
end
|
581
889
|
|
582
|
-
|
583
|
-
|
890
|
+
herald '-----------------------------------------------------------'
|
891
|
+
herald 'Test watching a call stack unwind (only)'
|
584
892
|
('a'..last='y').each do |method_name|
|
585
893
|
next_method_name = method_name.next unless method_name == last
|
586
894
|
eval <<-End, binding, __FILE__, __LINE__ + 1
|
@@ -594,8 +902,8 @@ if $0 == __FILE__
|
|
594
902
|
Unroller::trace_off
|
595
903
|
|
596
904
|
|
597
|
-
|
598
|
-
|
905
|
+
herald '-----------------------------------------------------------'
|
906
|
+
herald 'Testing :depth => :use_call_stack_depth'
|
599
907
|
def go_to_depth_and_call_1(depth, &block)
|
600
908
|
#puts caller(0).size
|
601
909
|
if caller(0).size == depth
|
@@ -630,16 +938,16 @@ if $0 == __FILE__
|
|
630
938
|
end
|
631
939
|
end
|
632
940
|
|
633
|
-
|
634
|
-
|
941
|
+
herald '-----------------------------------------------------------'
|
942
|
+
herald 'Testing without :depth => :use_call_stack_depth (for comparison)'
|
635
943
|
go_to_depth_and_call_1(14) do
|
636
944
|
Unroller::trace() do
|
637
945
|
a
|
638
946
|
end
|
639
947
|
end
|
640
948
|
|
641
|
-
|
642
|
-
|
949
|
+
herald '-----------------------------------------------------------'
|
950
|
+
herald "Test max_depth 5: We shouldn't see the calls to f, g, ... because their depth > 5"
|
643
951
|
('a'..last='y').each do |method_name|
|
644
952
|
next_method_name = method_name.next unless method_name == last
|
645
953
|
eval <<-End, binding, __FILE__, __LINE__ + 1
|
@@ -652,8 +960,8 @@ if $0 == __FILE__
|
|
652
960
|
a
|
653
961
|
end
|
654
962
|
|
655
|
-
|
656
|
-
|
963
|
+
herald '-----------------------------------------------------------'
|
964
|
+
herald 'Test with long filename (make sure it chops it correctly)'
|
657
965
|
File.open(filename = '_code_unroller_test_with_really_really_really_really_really_really_really_really_really_long_filename.rb', 'w') do |file|
|
658
966
|
file.puts "
|
659
967
|
def sit!
|
@@ -668,8 +976,8 @@ if $0 == __FILE__
|
|
668
976
|
require 'fileutils'
|
669
977
|
FileUtils.rm filename
|
670
978
|
|
671
|
-
|
672
|
-
|
979
|
+
herald '-----------------------------------------------------------'
|
980
|
+
herald 'Test @max_lines'
|
673
981
|
('a'..last='h').each do |method_name|
|
674
982
|
next_method_name = method_name.next unless method_name == last
|
675
983
|
eval <<-End, binding, __FILE__, __LINE__ + 1
|
@@ -682,18 +990,74 @@ if $0 == __FILE__
|
|
682
990
|
a
|
683
991
|
end
|
684
992
|
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
993
|
+
herald '-----------------------------------------------------------'
|
994
|
+
herald 'Test :line_matches'
|
995
|
+
herald 'Should only see lines matching "$a_global"'
|
996
|
+
require 'facets/core/string/to_re'
|
997
|
+
Unroller::trace :line_matches => '$a_global'.to_re do
|
998
|
+
# This won't print anything, because for evals that are missing the __FILE__, __LINE__ arguments, we can't even read the source code (unfortunately)
|
999
|
+
eval %(
|
1000
|
+
whatever = 'whatever'
|
1001
|
+
$a_global = '1st time'
|
1002
|
+
foo = 'foo'
|
1003
|
+
$a_global = '2nd time'
|
1004
|
+
blah = 'blah'
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
# This should print 2 lines:
|
1008
|
+
whatever = 'whatever'
|
1009
|
+
$a_global = '1st time'
|
1010
|
+
foo = 'foo'
|
1011
|
+
$a_global = '2nd time'
|
1012
|
+
blah = 'blah'
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
herald '-----------------------------------------------------------'
|
1016
|
+
herald 'Test @exclude_methods'
|
1017
|
+
herald 'Should only see calls to green, strong'
|
1018
|
+
class Wasabi
|
1019
|
+
def green
|
1020
|
+
'...'
|
1021
|
+
end
|
1022
|
+
def too_green
|
1023
|
+
'...'
|
1024
|
+
end
|
1025
|
+
def strong
|
1026
|
+
'...'
|
1027
|
+
end
|
1028
|
+
def too_strong
|
1029
|
+
'...'
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
wasabi = Wasabi.new
|
1033
|
+
Unroller::trace(:exclude_methods => /too_/) do
|
1034
|
+
wasabi.green
|
1035
|
+
wasabi.too_green # Censored, sucka!
|
1036
|
+
wasabi.strong
|
1037
|
+
wasabi.too_strong # Censored, sucka!
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
herald '-----------------------------------------------------------'
|
1041
|
+
herald 'Test @exclude_methods with symbol'
|
1042
|
+
herald "Should should see too_strong, because that doesn't exactly match the exclusion regexp"
|
1043
|
+
Unroller::trace(:exclude_methods => :too_) do
|
1044
|
+
wasabi.too_strong
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
herald '-----------------------------------------------------------'
|
1048
|
+
herald 'Test @exclude_classes'
|
1049
|
+
herald 'Should only see calls to Interesting::...'
|
1050
|
+
class Interesting
|
689
1051
|
def self.method
|
690
1052
|
'...'
|
691
1053
|
end
|
692
1054
|
def method
|
693
1055
|
'...'
|
694
1056
|
end
|
1057
|
+
|
695
1058
|
end
|
696
|
-
module Uninteresting
|
1059
|
+
module Uninteresting
|
1060
|
+
|
697
1061
|
class ClassThatCluttersUpOnesTraces
|
698
1062
|
('a'..last='h').each do |method_name|
|
699
1063
|
next_method_name = method_name.next unless method_name == last
|
@@ -710,7 +1074,7 @@ if $0 == __FILE__
|
|
710
1074
|
def create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
711
1075
|
Uninteresting::ClassThatCluttersUpOnesTraces.new.a
|
712
1076
|
end
|
713
|
-
Unroller::trace(:exclude_classes =>
|
1077
|
+
Unroller::trace(:exclude_classes => Uninteresting::ClassThatCluttersUpOnesTraces) do
|
714
1078
|
create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
715
1079
|
end
|
716
1080
|
|
@@ -731,7 +1095,8 @@ if $0 == __FILE__
|
|
731
1095
|
| | | | | | | | | | \ end (returning from Interesting::method) | unroller.rb:625
|
732
1096
|
| \ end (returning from )
|
733
1097
|
|
734
|
-
# ... which is probably a technically more accurate picture of the current call stack. However, it
|
1098
|
+
# ... which is probably a technically more accurate picture of the current call stack. However, it looked kind of unnatural
|
1099
|
+
# with all those "extra" indents in there. It was also a waste of horizontal screen space.
|
735
1100
|
|
736
1101
|
# This changed when the idea of an @internal_depth separate from the @depth (that the user sees) was introduced.
|
737
1102
|
|
@@ -752,16 +1117,16 @@ if $0 == __FILE__
|
|
752
1117
|
Much cleaner looking...
|
753
1118
|
=end
|
754
1119
|
|
755
|
-
|
756
|
-
|
757
|
-
Unroller::trace(:exclude_classes => [
|
1120
|
+
herald '-----------------------------------------------------------'
|
1121
|
+
herald 'Now let\'s be recursive! We should *not* see any calls to Interesting::* this time. But after we return from it, we should see the call to jump!'
|
1122
|
+
Unroller::trace(:exclude_classes => [Unroller::ClassExclusion.new('Uninteresting', :recursive)]) do
|
758
1123
|
create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
|
759
1124
|
jump!
|
760
1125
|
end
|
761
1126
|
|
762
1127
|
|
763
|
-
|
764
|
-
|
1128
|
+
herald '-----------------------------------------------------------'
|
1129
|
+
herald 'Test class definition'
|
765
1130
|
Unroller::trace do
|
766
1131
|
class NewClass
|
767
1132
|
def hi
|
@@ -771,8 +1136,8 @@ if $0 == __FILE__
|
|
771
1136
|
end
|
772
1137
|
|
773
1138
|
|
774
|
-
|
775
|
-
|
1139
|
+
herald '-----------------------------------------------------------'
|
1140
|
+
herald 'Test rescuing exception'
|
776
1141
|
def raise_an_error
|
777
1142
|
raise 'an error'
|
778
1143
|
end
|
@@ -780,8 +1145,8 @@ if $0 == __FILE__
|
|
780
1145
|
raise_an_error
|
781
1146
|
end rescue nil
|
782
1147
|
|
783
|
-
|
784
|
-
|
1148
|
+
herald '-----------------------------------------------------------'
|
1149
|
+
herald 'Demonstrate how :if condition is useful for local variables too (especially loops and iterators)'
|
785
1150
|
(1..6).each do |i|
|
786
1151
|
Unroller::trace :if => proc {
|
787
1152
|
if (3..4).include?(i)
|
@@ -793,8 +1158,8 @@ if $0 == __FILE__
|
|
793
1158
|
end
|
794
1159
|
end
|
795
1160
|
|
796
|
-
|
797
|
-
|
1161
|
+
herald '-----------------------------------------------------------'
|
1162
|
+
herald 'Testing the :rails "preset"'
|
798
1163
|
module ActiveSupport
|
799
1164
|
def self.whatever
|
800
1165
|
'whatever'
|
@@ -805,14 +1170,33 @@ if $0 == __FILE__
|
|
805
1170
|
'whatever'
|
806
1171
|
end
|
807
1172
|
end
|
808
|
-
Unroller::trace :
|
1173
|
+
Unroller::trace :preset => :rails do
|
809
1174
|
ActiveSupport.whatever
|
810
1175
|
ActiveMongoose.whatever
|
811
1176
|
ActiveSupport.whatever
|
812
1177
|
end
|
813
1178
|
|
814
|
-
|
815
|
-
|
1179
|
+
herald '-----------------------------------------------------------'
|
1180
|
+
herald 'Testing the :dependencies "preset"'
|
1181
|
+
module ActiveSupport
|
1182
|
+
module Dependencies
|
1183
|
+
def self.whatever
|
1184
|
+
'whatever'
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
module Gem
|
1189
|
+
def self.whatever
|
1190
|
+
'whatever'
|
1191
|
+
end
|
1192
|
+
end
|
1193
|
+
Unroller::trace :preset => :dependencies do
|
1194
|
+
ActiveSupport::Dependencies.whatever
|
1195
|
+
Gem.whatever
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
herald '-----------------------------------------------------------'
|
1199
|
+
herald 'Testing showing local variables'
|
816
1200
|
def sum(a, b, c)
|
817
1201
|
a + b + c
|
818
1202
|
end
|
@@ -825,4 +1209,37 @@ if $0 == __FILE__
|
|
825
1209
|
end
|
826
1210
|
my_very_own_method
|
827
1211
|
|
828
|
-
|
1212
|
+
herald '-----------------------------------------------------------'
|
1213
|
+
herald 'Testing watch_for_added_methods'
|
1214
|
+
class Foo
|
1215
|
+
def existing_method; end
|
1216
|
+
end
|
1217
|
+
Unroller::watch_for_added_methods(Foo, /interesting/) do
|
1218
|
+
class Foo
|
1219
|
+
def foo; end
|
1220
|
+
def an_interesting_method; end
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
herald '-----------------------------------------------------------'
|
1225
|
+
herald 'Testing :display_style => :show_entire_method_body'
|
1226
|
+
def bagel
|
1227
|
+
'...'
|
1228
|
+
end
|
1229
|
+
Unroller::trace :display_style => :show_entire_method_body do
|
1230
|
+
bagel
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
herald '-----------------------------------------------------------'
|
1234
|
+
herald 'Testing :display_style => :show_entire_method_body, :show_filename_and_line_numbers => false'
|
1235
|
+
def bagel
|
1236
|
+
'...'
|
1237
|
+
end
|
1238
|
+
Unroller::trace :display_style => :show_entire_method_body, :show_filename_and_line_numbers => false do
|
1239
|
+
bagel
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
herald 'End of non-automated tests'
|
1243
|
+
herald '-----------------------------------------------------------'
|
1244
|
+
|
1245
|
+
end # if $0 == __FILE__ (Tests)
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: unroller
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.0.17
|
7
|
+
date: 2007-06-05 00:00:00 -07:00
|
8
8
|
summary: A tool for generating human-readable "execution traces"
|
9
9
|
require_paths:
|
10
10
|
- lib
|