Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added :file_match, :dir_match

git-svn-id: svn+ssh://rubyforge.org/var/svn/unroller@3 9d3ae835-7db8-4d57-bbcb-260a800a6cf5
  • Loading branch information...
commit 5b16c05eca949f2e598065595f5cdb7b04e7670a 1 parent 18d565f
@TylerRick authored
Showing with 70 additions and 8 deletions.
  1. +10 −4 Readme
  2. +60 −4 lib/unroller.rb
View
14 Readme
@@ -28,7 +28,6 @@ It's a great tool for exploring 3rd-party source code that you aren't familiar w
Just insert these line before the point you want to start tracing:
- require 'rubygems'
require 'unroller'
Unroller::trace
@@ -42,6 +41,11 @@ You can also pass a block to <tt>Unroller::trace</tt> and it will automatically
...
end
+If you want really quick and dirty:
+ require 'tron'
+ ...
+ troff
+
===Example
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
code_that_may_or_may_not_be_traced
end
-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).
-
-
+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).
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.
@@ -123,6 +125,10 @@ A couple options are available to help things under control. The two main approa
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:
+<tt>:file_match => file_match</tt> ::
+ 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>).
+<tt>:dir_match => dir_match</tt> ::
+ 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.
<tt>:exclude_classes</tt> ::
Unroller won't show a trace for any calls to methods from the given class or classes (regular expressions).
Pass [/class_name/, :recursive] to also not show the trace for any calls made *from* those uninteresting methods.
View
64 lib/unroller.rb
@@ -15,6 +15,7 @@
require 'quality_extensions/kernel/capture_output'
require 'quality_extensions/string/with_knowledge_of_color'
require 'quality_extensions/exception/inspect_with_backtrace'
+require 'quality_extensions/regexp/join'
require 'quality_extensions/symbol/match'
require 'quality_extensions/module/alias_method_chain'
require 'quality_extensions/module/malias_method_chain'
@@ -157,6 +158,7 @@ def initialize(options = {})
@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".)
@line_matches = nil # The source code for that line matches this regular expression
@presets = []
+ @file_match = /./
@exclude_classes = []
@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.
@exclude_methods = []
@@ -221,11 +223,38 @@ def initialize(options = {})
end
end
+ #-----------------------------------------------------------------------------------------------
# Options
- options[:max_lines] = options.delete(:head) if options.has_key?(:head)
- options[:condition] = options.delete(:if) if options.has_key?(:if)
- options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
- options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
+
+ # Aliases
+ options[:max_lines] = options.delete(:head) if options.has_key?(:head)
+ options[:condition] = options.delete(:if) if options.has_key?(:if)
+ options[:initial_depth] = options.delete(:depth) if options.has_key?(:depth)
+ options[:initial_depth] = caller(0).size if options[:initial_depth] == :use_call_stack_depth
+ options[:file_match] = options.delete(:file) if options.has_key?(:file)
+ options[:file_match] = options.delete(:path) if options.has_key?(:path)
+ options[:file_match] = options.delete(:path_match) if options.has_key?(:path_match)
+ options[:dir_match] = options.delete(:dir) if options.has_key?(:dir)
+ options[:dir_match] = options.delete(:dir_match) if options.has_key?(:dir_match)
+
+ if (a = options.delete(:dir_match))
+ unless a.is_a?(Regexp)
+ if a =~ /.*\.rb/
+ # 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!!)
+ a = File.expand_path(File.dirname(a))
+ end
+ a = /^#{Regexp.escape(a)}/ # Must start with that entire directory path
+ end
+ options[:file_match] = a
+ end
+ if (a = options.delete(:file_match))
+ # Coerce it into a Regexp
+ unless a.is_a?(Regexp)
+ a = /#{Regexp.escape(a)}/
+ end
+ options[:file_match] = a
+ end
+
if options.has_key?(:exclude_classes)
# Coerce it into an array of ClassExclusions
a = options.delete(:exclude_classes)
@@ -246,6 +275,7 @@ def initialize(options = {})
options[:line_matches] = options.delete(:line_matches) if options.has_key?(:line_matches)
populate(options)
+ #-----------------------------------------------------------------------------------------------
# Private
@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.
# This is useful for two reasons:
@@ -690,6 +720,9 @@ def calling_interesting_method?(name)
end
end
def calling_interesting_line?
+ path = File.expand_path(@file) # rescue @file
+ #puts "Checking #{path} !~ #{@file_match}"
+ return false if path !~ @file_match
return true if @line_matches.nil? # No filter to apply
line = code_for(@file, @line) or return false
(line =~ @line_matches)
@@ -1170,6 +1203,29 @@ def #{method_name}
end
herald '-----------------------------------------------------------'
+ herald 'Test :file_match'
+ herald 'Should only see calls to in_this_file'
+ require_local '../test/other_file'
+ def in_this_file
+ a = 'a'
+ b = 'b'
+ c = 'c'
+ etc = 'etc.'
+ end
+ Unroller::trace(:file_match => __FILE__) do
+ in_this_file()
+ in_other_file()
+ end
+
+ herald '-----------------------------------------------------------'
+ herald 'Test :dir_match'
+ herald 'Should only see calls to in_this_file'
+ Unroller::trace(:dir_match => __FILE__) do
+ in_this_file()
+ in_other_file()
+ end
+
+ herald '-----------------------------------------------------------'
herald 'Test @exclude_methods'
herald 'Should only see calls to green, strong'
class Wasabi #:nodoc:
Please sign in to comment.
Something went wrong with that request. Please try again.