unroller 0.0.23 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/Readme +26 -7
  2. data/lib/troff.rb +4 -0
  3. data/lib/tron.rb +2 -0
  4. data/lib/unroller.rb +135 -40
  5. data/test/other_file.rb +6 -0
  6. metadata +11 -17
data/Readme CHANGED
@@ -28,7 +28,6 @@ It's a great tool for exploring 3rd-party source code that you aren't familiar w
28
28
 
29
29
  Just insert these line before the point you want to start tracing:
30
30
 
31
- require 'rubygems'
32
31
  require 'unroller'
33
32
  Unroller::trace
34
33
 
@@ -42,6 +41,11 @@ You can also pass a block to <tt>Unroller::trace</tt> and it will automatically
42
41
  ...
43
42
  end
44
43
 
44
+ If you want really quick and dirty:
45
+ require 'tron'
46
+ ...
47
+ troff
48
+
45
49
  ===Example
46
50
 
47
51
  Say I have an ActiveRecord model and want to know exactly what actually goes on behind the scenes when I call model.save! . All I have to do is wrap the method call in a "trace" block, like this:
@@ -109,9 +113,7 @@ And the somewhat trickier but arguably more elegant way that still uses a block
109
113
  code_that_may_or_may_not_be_traced
110
114
  end
111
115
 
112
- The other reason that the latter way is preferred is that the return value of the code-being-traced is preserved. With the first method, you could end up breaking things if the trace_off happens to be the last value in your method (because then the value of trace_off will be used as the return valuel).
113
-
114
-
116
+ The other reason that the latter way is preferred is that the return value of the code-being-traced is preserved. With the first method, you could end up breaking things if the trace_off happens to be the last value in your method (because then the value of trace_off will be used as the return value).
115
117
 
116
118
  Note: The actual application code (the code in the block passed to Unroller::trace, if using the block version) will _always_ get executed, with either of these methods. It is only the tracing that we are toggling, not the execution of the code within the trace(d) block.
117
119
 
@@ -123,6 +125,10 @@ A couple options are available to help things under control. The two main approa
123
125
 
124
126
  You may find that your trace is cluttered/dominated by calls to a small set of methods and classes that you don't care about. These options help you to exclude the worst offenders in a hurry:
125
127
 
128
+ <tt>:file_match => file_match</tt> ::
129
+ Unroller will only show a trace for code whose filename matches <tt>file_match</tt>. If <tt>file_match</tt> is not already a regular expression it will be escaped and turned into one (<tt>/file_match/</tt>).
130
+ <tt>:dir_match => dir_match</tt> ::
131
+ Unroller will only show a trace for code whose filename matches <tt>dir_match</tt>. If <tt>dir_match</tt> is not already a regular expression it will be escaped and turned into one (<tt>/^dir_match/</tt>). Tip: You can simply pass in __FILE__ and it will automatically turn that into File.expand_path(File.dirname(__FILE__)) for you.
126
132
  <tt>:exclude_classes</tt> ::
127
133
  Unroller won't show a trace for any calls to methods from the given class or classes (regular expressions).
128
134
  Pass [/class_name/, :recursive] to also not show the trace for any calls made *from* those uninteresting methods.
@@ -191,7 +197,9 @@ Here's one way you could try to answer that question...
191
197
 
192
198
  ===Usage in Rails
193
199
 
194
- Unroller can be useful for debugging in development mode as well as in tests. Keep in mind that it requires a terminal onto which to output, so it will work if you've started your server with <tt>./script/server</tt>, but not if the server is detached from the terminal with <tt>./script/server -d</tt> or if it's being executed via FastCGI...
200
+ Unroller can be useful for debugging in development mode as well as in tests.
201
+
202
+ <b>Requires a terminal</p>. Keep in mind that it requires a terminal onto which to output, so it will work if you've started your server with <tt>./script/server</tt>; it will _not_ work if the server is detached from the terminal using <tt>./script/server -d</tt>, for example, or if it's being executed via FastCGI... Don't worry, it will gently remind you of this with a happy <tt>can't get terminal parameters (Inappropriate ioctl for device)</tt> error if you forget about it.
195
203
 
196
204
  Rails has a lot of levels of abstractions, so even a seemingly simple call can generate many, many method calls. You may want to try using some of the filtering options, such as <tt>:exclude_classes</tt>, to reduce the verbosity of the output.
197
205
 
@@ -209,7 +217,7 @@ If you want to see all code that gets executed within a certain action in your c
209
217
 
210
218
  sudo gem install unroller --include-dependencies
211
219
 
212
- The dependencies include: facets, qualitysmith_extensions, colored, and extensions
220
+ The dependencies include: facets, quality_extensions, colored, and extensions
213
221
 
214
222
  ==Status
215
223
 
@@ -268,6 +276,9 @@ You're welcome to submit comments, ideas, and/or patches.
268
276
 
269
277
  * Make a GUI interface that lets you quickly collapse/nodes nodes of the tree.
270
278
  * :include_classes option in addition to :exclude_classes?
279
+
280
+ ===Presets
281
+
271
282
  * 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.
272
283
  * :rails => true
273
284
  * :preset => :Rails : Exclude ActiveSupport, Dependencies, etc.
@@ -275,7 +286,14 @@ You're welcome to submit comments, ideas, and/or patches.
275
286
  * :preset => :'ActiveRecord high level' : excludes the lowel-level database stuff (like the individual adapter (SQLite, MySQL, ...).
276
287
  * :preset => :'ActiveRecord low level'
277
288
 
278
- ==="Watch for" conditions===
289
+ Might be more intuitive to say :exclude => :boring_rails_stuff
290
+ (If it's a symbol passed in instead of a string or regexp, we'll assume it's a preset, which will be turned into the strings/regexp's that it defines)
291
+
292
+ Also :include => :something.
293
+
294
+ Let them store presets in a ~/.unroller file so they're not limited to my ideas, and don't have to type out a long list of exclusions every time they want to reuse a common set of exclusions.
295
+
296
+ ==="Watch for" conditions
279
297
 
280
298
  Only traces / shows you when/if a certain condition is met.
281
299
 
@@ -298,3 +316,4 @@ end
298
316
 
299
317
  Other name ideas: :match_code, :match_event, :event_match, :only_events
300
318
 
319
+
data/lib/troff.rb ADDED
@@ -0,0 +1,4 @@
1
+ # Just use the troff method instead, dummy.
2
+ require 'unroller'
3
+ troff
4
+
data/lib/tron.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'unroller'
2
+ tron
data/lib/unroller.rb CHANGED
@@ -1,29 +1,31 @@
1
1
  require 'rubygems'
2
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/kernel/singleton_class'
7
- require 'facets/core/symbol/to_proc'
8
- require 'facets/core/string/bracket'
9
- require 'facets/core/string/index_all'
10
- require 'facets/core/hash/reverse_merge'
11
- gem 'qualitysmith_extensions'
12
- require 'qualitysmith_extensions/object/send_if_not_nil'
13
- require 'qualitysmith_extensions/kernel/trap_chain'
14
- require 'qualitysmith_extensions/kernel/capture_output'
15
- require 'qualitysmith_extensions/string/with_knowledge_of_color'
16
- require 'qualitysmith_extensions/exception/inspect_with_backtrace'
17
- require 'qualitysmith_extensions/symbol/match'
18
- require 'qualitysmith_extensions/module/alias_method_chain'
19
- require 'qualitysmith_extensions/module/malias_method_chain'
20
- require 'qualitysmith_extensions/module/attribute_accessors'
21
- require 'qualitysmith_extensions/enumerable/select_until'
22
- require 'qualitysmith_extensions/module/bool_attr_accessor'
3
+ require 'facets'
4
+ require 'facets/methodspace'
5
+ require 'facets/kernel/populate'
6
+ require 'facets/kernel/returning'
7
+ #require 'facets/kernel/singleton_class'
8
+ #require 'facets/symbol/to_proc'
9
+ #require 'facets/string/bracket'
10
+ #require 'facets/string/index_all'
11
+ #require 'facets/hash/reverse_merge'
12
+ gem 'quality_extensions'
13
+ require 'quality_extensions/object/send_if_not_nil'
14
+ require 'quality_extensions/kernel/trap_chain'
15
+ require 'quality_extensions/kernel/capture_output'
16
+ require 'quality_extensions/string/with_knowledge_of_color'
17
+ require 'quality_extensions/exception/inspect_with_backtrace'
18
+ require 'quality_extensions/regexp/join'
19
+ require 'quality_extensions/symbol/match'
20
+ require 'quality_extensions/module/alias_method_chain'
21
+ require 'quality_extensions/module/malias_method_chain'
22
+ require 'quality_extensions/module/attribute_accessors'
23
+ require 'quality_extensions/enumerable/select_until'
24
+ require 'quality_extensions/module/bool_attr_accessor'
23
25
  gem 'colored'
24
26
  require 'colored'
25
- gem 'extensions'
26
- require 'extensions/object'
27
+ #gem 'extensions'
28
+ #require 'extensions/object'
27
29
 
28
30
  require 'English'
29
31
  require 'pp'
@@ -156,6 +158,7 @@ class Unroller
156
158
  @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".)
157
159
  @line_matches = nil # The source code for that line matches this regular expression
158
160
  @presets = []
161
+ @file_match = /./
159
162
  @exclude_classes = []
160
163
  @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.
161
164
  @exclude_methods = []
@@ -220,11 +223,38 @@ class Unroller
220
223
  end
221
224
  end
222
225
 
226
+ #-----------------------------------------------------------------------------------------------
223
227
  # Options
224
- options[:max_lines] = options.delete(:head) if options.has_key?(:head)
225
- options[:condition] = options.delete(:if) if options.has_key?(:if)
226
- options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
227
- options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
228
+
229
+ # Aliases
230
+ options[:max_lines] = options.delete(:head) if options.has_key?(:head)
231
+ options[:condition] = options.delete(:if) if options.has_key?(:if)
232
+ options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
233
+ options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
234
+ options[:file_match] = options.delete(:file) if options.has_key?(:file)
235
+ options[:file_match] = options.delete(:path) if options.has_key?(:path)
236
+ options[:file_match] = options.delete(:path_match) if options.has_key?(:path_match)
237
+ options[:dir_match] = options.delete(:dir) if options.has_key?(:dir)
238
+ options[:dir_match] = options.delete(:dir_match) if options.has_key?(:dir_match)
239
+
240
+ if (a = options.delete(:dir_match))
241
+ unless a.is_a?(Regexp)
242
+ if a =~ /.*\.rb/
243
+ # They probably passed in __FILE__ and wanted us to File.expand_path(File.dirname()) it for them (and who can blame them? that's a lot of junk to type!!)
244
+ a = File.expand_path(File.dirname(a))
245
+ end
246
+ a = /^#{Regexp.escape(a)}/ # Must start with that entire directory path
247
+ end
248
+ options[:file_match] = a
249
+ end
250
+ if (a = options.delete(:file_match))
251
+ # Coerce it into a Regexp
252
+ unless a.is_a?(Regexp)
253
+ a = /#{Regexp.escape(a)}/
254
+ end
255
+ options[:file_match] = a
256
+ end
257
+
228
258
  if options.has_key?(:exclude_classes)
229
259
  # Coerce it into an array of ClassExclusions
230
260
  a = options.delete(:exclude_classes)
@@ -243,8 +273,9 @@ class Unroller
243
273
  @exclude_methods.concat a
244
274
  end
245
275
  options[:line_matches] = options.delete(:line_matches) if options.has_key?(:line_matches)
246
- set_with(options)
276
+ populate(options)
247
277
 
278
+ #-----------------------------------------------------------------------------------------------
248
279
  # Private
249
280
  @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.
250
281
  # This is useful for two reasons:
@@ -689,6 +720,9 @@ protected
689
720
  end
690
721
  end
691
722
  def calling_interesting_line?
723
+ path = File.expand_path(@file) # rescue @file
724
+ #puts "Checking #{path} !~ #{@file_match}"
725
+ return false if path !~ @file_match
692
726
  return true if @line_matches.nil? # No filter to apply
693
727
  line = code_for(@file, @line) or return false
694
728
  (line =~ @line_matches)
@@ -702,7 +736,7 @@ protected
702
736
  end
703
737
  # The same thing, only just using whitespace.
704
738
  def plain_indent(indent_adjustment = 0)
705
- (' '*@indent_step.length_without_color) * (@depth + indent_adjustment)
739
+ (' '*@indent_step.length_without_color) * [(@depth + indent_adjustment), 0].max
706
740
  end
707
741
 
708
742
  def remaining_width
@@ -799,7 +833,7 @@ protected
799
833
 
800
834
  ' ' + prefix + ' ' +
801
835
  code_for(file, line_num).to_s.send_if_not_nil(color) +
802
- suffix
836
+ suffix
803
837
  end
804
838
 
805
839
  #----------------------------------------------------------
@@ -887,10 +921,10 @@ end # class Unroller
887
921
 
888
922
 
889
923
  class String
890
- namespace :code_unroller do
924
+ method_space :code_unroller do
891
925
 
892
926
  def make_it_fit(max_width, overflow = :chop_right)
893
- with(string = self) do
927
+ returning(string = self) do
894
928
  if string.length_without_color > max_width # Wider than desired column width; Needs to be chopped.
895
929
  unless max_width < 4 # Is there even enough room for it if it *is* chopped? If not, then don't even bother.
896
930
  #Kernel.p overflow
@@ -1149,7 +1183,7 @@ if $0 == __FILE__
1149
1183
  herald '-----------------------------------------------------------'
1150
1184
  herald 'Test :line_matches'
1151
1185
  herald 'Should only see lines matching "$a_global"'
1152
- require 'facets/core/string/to_re'
1186
+ require 'facets/string/to_re'
1153
1187
  Unroller::trace :line_matches => '$a_global'.to_re do
1154
1188
  # This won't print anything, because for evals that are missing the __FILE__, __LINE__ arguments, we can't even read the source code (unfortunately)
1155
1189
  eval %(
@@ -1168,6 +1202,29 @@ if $0 == __FILE__
1168
1202
  blah = 'blah'
1169
1203
  end
1170
1204
 
1205
+ herald '-----------------------------------------------------------'
1206
+ herald 'Test :file_match'
1207
+ herald 'Should only see calls to in_this_file'
1208
+ require_local '../test/other_file'
1209
+ def in_this_file
1210
+ a = 'a'
1211
+ b = 'b'
1212
+ c = 'c'
1213
+ etc = 'etc.'
1214
+ end
1215
+ Unroller::trace(:file_match => __FILE__) do
1216
+ in_this_file()
1217
+ in_other_file()
1218
+ end
1219
+
1220
+ herald '-----------------------------------------------------------'
1221
+ herald 'Test :dir_match'
1222
+ herald 'Should only see calls to in_this_file'
1223
+ Unroller::trace(:dir_match => __FILE__) do
1224
+ in_this_file()
1225
+ in_other_file()
1226
+ end
1227
+
1171
1228
  herald '-----------------------------------------------------------'
1172
1229
  herald 'Test @exclude_methods'
1173
1230
  herald 'Should only see calls to green, strong'
@@ -1404,17 +1461,55 @@ if $0 == __FILE__
1404
1461
  big_ol_gaggle
1405
1462
  end
1406
1463
 
1464
+ # herald '-----------------------------------------------------------'
1465
+ # herald 'Testing :interactive => true / interactive debugger'
1466
+ # def factorial(x)
1467
+ # if x == 1
1468
+ # x
1469
+ # else
1470
+ # x * factorial(x - 1)
1471
+ # end
1472
+ # end
1473
+ # Unroller::debug do
1474
+ # factorial(4)
1475
+ # end
1476
+
1407
1477
  herald '-----------------------------------------------------------'
1408
- herald 'Testing :interactive => true / interactive debugger'
1409
- def factorial(x)
1410
- if x == 1
1411
- x
1412
- else
1413
- x * factorial(x - 1)
1414
- end
1478
+ herald 'Testing :interactive => true / interactive debugger: two calls on the same line'
1479
+ def method_1
1480
+ 'stuff'
1415
1481
  end
1482
+ def method_2
1483
+ 'stuff'
1484
+ end
1485
+ # Step Over will step over the call to method_1 but will immediately step into method_2, without coming back to this line to ask you what you want to do about the call to method_2 ...
1486
+ # In other words, there is no intermediate 'line' event between the 2 'call' events (right?)
1487
+ # This could be confusing to some users... Should we hack it to somehow detect that there are two calls in a row for the same
1488
+ # line and artificially inject a pseudo-line event in between so that we have a chance to show the menu again??
1416
1489
  Unroller::debug do
1417
- factorial(4)
1490
+ sum = method_1 + method_2
1491
+ puts sum
1492
+ end
1493
+
1494
+ herald '-----------------------------------------------------------'
1495
+ herald 'Testing :interactive => true / interactive debugger: with blocks'
1496
+ def block_taker(&block)
1497
+ puts "I'm the block taker. I take blocks."
1498
+ puts "Please Step Over the next line."
1499
+ yield # buggy!
1500
+ # false:: (unroller.rb:1433) (line): ??
1501
+ puts "Done yielding to the block"
1502
+ end
1503
+ Unroller::debug do
1504
+ puts "If you do a Step Over, will it go into the block or not? Nope. Because we skipped tracing the yield."
1505
+ # to do: have a separate "step over method body but into block" option??
1506
+ # that way, it you just want to stay in this local file and avoid seeing the code that *wraps* the call to the block, you could do so...
1507
+ block_taker do
1508
+ puts 'Pick me! Pick me!'
1509
+ puts 'I say! When will I ever be executed?'
1510
+ puts 'Oh dear, will they just skip right over me?'
1511
+ puts 'Or will they Step In here and take a look?'
1512
+ end
1418
1513
  end
1419
1514
 
1420
1515
  herald '-----------------------------------------------------------'
@@ -0,0 +1,6 @@
1
+ def in_other_file
2
+ a = 'a'
3
+ b = 'b'
4
+ c = 'c'
5
+ etc = 'etc.'
6
+ end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.2
2
+ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: unroller
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.23
7
- date: 2007-06-18 00:00:00 -07:00
6
+ version: 0.1.0
7
+ date: 2008-06-20 00:00:00 -07:00
8
8
  summary: "Ruby Code Unroller: A tool for generating human-readable \"execution traces\""
9
9
  require_paths:
10
10
  - lib
@@ -29,10 +29,13 @@ post_install_message:
29
29
  authors:
30
30
  - Tyler Rick
31
31
  files:
32
+ - lib/tron.rb
32
33
  - lib/unroller.rb
34
+ - lib/troff.rb
35
+ - test/other_file.rb
33
36
  - Readme
34
- test_files: []
35
-
37
+ test_files:
38
+ - test/other_file.rb
36
39
  rdoc_options:
37
40
  - --title
38
41
  - unroller
@@ -55,16 +58,16 @@ dependencies:
55
58
  requirements:
56
59
  - - ">="
57
60
  - !ruby/object:Gem::Version
58
- version: 1.8.54
61
+ version: 2.4.1
59
62
  version:
60
63
  - !ruby/object:Gem::Dependency
61
- name: qualitysmith_extensions
64
+ name: quality_extensions
62
65
  version_requirement:
63
66
  version_requirements: !ruby/object:Gem::Version::Requirement
64
67
  requirements:
65
68
  - - ">="
66
69
  - !ruby/object:Gem::Version
67
- version: 0.0.13
70
+ version: 0.1.3
68
71
  version:
69
72
  - !ruby/object:Gem::Dependency
70
73
  name: colored
@@ -84,12 +87,3 @@ dependencies:
84
87
  - !ruby/object:Gem::Version
85
88
  version: 0.0.0
86
89
  version:
87
- - !ruby/object:Gem::Dependency
88
- name: extensions
89
- version_requirement:
90
- version_requirements: !ruby/object:Gem::Version::Requirement
91
- requirements:
92
- - - ">"
93
- - !ruby/object:Gem::Version
94
- version: 0.0.0
95
- version: