unroller 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/Readme +15 -2
  2. data/lib/unroller.rb +710 -2
  3. metadata +10 -2
  4. data/lib/unroller/unroller.rb +0 -589
data/Readme CHANGED
@@ -55,6 +55,13 @@ Screenshot[link:include/screenshot1.png]
55
55
 
56
56
  This is much more efficient and reliable than manually tracing through the execution yourself! (Trying to guess which file the <tt>save!</tt> method would be defined in, searching for the file in the depths of <tt>/usr/lib/ruby/gems</tt>, scrolling down to the right line number, and repeating a zillion times...)
57
57
 
58
+ ===Inspecting variables
59
+
60
+ If you'd like to see the values of all arguments/parameters that were passed into the method for each method call, just pass in the :show_args => true option.
61
+
62
+ If you'd like to see the values of all local variables as they exist right before executing the current line, just pass in the :show_locals => true option.
63
+
64
+ Note: You might like to know that the state of those variables is _after_ executing that line, too, but currently that's not possible.
58
65
 
59
66
  ===Reducing verbosity
60
67
 
@@ -132,7 +139,9 @@ If you want to see all code that gets executed within a certain action in your c
132
139
 
133
140
  ==Installation
134
141
 
135
- sudo gem install unroller
142
+ sudo gem install unroller --include-dependencies
143
+
144
+ The dependencies include: facets, qualitysmith_extensions, colored, and extensions
136
145
 
137
146
  ==Status
138
147
 
@@ -142,6 +151,11 @@ Occasionally it has caused some segmentation faults and other weirdness for me w
142
151
 
143
152
  (Don't even *think* about leaving this in your production code!)
144
153
 
154
+ The code isn't really clean and there aren't (m)any automated tests yet because I've kind of thrown this together in a big hurry, but I hope to solve both of those problems eventually.
155
+ You might consider this a "prototype" right now -- it works, but it wouldn't hurt at some point to throw it away and re-implement it a bit better.
156
+
157
+ Also keep in mind that the API as subject to change as I try to think of better design ideas and as I get feedback from people telling me what they want to see.
158
+
145
159
  ==About the name
146
160
 
147
161
  I called it "Ruby Unroller" because it visually "unrolls" a stack trace for you (like a scroll?). (And it sounds cooler than "Ruby Script Execution Tracer".)
@@ -180,7 +194,6 @@ It's also sort of like a call stack (caller(0)). But unlike the callstack you us
180
194
  You're welcome to submit comments and/or patches.
181
195
 
182
196
  * Make a GUI interface that lets you quickly collapse/nodes nodes of the tree.
183
- * It would be nice if we could see what arguments are being passed to each method. This would be technically difficult, but I wonder if it would be possible to just wait for a 'call' event and when you see one, wrap/alias_method_chain the given classname/id with a wrapper method that has variable *args and does whatever you want to do with the args before actually doing the *real* method call....
184
197
  * :include_classes option in addition to :exclude_classes?
185
198
  * 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.
186
199
  * :preset => :Rails : Exclude ActiveSupport, etc.
@@ -1,3 +1,711 @@
1
1
  require 'rubygems'
2
- require 'facets/core/kernel/require_local'
3
- require_local 'unroller/unroller'
2
+ gem 'facets'
3
+ require 'facets/core/module/namespace'
4
+ require 'facets/core/kernel/with'
5
+ require 'facets/core/kernel/set_with'
6
+ require 'facets/core/string/bracket'
7
+ gem 'qualitysmith_extensions'
8
+ require 'qualitysmith_extensions/object/send_if_not_nil'
9
+ require 'qualitysmith_extensions/kernel/trap_chain'
10
+ require 'qualitysmith_extensions/kernel/capture_output'
11
+ require 'qualitysmith_extensions/string/with_knowledge_of_color'
12
+ require 'qualitysmith_extensions/exception/inspect_with_backtrace'
13
+ require 'qualitysmith_extensions/symbol/match'
14
+ gem 'colored'
15
+ require 'colored'
16
+ gem 'extensions'
17
+ require 'extensions/symbol' # to_proc
18
+
19
+ # To disable color, uncomment this:
20
+ #class String
21
+ # def colorize(string, options = {})
22
+ # string
23
+ # end
24
+ #end
25
+
26
+
27
+
28
+
29
+ #
30
+ class Unroller
31
+ class Variables
32
+ def initialize(which, binding)
33
+ @variables = eval("#{which}_variables", binding).map { |variable|
34
+ value = eval(variable, binding)
35
+ [variable, value]
36
+ }
37
+ end
38
+ def to_s
39
+ #@variables.inspect
40
+ @variables.map do |variable|
41
+ name, value = *variable
42
+ "#{name} = #{value.inspect}"
43
+ end.join('; ').bracket(' (', ')')
44
+ end
45
+ def any?
46
+ !@variables.empty?
47
+ end
48
+ end
49
+ @@instance = nil
50
+
51
+ def self.trace(options = {}, &block)
52
+ @@instance = Unroller.new(options)
53
+ @@instance.trace &block
54
+ end
55
+
56
+ attr_accessor :depth
57
+ attr_reader :tracing
58
+
59
+ def initialize(options = {})
60
+ # Defaults
61
+ @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.
62
+ @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.
63
+ @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...
64
+ @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".)
65
+ @exclude_classes = []
66
+ @show_args = true
67
+ @show_locals = false
68
+ @strip_comments = true # :todo:
69
+ @use_full_path = false # :todo:
70
+ @screen_width = 150
71
+ @column_widths = [70]
72
+ @indent_step = ' ' + '|'.magenta + ' '
73
+ @column_separator = ' ' + '|'.yellow.bold + ' '
74
+ instance_variables.each do |v|
75
+ self.class.class_eval do
76
+ attr_accessor v.gsub!(/^@/, '')
77
+ end
78
+ end
79
+
80
+ # "Presets"
81
+ # Experimental -- subject to change a lot before it's finalized
82
+ [:rails].each do |preset|
83
+ if options.has_key?(preset)
84
+ options.delete(preset)
85
+ case preset
86
+ when :rails
87
+ options[:exclude_classes] ||= []
88
+ options[:exclude_classes].concat [
89
+ /Benchmark/,
90
+ /Gem/,
91
+ /Dependencies/,
92
+ /Logger/,
93
+ /MonitorMixin/,
94
+ /Set/,
95
+ /HashWithIndifferentAccess/,
96
+ /ERB/,
97
+ /ActiveRecord/,
98
+ /SQLite3/,
99
+ /Class/,
100
+ /ActiveSupport/,
101
+ /ActiveSupport::Deprecation/,
102
+ /Pathname/
103
+ ]
104
+ end
105
+
106
+ end
107
+ end
108
+
109
+ # Options
110
+ options[:condition] = options.delete(:if) if options.has_key?(:if)
111
+ options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
112
+ options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
113
+ if options.has_key?(:exclude_classes)
114
+ options[:exclude_classes] = [options[:exclude_classes]] if options[:exclude_classes].is_a?(Regexp)
115
+ raise ArgumentError if !options[:exclude_classes].is_a?(Array)
116
+ end
117
+ set_with(options)
118
+
119
+ # Private
120
+ @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.
121
+ # This is useful for two reasons:
122
+ # 1. Sometimes (and I don't know why), the code that gets shown for a 'return' event doesn't even look like it has anything to do with a return...
123
+ # 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
124
+ # (the filename may give some clue, but not enough), so this is likely to be a welcome reminder a lot of the time.
125
+ @depth = @initial_depth
126
+ @output_line = ''
127
+ @column_counter = 0
128
+ @tracing = false
129
+ @files = {}
130
+ @lines_output = 0
131
+ @excluding_calls_made_within_unintersting_call = nil
132
+ end
133
+
134
+ def self.exclude(*args, &block)
135
+ @@instance.exclude(*args, &block)
136
+ end
137
+ def exclude(&block)
138
+ old_tracing = @tracing
139
+ (trace_off; puts 'Suspending tracing')
140
+ yield
141
+ (trace; puts 'Resuming tracing') if old_tracing
142
+ end
143
+
144
+ def trace(&block)
145
+ (puts 'Already tracing!'; return) if @tracing # This does not work. :fixme:
146
+ @tracing = true
147
+
148
+
149
+ if @condition.call
150
+
151
+ trap_chain("INT") { set_trace_func(nil) }
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+
160
+
161
+ # (This is the meat of the library right here, so let's set it off with at least 5 blank lines.)
162
+ set_trace_func( proc do |event, file, line, id, binding, klass|
163
+ begin # begin/rescue block
164
+ @event, @file, @line, @id, @binding, @klass =
165
+ event, file, line, id, binding, klass
166
+
167
+ # Sometimes klass is false and id is nil. Not sure why, but that's the way it is.
168
+ #printf "- (event=%8s) (klass=%10s) (id=%10s) (%s:%-2d)\n", event, klass, id, file, line #if klass.to_s == 'false'
169
+ #puts 'false!!!!!!!'+klass.inspect if klass.to_s == 'false'
170
+
171
+ return if ['c-call', 'c-return'].include? event
172
+ #(puts 'exclude') if @excluding_calls_made_within_unintersting_call unless event == 'return' # Until we hit a return and can break out of this uninteresting call, we don't want to do *anything*.
173
+ #return if uninteresting_class?(klass.to_s) unless (klass == false)
174
+
175
+ if too_far?
176
+ puts "We've read #{@max_lines} (@max_lines) lines now. Turning off tracing..."
177
+ trace_off
178
+ return
179
+ end
180
+
181
+ case event
182
+
183
+
184
+
185
+ when 'call'
186
+ unless skip_line?
187
+ # :todo: use # instead of :: if klass.constantize.instance_methods.include?(id)
188
+ column sprintf(' ' + '+'.cyan + ' calling'.cyan + ' ' + '%s'.underline.cyan, fully_qualified_method), @column_widths[0]
189
+ newline
190
+
191
+ column code_for(file, line, '/'.magenta, :green), @column_widths[0]
192
+ file_column file, line
193
+ newline
194
+
195
+ # The locals at this point will be simply be the arguments that were passed in to this method.
196
+ do_show_locals if show_args
197
+
198
+ @lines_output += 1
199
+
200
+ @call_stack.push fully_qualified_method
201
+ end
202
+
203
+ @depth += 1
204
+
205
+
206
+ when 'class'
207
+ when 'end'
208
+ when 'line'
209
+ unless skip_line?
210
+ # 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...)
211
+ do_show_locals if show_locals
212
+
213
+ column code_for(file, line, ' ', :bold), @column_widths[0]
214
+ file_column file, line
215
+ newline
216
+
217
+ @lines_output += 1
218
+ end
219
+
220
+
221
+
222
+ when 'return'
223
+ puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth < 0
224
+ @depth -= 1 unless @depth == 0
225
+ returning_from = @call_stack.pop
226
+
227
+ unless skip_line?
228
+ code = code_for(file, line, '\\'.magenta, :green, suffix = " (returning from #{returning_from})".green)
229
+ code = code_for(file, line, '\\'.magenta + " (returning from #{returning_from})".green, :green) unless code =~ /return|end/
230
+ # I've seen some really weird statements listed as "return" statements.
231
+ # I'm not really sure *why* it thinks these are returns, but let's at least identify those lines for the user. Examples:
232
+ # * must_be_open!
233
+ # * @db = db
234
+ # * stmt = @statement_factory.new( self, sql )
235
+ # 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.
236
+ # But I don't know if that explains all such cases or not...
237
+ column code, @column_widths[0]
238
+ file_column file, line
239
+ newline
240
+
241
+ @lines_output += 1
242
+ end
243
+
244
+ # Did we just get out of an uninteresting call?? Are we back in interesting land again??
245
+ if @excluding_calls_made_within_unintersting_call and @excluding_calls_made_within_unintersting_call == @depth
246
+ if @excluding_calls_made_within_unintersting_call == @depth
247
+ puts "Yay, we're back in interesting land!"
248
+ @excluding_calls_made_within_unintersting_call = nil
249
+ end
250
+ end
251
+
252
+
253
+
254
+ when 'raise'
255
+ # We probably always want to see these (?)... Never skip displaying them, even if we are "too deep".
256
+ column "Raising an error (#{$!}) from #{klass}".red.bold, @column_widths[0]
257
+ newline
258
+
259
+ column code_for(file, line, ' ').red, @column_widths[0]
260
+ file_column file, line
261
+ newline
262
+
263
+ else
264
+ column sprintf("- (%8s) %10s %10s (%s:%-2d)", event, klass, id, file, line)
265
+ newline
266
+ end
267
+
268
+
269
+ rescue Exception => exception
270
+ puts exception.inspect
271
+ raise
272
+ end # begin/rescue block
273
+ end) # set_trace_func
274
+
275
+
276
+
277
+
278
+
279
+
280
+
281
+
282
+ end # if @condition.call
283
+
284
+ if block_given?
285
+ yield
286
+ end
287
+
288
+ ensure
289
+ trace_off if block_given?
290
+ end
291
+
292
+ def self.trace_off
293
+ @@instance.trace_off
294
+ end
295
+ def trace_off
296
+ @tracing = false
297
+ set_trace_func(nil)
298
+ end
299
+
300
+ protected
301
+ #----------------------------------------------------------
302
+ # Helpers
303
+
304
+ def fully_qualified_method
305
+ "#{@klass}::#{@id}"
306
+ end
307
+ def do_show_locals
308
+ variables = Variables.new(:local, @binding)
309
+ if variables.any?
310
+ column variables.to_s
311
+ newline
312
+ end
313
+ end
314
+ def skip_line?
315
+ @excluding_calls_made_within_unintersting_call or calling_method_in_an_uninteresting_class?(@klass.to_s) or too_deep?
316
+ end
317
+ def too_deep?
318
+ # 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).
319
+ @max_depth and (@depth - @initial_depth + 1 > @max_depth)
320
+ end
321
+ def too_far?
322
+ @max_lines and (@lines_output > @max_lines)
323
+ end
324
+ def calling_method_in_an_uninteresting_class?(class_name)
325
+ ( @exclude_classes + [/#{self.class.name}/] ).any? do |item|
326
+ item = item.dup
327
+ if item.is_a?(Array)
328
+ # Expect item to be in format [/class_name/, :recursive, any other flags...]
329
+ regexp = item.shift
330
+ recursive = item.include?(:recursive)
331
+ else
332
+ regexp = item
333
+ recursive = false
334
+ end
335
+
336
+ returning(class_name =~ regexp) do |uninteresting|
337
+ if uninteresting && recursive && @excluding_calls_made_within_unintersting_call.nil?
338
+ puts "Turning tracing off until we get back to depth #{@depth} again because we're calling uninteresting #{class_name}:#{@id}"
339
+ @excluding_calls_made_within_unintersting_call = @depth
340
+ end
341
+ end
342
+ end
343
+ end
344
+
345
+ #----------------------------------------------------------
346
+ # Formatting stuff.
347
+ def indent
348
+ @indent_step*@depth
349
+ end
350
+
351
+ def remaining_width
352
+ @screen_width - @output_line.length_without_color
353
+ end
354
+
355
+ # +width+ is the minimum width for this column. It's also a maximum if +column_overflow+ is :chop_left or :chop_right
356
+ # +color+ is only needed if you plan on doing some chopping, because if you apply the color *before* the chopping, the color code might get chopped off.
357
+ def column(string, width = nil, column_overflow = :allow, color = nil)
358
+ raise ArgumentError if ![:allow, :chop_left, :chop_right].include?(column_overflow)
359
+ raise ArgumentError if width and !(width == :remainder or width.is_a?(Fixnum))
360
+
361
+ if @column_counter == 0
362
+ @output_line << indent
363
+ width -= indent.length_without_color if width
364
+ else
365
+ @output_line << @column_separator # So the columns won't be squashed up against each other
366
+ end
367
+
368
+ if width == :remainder
369
+ width = remaining_width()
370
+ end
371
+
372
+ if width
373
+ if column_overflow =~ /chop_/
374
+ #puts "width = #{width}"
375
+ string = string.code_unroller.make_it_fit(width, column_overflow)
376
+ end
377
+ string = string.code_unroller.make_it_fit(remaining_width) # Handles maximum width
378
+
379
+ string = string.ljust_without_color(width) # Handles minimum width
380
+ end
381
+
382
+ @output_line << string.send_if_not_nil(color)
383
+
384
+ @column_counter += 1
385
+ end # def column
386
+
387
+ def newline
388
+ unless @output_line.strip.length_without_color == 0 or @output_line == @last_line_printed
389
+ Kernel.print @output_line
390
+ Kernel.puts
391
+ @last_line_printed = @output_line
392
+ end
393
+
394
+ @output_line = ''
395
+ @column_counter = 0
396
+ end
397
+
398
+ def file_column(file, line)
399
+ column "#{file}:#{line}", :remainder, :chop_left, :magenta
400
+ end
401
+
402
+ #----------------------------------------------------------
403
+
404
+ def code_for(file, line, prefix, color = nil, suffix = '')
405
+ if file == '(eval)'
406
+ # Can't really read the source from the 'eval' file, unfortunately!
407
+ return ' ' + prefix + ' ' + file.send_if_not_nil(color) + ''
408
+ end
409
+
410
+ line -= 1 # Adjust for the fact that line arg is 0-based, readlines line is 1-based
411
+ begin
412
+ @files[file] ||= File.readlines(file)
413
+ line = [@files[file].size - 1, line].min
414
+ ' ' + prefix + ' ' + @files[file][line].strip.send_if_not_nil(color) + suffix
415
+ rescue Errno::ENOENT
416
+ $stderr.puts( message = "Error Could not open #{file}" )
417
+ message
418
+ rescue Exception => exception
419
+ puts "Error while getting code for #{file}:#{line}:"
420
+ puts exception.inspect
421
+ end
422
+ end
423
+ end
424
+
425
+
426
+ class String
427
+ namespace :code_unroller do
428
+
429
+ def make_it_fit(max_width, overflow = :chop_right)
430
+ with(string = self) do
431
+ if string.length_without_color > max_width # Wider than desired column width; Needs to be chopped.
432
+ unless max_width < 4 # Is there even enough room for it if it *is* chopped?
433
+ if overflow == :chop_left
434
+ #puts "making string (#{string.length_without_color}) fit within #{max_width}"
435
+ #puts "chopping '#{string}' at -(#{max_width} - 3) .. -1!"
436
+ chopped_part = string[-(max_width - 3) .. -1]
437
+ string.replace '...' + chopped_part
438
+ elsif overflow == :chop_right
439
+ chopped_part = string[0 .. (max_width - 3)]
440
+ string.replace chopped_part + '...'
441
+ end
442
+ else
443
+ string = ''
444
+ end
445
+ end
446
+ end
447
+ end
448
+
449
+ end
450
+ end
451
+
452
+
453
+
454
+
455
+ if $0 == __FILE__
456
+ puts '-----------------------------------------------------------'
457
+ puts 'Simple test'
458
+ def jump!(how_high = 3)
459
+ how_high.times do
460
+ 'jump!'
461
+ end
462
+ end
463
+ Unroller::trace
464
+ jump!(2)
465
+ Unroller::trace_off
466
+
467
+ puts '-----------------------------------------------------------'
468
+ puts "Testing that this doesn't trace anything (condition == false proc)"
469
+ $trace = false
470
+ Unroller::trace(:condition => proc { $trace }) do
471
+ jump!
472
+ end
473
+
474
+ puts '-----------------------------------------------------------'
475
+ puts "Testing that this doesn't trace the inner method (method2), but does trace method1 and method3 (exclude)"
476
+ def method1; end
477
+ def method2
478
+ 'stuff!'
479
+ end
480
+ def method3; end
481
+ Unroller::trace do
482
+ method1
483
+ Unroller::exclude do
484
+ method2
485
+ end
486
+ method3
487
+ end
488
+
489
+ puts '-----------------------------------------------------------'
490
+ puts 'Test with block; very deep (test for over-wide columns)'
491
+ ('a'..last='y').each do |method_name|
492
+ next_method_name = method_name.next unless method_name == last
493
+ eval <<-End, binding, __FILE__, __LINE__ + 1
494
+ def #{method_name}
495
+ #{next_method_name}
496
+ end
497
+ End
498
+ end
499
+ Unroller::trace(:depth => 5) do
500
+ a
501
+ end
502
+
503
+ puts '-----------------------------------------------------------'
504
+ puts 'Test watching a call stack unwind (only)'
505
+ ('a'..last='y').each do |method_name|
506
+ next_method_name = method_name.next unless method_name == last
507
+ eval <<-End, binding, __FILE__, __LINE__ + 1
508
+ def #{method_name}
509
+ #{next_method_name}
510
+ #{'Unroller::trace(:depth => caller(0).size)' if method_name == last }
511
+ end
512
+ End
513
+ end
514
+ a
515
+ Unroller::trace_off
516
+
517
+
518
+ puts '-----------------------------------------------------------'
519
+ puts 'Testing :depth => :use_call_stack_depth'
520
+ def go_to_depth_and_call_1(depth, &block)
521
+ #puts caller(0).size
522
+ if caller(0).size == depth
523
+ puts 'calling a'
524
+ block.call
525
+ else
526
+ go_to_depth_and_call_2(depth, &block)
527
+ end
528
+ #puts caller(0).size
529
+ end
530
+ def go_to_depth_and_call_2(depth, &block)
531
+ #puts caller(0).size
532
+ if caller(0).size == depth
533
+ puts 'calling a'
534
+ block.call
535
+ else
536
+ go_to_depth_and_call_1(depth, &block)
537
+ end
538
+ #puts caller(0).size
539
+ end
540
+ ('a'..last='c').each do |method_name|
541
+ next_method_name = method_name.next unless method_name == last
542
+ eval <<-End, binding, __FILE__, __LINE__ + 1
543
+ def #{method_name}
544
+ #{next_method_name}
545
+ end
546
+ End
547
+ end
548
+ go_to_depth_and_call_1(14) do
549
+ Unroller::trace(:depth => :use_call_stack_depth) do
550
+ a
551
+ end
552
+ end
553
+
554
+ puts '-----------------------------------------------------------'
555
+ puts 'Testing without :depth => :use_call_stack_depth (for comparison)'
556
+ go_to_depth_and_call_1(14) do
557
+ Unroller::trace() do
558
+ a
559
+ end
560
+ end
561
+
562
+ puts '-----------------------------------------------------------'
563
+ puts "Test max_depth 5: We shouldn't see the calls to f, g, ... because their depth > 5"
564
+ ('a'..last='y').each do |method_name|
565
+ next_method_name = method_name.next unless method_name == last
566
+ eval <<-End, binding, __FILE__, __LINE__ + 1
567
+ def #{method_name}
568
+ #{next_method_name}
569
+ end
570
+ End
571
+ end
572
+ Unroller::trace(:max_depth => 5) do
573
+ a
574
+ end
575
+
576
+ puts '-----------------------------------------------------------'
577
+ puts 'Test with long filename (make sure it chops it correctly)'
578
+ File.open(filename = '_code_unroller_test_with_really_really_really_really_really_really_really_really_really_long_filename.rb', 'w') do |file|
579
+ file.puts "
580
+ def sit!
581
+ jump!
582
+ end
583
+ "
584
+ end
585
+ load filename
586
+ Unroller::trace(:depth => 5) do
587
+ sit!
588
+ end
589
+ require 'fileutils'
590
+ FileUtils.rm filename
591
+
592
+ puts '-----------------------------------------------------------'
593
+ puts 'Test @max_lines'
594
+ ('a'..last='h').each do |method_name|
595
+ next_method_name = method_name.next unless method_name == last
596
+ eval <<-End, binding, __FILE__, __LINE__ + 1
597
+ def #{method_name}
598
+ #{next_method_name}
599
+ end
600
+ End
601
+ end
602
+ Unroller::trace(:max_lines => 20) do
603
+ a
604
+ end
605
+
606
+ puts '-----------------------------------------------------------'
607
+ puts 'Test @exclude_classes'
608
+ puts 'Should only see calls to Interesting::...'
609
+ class Interesting # :nodoc: all
610
+ def self.method
611
+ '...'
612
+ end
613
+ def method
614
+ '...'
615
+ end
616
+ end
617
+ module Uninteresting # :nodoc: all
618
+ class ClassThatCluttersUpOnesTraces
619
+ ('a'..last='h').each do |method_name|
620
+ next_method_name = method_name.next unless method_name == last
621
+ eval <<-End, binding, __FILE__, __LINE__ + 1
622
+ def #{method_name}
623
+ #{next_method_name}
624
+ #{'Interesting::method' if method_name == last }
625
+ #{'Interesting.new.method' if method_name == last }
626
+ end
627
+ End
628
+ end
629
+ end
630
+ end
631
+ def create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
632
+ Uninteresting::ClassThatCluttersUpOnesTraces.new.a
633
+ end
634
+ Unroller::trace(:exclude_classes => /Uninteresting::ClassThatCluttersUpOnesTraces/) do
635
+ create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
636
+ end
637
+
638
+ puts '-----------------------------------------------------------'
639
+ puts '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!'
640
+ Unroller::trace(:exclude_classes => [[/Uninteresting/, :recursive]]) do
641
+ create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
642
+ jump!
643
+ end
644
+
645
+
646
+ puts '-----------------------------------------------------------'
647
+ puts 'Test class definition'
648
+ Unroller::trace do
649
+ class NewClass
650
+ def hi
651
+ 'hi'
652
+ end
653
+ end
654
+ end
655
+
656
+
657
+ puts '-----------------------------------------------------------'
658
+ puts 'Test rescuing exception'
659
+ def raise_an_error
660
+ raise 'an error'
661
+ end
662
+ Unroller::trace do
663
+ raise_an_error
664
+ end rescue nil
665
+
666
+ puts '-----------------------------------------------------------'
667
+ puts 'Demonstrate how :if condition is useful for local variables too (especially loops and iterators)'
668
+ (1..6).each do |i|
669
+ Unroller::trace :if => proc {
670
+ if (3..4).include?(i)
671
+ puts "Yep, it's a 3 or a 4. I guess we'll enable the tracer for this iteration then...:"
672
+ true
673
+ end
674
+ } do
675
+ puts "i is now equal to #{i}"
676
+ end
677
+ end
678
+
679
+ puts '-----------------------------------------------------------'
680
+ puts 'Testing the :rails "preset"'
681
+ module ActiveSupport
682
+ def self.whatever
683
+ 'whatever'
684
+ end
685
+ end
686
+ module ActiveMongoose
687
+ def self.whatever
688
+ 'whatever'
689
+ end
690
+ end
691
+ Unroller::trace :rails => 1 do
692
+ ActiveSupport.whatever
693
+ ActiveMongoose.whatever
694
+ ActiveSupport.whatever
695
+ end
696
+
697
+ puts '-----------------------------------------------------------'
698
+ puts 'Testing showing local variables'
699
+ def sum(a, b, c)
700
+ a + b + c
701
+ end
702
+ def my_very_own_method
703
+ Unroller::trace :show_args => false, :show_locals => true do
704
+ sum = sum(1, 2, 3)
705
+ sum
706
+ sum = sum(3, 3, 3)
707
+ end
708
+ end
709
+ my_very_own_method
710
+
711
+ end
metadata CHANGED
@@ -3,7 +3,7 @@ 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.6
6
+ version: 0.0.8
7
7
  date: 2007-04-20 00:00:00 -07:00
8
8
  summary: A tool for generating human-readable "execution traces"
9
9
  require_paths:
@@ -30,7 +30,6 @@ authors:
30
30
  - Tyler Rick
31
31
  files:
32
32
  - lib/unroller.rb
33
- - lib/unroller/unroller.rb
34
33
  - Readme
35
34
  test_files: []
36
35
 
@@ -76,3 +75,12 @@ dependencies:
76
75
  - !ruby/object:Gem::Version
77
76
  version: 0.0.0
78
77
  version:
78
+ - !ruby/object:Gem::Dependency
79
+ name: extensions
80
+ version_requirement:
81
+ version_requirements: !ruby/object:Gem::Version::Requirement
82
+ requirements:
83
+ - - ">"
84
+ - !ruby/object:Gem::Version
85
+ version: 0.0.0
86
+ version:
@@ -1,589 +0,0 @@
1
- require 'rubygems'
2
- gem 'facets'
3
- require 'facets/core/module/namespace'
4
- require 'facets/core/kernel/with'
5
- require 'facets/core/kernel/set_with'
6
- gem 'qualitysmith_extensions'
7
- require 'qualitysmith_extensions/object/send_if_not_nil'
8
- require 'qualitysmith_extensions/kernel/trap_chain'
9
- require 'qualitysmith_extensions/kernel/capture_output'
10
- require 'qualitysmith_extensions/string/with_knowledge_of_color'
11
- require 'qualitysmith_extensions/exception/inspect_with_backtrace'
12
- require 'qualitysmith_extensions/symbol/match'
13
- gem 'colored'
14
- require 'colored'
15
-
16
- # To disable color, uncomment this:
17
- #class String
18
- # def colorize(string, options = {})
19
- # string
20
- # end
21
- #end
22
-
23
- #
24
- class Unroller
25
- @@instance = nil
26
-
27
- def self.trace(options = {}, &block)
28
- @@instance = Unroller.new(options)
29
- @@instance.trace &block
30
- end
31
-
32
- attr_accessor :depth
33
- attr_reader :tracing
34
-
35
- def initialize(options = {})
36
- # Defaults
37
- @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.
38
- @initial_depth = 0 # ("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.
39
- @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...
40
- @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".)
41
- @exclude_classes = []
42
- @strip_comments = true # :todo:
43
- @use_full_path = false # :todo:
44
- @screen_width = 150
45
- @column_widths = [70]
46
- @indent_step = ' ' + '|'.magenta + ' '
47
- @column_separator = ' ' + '|'.yellow.bold + ' '
48
- instance_variables.each do |v|
49
- self.class.class_eval do
50
- attr_accessor v.gsub!(/^@/, '')
51
- end
52
- end
53
-
54
- # Options
55
- options[:condition] = options.delete(:if) if options.has_key?(:if)
56
- options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
57
- options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
58
- if options.has_key?(:exclude_classes)
59
- options[:exclude_classes] = [options[:exclude_classes]] if options[:exclude_classes].is_a?(Regexp)
60
- raise ArgumentError if !options[:exclude_classes].is_a?(Array)
61
- end
62
- set_with(options)
63
-
64
- # Private
65
- @depth = @initial_depth
66
- @output_line = ''
67
- @column_counter = 0
68
- @tracing = false
69
- @files = {}
70
- @lines_output = 0
71
- @excluding_calls_made_within_unintersting_call = nil
72
- end
73
-
74
- def self.exclude(*args, &block)
75
- @@instance.exclude(*args, &block)
76
- end
77
- def exclude(&block)
78
- old_tracing = @tracing
79
- (trace_off; puts 'Suspending tracing')
80
- yield
81
- (trace; puts 'Resuming tracing') if old_tracing
82
- end
83
-
84
- def trace(&block)
85
- (puts 'Already tracing!'; return) if @tracing # This does not work. :fixme:
86
- @tracing = true
87
-
88
-
89
- if @condition.call
90
-
91
- trap_chain("INT") { set_trace_func(nil) }
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
- # (This is the meat of the library right here, so let's set it off with at least 5 blank lines.)
102
- set_trace_func( proc do |event, file, line, id, binding, klass|
103
- begin # begin/rescue block
104
- @event, @file, @line, @id, @binding, @klass =
105
- event, file, line, id, binding, klass
106
-
107
- # Sometimes klass is false and id is nil. Not sure why, but that's the way it is.
108
- #printf "- (event=%8s) (klass=%10s) (id=%10s) (%s:%-2d)\n", event, klass, id, file, line #if klass.to_s == 'false'
109
- #puts 'false!!!!!!!'+klass.inspect if klass.to_s == 'false'
110
-
111
- return if ['c-call', 'c-return'].include? event
112
- #(puts 'exclude') if @excluding_calls_made_within_unintersting_call unless event == 'return' # Until we hit a return and can break out of this uninteresting call, we don't want to do *anything*.
113
- #return if uninteresting_class?(klass.to_s) unless (klass == false)
114
-
115
- if too_far?
116
- puts "We've read #{@max_lines} (@max_lines) lines now. Turning off tracing..."
117
- trace_off
118
- return
119
- end
120
-
121
- case event
122
-
123
-
124
-
125
- when 'call'
126
- unless skip_line?
127
- # :todo: use # instead of :: if klass.constantize.instance_methods.include?(id)
128
- column sprintf(' ' + '+'.cyan + ' calling'.cyan + ' ' + '%s::%s'.underline.cyan, klass, id), @column_widths[0]
129
- newline
130
-
131
- column code_for(file, line, '/'.magenta, :green), @column_widths[0]
132
- file_column file, line
133
- newline
134
-
135
- @lines_output += 1
136
- end
137
-
138
- @depth += 1
139
-
140
-
141
- when 'class'
142
- when 'end'
143
- when 'line'
144
- unless skip_line?
145
- column code_for(file, line, ' ', :bold), @column_widths[0]
146
- file_column file, line
147
- newline
148
-
149
- @lines_output += 1
150
- end
151
-
152
-
153
-
154
- when 'return'
155
- puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth < 0
156
- @depth -= 1 unless @depth == 0
157
-
158
-
159
- unless skip_line?
160
- code = code_for(file, line, '\\'.magenta, :green)
161
- code = code_for(file, line, '\\'.magenta + ' (returning)'.green, :green) unless code =~ /return|end/
162
- # I've seen some really weird statements listed as "return" statements.
163
- # I'm not really sure *why* it thinks these are returns, but let's at least identify those lines for the user. Examples:
164
- # * must_be_open!
165
- # * @db = db
166
- # * stmt = @statement_factory.new( self, sql )
167
- # 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.
168
- # But I don't know if that explains all such cases or not...
169
- column code, @column_widths[0]
170
- file_column file, line
171
- newline
172
-
173
- @lines_output += 1
174
- end
175
-
176
- # Did we just get out of an uninteresting call?? Are we back in interesting land again??
177
- if @excluding_calls_made_within_unintersting_call and @excluding_calls_made_within_unintersting_call == @depth
178
- if @excluding_calls_made_within_unintersting_call == @depth
179
- puts "Yay, we're back in interesting land!"
180
- @excluding_calls_made_within_unintersting_call = nil
181
- end
182
- end
183
-
184
-
185
-
186
- when 'raise'
187
- # We probably always want to see these (?)... Never skip displaying them, even if we are "too deep".
188
- column "Raising an error (#{$!}) from #{klass}".red.bold, @column_widths[0]
189
- newline
190
-
191
- column code_for(file, line, ' ').red, @column_widths[0]
192
- file_column file, line
193
- newline
194
-
195
- else
196
- column sprintf("- (%8s) %10s %10s (%s:%-2d)", event, klass, id, file, line)
197
- newline
198
- end
199
-
200
-
201
- rescue Exception => exception
202
- puts exception.inspect
203
- raise
204
- end # begin/rescue block
205
- end) # set_trace_func
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
- end # if @condition.call
215
-
216
- if block_given?
217
- yield
218
- end
219
-
220
- ensure
221
- trace_off if block_given?
222
- end
223
-
224
- def self.trace_off
225
- @@instance.trace_off
226
- end
227
- def trace_off
228
- @tracing = false
229
- set_trace_func(nil)
230
- end
231
-
232
- protected
233
- #----------------------------------------------------------
234
- # Helpers
235
-
236
- def skip_line?
237
- @excluding_calls_made_within_unintersting_call or calling_method_in_an_uninteresting_class?(@klass.to_s) or too_deep?
238
- end
239
- def too_deep?
240
- # 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).
241
- @max_depth and (@depth - @initial_depth + 1 > @max_depth)
242
- end
243
- def too_far?
244
- @max_lines and (@lines_output > @max_lines)
245
- end
246
- def calling_method_in_an_uninteresting_class?(class_name)
247
- ( @exclude_classes + [/#{self.class.name}/] ).any? do |item|
248
- item = item.dup
249
- if item.is_a?(Array)
250
- # Expect item to be in format [/class_name/, :recursive, any other flags...]
251
- regexp = item.shift
252
- recursive = item.include?(:recursive)
253
- else
254
- regexp = item
255
- recursive = false
256
- end
257
-
258
- returning(class_name =~ regexp) do |uninteresting|
259
- if uninteresting && recursive && @excluding_calls_made_within_unintersting_call.nil?
260
- puts "Turning tracing off until we get back to depth #{@depth} again because we're calling uninteresting #{class_name}:#{@id}"
261
- @excluding_calls_made_within_unintersting_call = @depth
262
- end
263
- end
264
- end
265
- end
266
-
267
- #----------------------------------------------------------
268
- # Formatting stuff.
269
- def indent
270
- @indent_step*@depth
271
- end
272
-
273
- def remaining_width
274
- @screen_width - @output_line.length_without_color
275
- end
276
-
277
- # +width+ is the minimum width for this column. It's also a maximum if +column_overflow+ is :chop_left or :chop_right
278
- # +color+ is only needed if you plan on doing some chopping, because if you apply the color *before* the chopping, the color code might get chopped off.
279
- def column(string, width = nil, column_overflow = :allow, color = nil)
280
- raise ArgumentError if ![:allow, :chop_left, :chop_right].include?(column_overflow)
281
- raise ArgumentError if width and !(width == :remainder or width.is_a?(Fixnum))
282
-
283
- if @column_counter == 0
284
- @output_line << indent
285
- width -= indent.length_without_color if width
286
- else
287
- @output_line << @column_separator # So the columns won't be squashed up against each other
288
- end
289
-
290
- if width == :remainder
291
- width = remaining_width()
292
- end
293
-
294
- if width
295
- if column_overflow =~ /chop_/
296
- #puts "width = #{width}"
297
- string = string.code_unroller.make_it_fit(width, column_overflow)
298
- end
299
- string = string.code_unroller.make_it_fit(remaining_width) # Handles maximum width
300
-
301
- string = string.ljust_without_color(width) # Handles minimum width
302
- end
303
-
304
- @output_line << string.send_if_not_nil(color)
305
-
306
- @column_counter += 1
307
- end # def column
308
-
309
- def newline
310
- unless @output_line.strip.length_without_color == 0 or @output_line == @last_line_printed
311
- Kernel.print @output_line
312
- Kernel.puts
313
- @last_line_printed = @output_line
314
- end
315
-
316
- @output_line = ''
317
- @column_counter = 0
318
- end
319
-
320
- def file_column(file, line)
321
- column "#{file}:#{line}", :remainder, :chop_left, :magenta
322
- end
323
-
324
- #----------------------------------------------------------
325
-
326
- def code_for(file, line, prefix, color = nil)
327
- if file == '(eval)'
328
- # Can't really read the source from the 'eval' file, unfortunately!
329
- return ' ' + prefix + ' ' + file.send_if_not_nil(color) + ''
330
- end
331
-
332
- line -= 1 # Adjust for the fact that line arg is 0-based, readlines line is 1-based
333
- begin
334
- @files[file] ||= File.readlines(file)
335
- line = [@files[file].size - 1, line].min
336
- ' ' + prefix + ' ' + @files[file][line].strip.send_if_not_nil(color) + ''
337
- rescue Errno::ENOENT
338
- $stderr.puts( message = "Error Could not open #{file}" )
339
- message
340
- rescue Exception => exception
341
- puts "Error while getting code for #{file}:#{line}:"
342
- puts exception.inspect
343
- end
344
- end
345
- end
346
-
347
-
348
- class String
349
- namespace :code_unroller do
350
-
351
- def make_it_fit(max_width, overflow = :chop_right)
352
- with(string = self) do
353
- if string.length_without_color > max_width # Wider than desired column width; Needs to be chopped.
354
- unless max_width < 4 # Is there even enough room for it if it *is* chopped?
355
- if overflow == :chop_left
356
- #puts "making string (#{string.length_without_color}) fit within #{max_width}"
357
- #puts "chopping '#{string}' at -(#{max_width} - 3) .. -1!"
358
- chopped_part = string[-(max_width - 3) .. -1]
359
- string.replace '...' + chopped_part
360
- elsif overflow == :chop_right
361
- chopped_part = string[0 .. (max_width - 3)]
362
- string.replace chopped_part + '...'
363
- end
364
- else
365
- string = ''
366
- end
367
- end
368
- end
369
- end
370
-
371
- end
372
- end
373
-
374
-
375
-
376
-
377
- if $0 == __FILE__
378
- puts '-----------------------------------------------------------'
379
- puts 'Simple test'
380
- def jump!
381
- 3.times do
382
- 'jump!'
383
- end
384
- end
385
- Unroller::trace
386
- jump!
387
- Unroller::trace_off
388
-
389
- puts '-----------------------------------------------------------'
390
- puts "Testing that this doesn't trace anything (condition == false proc)"
391
- $trace = false
392
- Unroller::trace(:condition => proc { $trace }) do
393
- jump!
394
- end
395
-
396
- puts '-----------------------------------------------------------'
397
- puts "Testing that this doesn't trace the inner method (method2), but does trace method1 and method3 (exclude)"
398
- def method1; end
399
- def method2
400
- 'stuff!'
401
- end
402
- def method3; end
403
- Unroller::trace do
404
- method1
405
- Unroller::exclude do
406
- method2
407
- end
408
- method3
409
- end
410
-
411
- puts '-----------------------------------------------------------'
412
- puts 'Test with block; very deep (test for over-wide columns)'
413
- ('a'..last='y').each do |method_name|
414
- next_method_name = method_name.next unless method_name == last
415
- eval <<-End, binding, __FILE__, __LINE__ + 1
416
- def #{method_name}
417
- #{next_method_name}
418
- end
419
- End
420
- end
421
- Unroller::trace(:depth => 5) do
422
- a
423
- end
424
-
425
- puts '-----------------------------------------------------------'
426
- puts 'Test watching a call stack unwind (only)'
427
- ('a'..last='y').each do |method_name|
428
- next_method_name = method_name.next unless method_name == last
429
- eval <<-End, binding, __FILE__, __LINE__ + 1
430
- def #{method_name}
431
- #{next_method_name}
432
- #{'Unroller::trace(:depth => caller(0).size)' if method_name == last }
433
- end
434
- End
435
- end
436
- a
437
- Unroller::trace_off
438
-
439
-
440
- puts '-----------------------------------------------------------'
441
- puts 'Testing :depth => :use_call_stack_depth'
442
- def go_to_depth_and_call_1(depth, &block)
443
- #puts caller(0).size
444
- if caller(0).size == depth
445
- puts 'calling a'
446
- block.call
447
- else
448
- go_to_depth_and_call_2(depth, &block)
449
- end
450
- #puts caller(0).size
451
- end
452
- def go_to_depth_and_call_2(depth, &block)
453
- #puts caller(0).size
454
- if caller(0).size == depth
455
- puts 'calling a'
456
- block.call
457
- else
458
- go_to_depth_and_call_1(depth, &block)
459
- end
460
- #puts caller(0).size
461
- end
462
- ('a'..last='c').each do |method_name|
463
- next_method_name = method_name.next unless method_name == last
464
- eval <<-End, binding, __FILE__, __LINE__ + 1
465
- def #{method_name}
466
- #{next_method_name}
467
- end
468
- End
469
- end
470
- go_to_depth_and_call_1(14) do
471
- Unroller::trace(:depth => :use_call_stack_depth) do
472
- a
473
- end
474
- end
475
-
476
- puts '-----------------------------------------------------------'
477
- puts 'Testing without :depth => :use_call_stack_depth (for comparison)'
478
- go_to_depth_and_call_1(14) do
479
- Unroller::trace() do
480
- a
481
- end
482
- end
483
-
484
- puts '-----------------------------------------------------------'
485
- puts "Test max_depth 5: We shouldn't see the calls to f, g, ... because their depth > 5"
486
- ('a'..last='y').each do |method_name|
487
- next_method_name = method_name.next unless method_name == last
488
- eval <<-End, binding, __FILE__, __LINE__ + 1
489
- def #{method_name}
490
- #{next_method_name}
491
- end
492
- End
493
- end
494
- Unroller::trace(:max_depth => 5) do
495
- a
496
- end
497
-
498
- puts '-----------------------------------------------------------'
499
- puts 'Test with long filename (make sure it chops it correctly)'
500
- File.open(filename = '_code_unroller_test_with_really_really_really_really_really_really_really_really_really_long_filename.rb', 'w') do |file|
501
- file.puts "
502
- def sit!
503
- jump!
504
- end
505
- "
506
- end
507
- load filename
508
- Unroller::trace(:depth => 5) do
509
- sit!
510
- end
511
- require 'fileutils'
512
- FileUtils.rm filename
513
-
514
- puts '-----------------------------------------------------------'
515
- puts 'Test @max_lines'
516
- ('a'..last='h').each do |method_name|
517
- next_method_name = method_name.next unless method_name == last
518
- eval <<-End, binding, __FILE__, __LINE__ + 1
519
- def #{method_name}
520
- #{next_method_name}
521
- end
522
- End
523
- end
524
- Unroller::trace(:max_lines => 20) do
525
- a
526
- end
527
-
528
- puts '-----------------------------------------------------------'
529
- puts 'Test @exclude_classes'
530
- puts 'Should only see calls to Interesting::...'
531
- class Interesting # :nodoc: all
532
- def self.method
533
- '...'
534
- end
535
- def method
536
- '...'
537
- end
538
- end
539
- module Uninteresting # :nodoc: all
540
- class ClassThatCluttersUpOnesTraces
541
- ('a'..last='h').each do |method_name|
542
- next_method_name = method_name.next unless method_name == last
543
- eval <<-End, binding, __FILE__, __LINE__ + 1
544
- def #{method_name}
545
- #{next_method_name}
546
- #{'Interesting::method' if method_name == last }
547
- #{'Interesting.new.method' if method_name == last }
548
- end
549
- End
550
- end
551
- end
552
- end
553
- def create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
554
- Uninteresting::ClassThatCluttersUpOnesTraces.new.a
555
- end
556
- Unroller::trace(:exclude_classes => /Uninteresting::ClassThatCluttersUpOnesTraces/) do
557
- create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
558
- end
559
-
560
- puts '-----------------------------------------------------------'
561
- puts '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!'
562
- Unroller::trace(:exclude_classes => [[/Uninteresting/, :recursive]]) do
563
- create_an_instance_of_UninterestingClassThatCluttersUpOnesTraces
564
- jump!
565
- end
566
-
567
-
568
- puts '-----------------------------------------------------------'
569
- puts 'Test class definition'
570
- Unroller::trace do
571
- class NewClass
572
- def hi
573
- 'hi'
574
- end
575
- end
576
- end
577
-
578
-
579
- puts '-----------------------------------------------------------'
580
- puts 'Test rescuing exception'
581
- def raise_an_error
582
- raise 'an error'
583
- end
584
- Unroller::trace do
585
- raise_an_error
586
- end
587
-
588
-
589
- end