unroller 0.0.12 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/Readme +12 -3
  2. data/lib/unroller.rb +579 -162
  3. 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>:] GPL
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
- * :preset => :Rails : Exclude ActiveSupport, etc.
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 really don't care about C-calls because we can't see the source for them anyway...
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
- [:rails].each do |preset|
94
- if options.has_key?(preset)
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
- options[:exclude_classes] ||= []
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 arry of Regexp's, if possible
126
- options[:exclude_classes] = [options[:exclude_classes]] if options[:exclude_classes].is_a?(Regexp)
127
- raise ArgumentError if !options[:exclude_classes].is_a?(Array)
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 like it has anything to do with a return...
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 event
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
- column code_for(file, line, '/'.magenta, :green), @column_widths[0]
216
- file_column file, line
217
- newline
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
- @lines_output += 1
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
- @call_stack.push fully_qualified_method
291
+ @lines_output += 1
222
292
 
223
- @depth += 1
224
- #puts "++ Increased depth to #{depth}"
293
+ @call_stack.push current_call
225
294
 
226
- # The locals at this point will be simply be the arguments that were passed in to this method.
227
- do_show_locals if show_args
228
- end
229
- @internal_depth += 1
295
+ @depth += 1
296
+ end
297
+ @internal_depth += 1
230
298
 
231
299
 
232
- when 'class'
233
- when 'end'
234
- when 'line'
235
- unless skip_line?
236
- # 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...)
237
- do_show_locals if show_locals
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
- @lines_output += 1
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
- when 'return'
339
+ @lines_output += 1
340
+ end
249
341
 
250
- @internal_depth -= 1
251
- unless skip_line?
252
- puts "Warning: @depth < 0. You may wish to call trace with a :depth => depth value greater than #{@initial_depth}" if @depth-1 < 0
253
- @depth -= 1 unless @depth == 0
254
- #puts "-- Decreased depth to #{depth}"
255
- returning_from = @call_stack.pop
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
- code = code_for(file, line, '\\'.magenta, :green, suffix = " (returning from #{returning_from})".green)
258
- code = code_for(file, line, '\\'.magenta + " (returning from #{returning_from})".green, :green) unless code =~ /return|end/
259
- # I've seen some really weird statements listed as "return" statements.
260
- # I'm not really sure *why* it thinks these are returns, but let's at least identify those lines for the user. Examples:
261
- # * must_be_open!
262
- # * @db = db
263
- # * stmt = @statement_factory.new( self, sql )
264
- # 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.
265
- # But I don't know if that explains all such cases or not...
266
- column code, @column_widths[0]
267
- file_column file, line
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
- @lines_output += 1
271
- end
367
+ # End when :show_entire_method_body
272
368
 
273
- # Did we just get out of an uninteresting call?? Are we back in interesting land again??
274
- if @excluding_calls_made_within_unintersting_call and
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
- when 'raise'
283
- # We probably always want to see these (?)... Never skip displaying them, even if we are "too deep".
284
- column "Raising an error (#{$!}) from #{klass}".red.bold, @column_widths[0]
285
- newline
379
+ column pretty_code_for(file, line, '/'.magenta, :green), @column_widths[0]
380
+ file_column file, line
381
+ newline
286
382
 
287
- column code_for(file, line, ' ').red, @column_widths[0]
288
- file_column file, line
289
- newline
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 calling_method_in_an_uninteresting_class?(@klass.to_s) or too_deep?
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 calling_method_in_an_uninteresting_class?(class_name)
357
- ( @exclude_classes + [/#{self.class.name}/] ).any? do |item|
358
- item = item.dup
359
- if item.is_a?(Array)
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
- def indent
380
- @indent_step*@depth
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 code_for(file, line, prefix, color = nil, suffix = '')
439
- if file == '(eval)'
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
- @files[file] ||= File.readlines(file)
447
- line = [@files[file].size - 1, line].min
448
- ' ' + prefix + ' ' + @files[file][line].strip.send_if_not_nil(color) + suffix
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 while getting code for #{file}:#{line}:"
657
+ puts "Error in code_for_file(#{file}):"
454
658
  puts exception.inspect
455
659
  end
456
660
  end
457
- end
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
- puts '-----------------------------------------------------------'
509
- puts 'Can call trace_off even if not tracing'
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
- puts '-----------------------------------------------------------'
513
- puts 'Can call exclude even if not tracing'
819
+ herald '-----------------------------------------------------------'
820
+ herald 'Can call exclude even if not tracing'
821
+ herald '(Should see no output)'
514
822
  Unroller::exclude
515
823
 
516
- puts '-----------------------------------------------------------'
517
- puts 'Testing return value (should print 3 in this case)'
824
+ herald '-----------------------------------------------------------'
825
+ herald 'Testing return value (should print 3 in this case)'
518
826
  puts Unroller::trace { 1 + 2 }
519
827
 
520
- puts '-----------------------------------------------------------'
521
- puts 'Simple test'
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
- puts '-----------------------------------------------------------'
532
- puts "Testing that this doesn't trace anything (condition == false proc)"
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
- puts '-----------------------------------------------------------'
539
- puts "Testing that this doesn't trace the inner method (method2), but does trace method1 and method3 (exclude)"
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
- puts '-----------------------------------------------------------'
554
- puts "Testing that we can try to turn tracing on even if it's already on"
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
- puts '-----------------------------------------------------------'
569
- puts 'Test with block; very deep (test for over-wide columns)'
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
- puts '-----------------------------------------------------------'
583
- puts 'Test watching a call stack unwind (only)'
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
- puts '-----------------------------------------------------------'
598
- puts 'Testing :depth => :use_call_stack_depth'
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
- puts '-----------------------------------------------------------'
634
- puts 'Testing without :depth => :use_call_stack_depth (for comparison)'
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
- puts '-----------------------------------------------------------'
642
- puts "Test max_depth 5: We shouldn't see the calls to f, g, ... because their depth > 5"
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
- puts '-----------------------------------------------------------'
656
- puts 'Test with long filename (make sure it chops it correctly)'
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
- puts '-----------------------------------------------------------'
672
- puts 'Test @max_lines'
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
- puts '-----------------------------------------------------------'
686
- puts 'Test @exclude_classes'
687
- puts 'Should only see calls to Interesting::...'
688
- class Interesting # :nodoc: all
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 # :nodoc: all
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 => /Uninteresting::ClassThatCluttersUpOnesTraces/) do
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
- puts '-----------------------------------------------------------'
756
- 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!'
757
- Unroller::trace(:exclude_classes => [[/Uninteresting/, :recursive]]) do
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
- puts '-----------------------------------------------------------'
764
- puts 'Test class definition'
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
- puts '-----------------------------------------------------------'
775
- puts 'Test rescuing exception'
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
- puts '-----------------------------------------------------------'
784
- puts 'Demonstrate how :if condition is useful for local variables too (especially loops and iterators)'
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
- puts '-----------------------------------------------------------'
797
- puts 'Testing the :rails "preset"'
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 :rails => 1 do
1173
+ Unroller::trace :preset => :rails do
809
1174
  ActiveSupport.whatever
810
1175
  ActiveMongoose.whatever
811
1176
  ActiveSupport.whatever
812
1177
  end
813
1178
 
814
- puts '-----------------------------------------------------------'
815
- puts 'Testing showing local variables'
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
- end
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.12
7
- date: 2007-04-24 00:00:00 -07:00
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