minstrel 0.1.20101115013901 → 0.2.20101115030455

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -3,11 +3,19 @@ h1. Ruby Minstrel
3
3
  Minstrel allows you to wrap every method call for a given class so you can more
4
4
  easily observe how a class and its methods are being used.
5
5
 
6
+ h2. Get it
7
+
8
+ * gem install minstrel
9
+ * or download versions here: http://rubygems.org/gems/minstrel
10
+ * or github: https://github.com/jordansissel/ruby-minstrel
11
+
6
12
  h2. Why?
7
13
 
8
14
  Fun. Also, sometimes ruby-prof and similar tools are overkill when I am trying
9
15
  to debug or dig into how a piece of code works.
10
16
 
17
+ It's a lot like strace/tcpdump/dtrace for ruby, or aims to be, anyway.
18
+
11
19
  h2. Examples
12
20
 
13
21
  h3. From the commandline
data/bin/minstrel ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ require "rubygems"
4
+ require "minstrel"
5
+
6
+ load ARGV[0]
data/lib/minstrel.rb CHANGED
@@ -18,11 +18,18 @@
18
18
  module Minstrel; class Instrument
19
19
  attr_accessor :counter
20
20
 
21
+ class << self
22
+ @@deferred_wraps = {}
23
+ end
24
+
21
25
  # Put methods we must not be wrapping here.
22
- DONOTWRAP = [Kernel, Object, Module, Class, Minstrel::Instrument].collect do |obj|
26
+ #DONOTWRAP = #[Kernel, Object, Module, Class,
27
+ DONOTWRAP = [Minstrel::Instrument].collect do |obj|
23
28
  obj.methods.collect { |m| m.to_sym }
24
29
  end.flatten
25
30
  DONOTWRAP << :to_sym
31
+ DONOTWRAP << :respond_to?
32
+ DONOTWRAP << :send
26
33
 
27
34
  # Wrap a class's instance methods with your block.
28
35
  # The block will be called with 4 arguments, and called
@@ -33,35 +40,118 @@ module Minstrel; class Instrument
33
40
  # * method - the method (symbol) being called
34
41
  # * *args - the arguments (array) passed to this method.
35
42
  def wrap(klass, &block)
43
+ #puts "Instrumenting #{klass.name} with #{block.inspect}"
36
44
  instrumenter = self
37
- self.counter = 0
38
45
 
39
46
  klass.instance_methods.each do |method|
40
47
  next if DONOTWRAP.include?(method.to_sym)
41
48
  klass.class_eval do
42
49
  orig_method = "#{method}_original(wrapped)".to_sym
43
50
  alias_method orig_method, method.to_sym
44
- instrumenter.counter += 1
45
51
  method = method.to_sym
52
+ #block.call(:wrap, klass, method)
46
53
  define_method(method) do |*args, &argblock|
54
+ puts "call #{klass.name}##{method}(#{args.inspect}, #{block_given?})"
47
55
  block.call(:enter, klass, method, *args)
48
- val = send(orig_method, *args, &argblock)
49
- block.call(:exit, klass, method, *args)
56
+ exception = false
57
+ begin
58
+ m = self.method(orig_method)
59
+ val = m.call(*args, &argblock)
60
+ rescue => e
61
+ exception = e
62
+ end
63
+ if exception
64
+ block.call(:exit_exception, klass, method, *args)
65
+ raise e if exception
66
+ else
67
+ block.call(:exit, klass, method, *args)
68
+ end
50
69
  return val
51
70
  end
52
71
  end # klass.class_eval
53
72
  end # klass.instance_methods.each
73
+
74
+ klass.methods.each do |method|
75
+ next if DONOTWRAP.include?(method.to_sym)
76
+ klass.instance_eval do
77
+ orig_method = "#{method}_original(classwrapped)".to_sym
78
+ (class << self; self; end).instance_eval do
79
+ begin
80
+ alias_method orig_method, method.to_sym
81
+ rescue NameError => e
82
+ # No such method, strange but true.
83
+ orig_method = self.method(method.to_sym)
84
+ end
85
+ method = method.to_sym
86
+ define_method(method) do |*args, &argblock|
87
+ block.call(:class_enter, klass, method, *args)
88
+ exception = false
89
+ begin
90
+ if orig_method.is_a?(Symbol)
91
+ val = send(orig_method, *args, &argblock)
92
+ else
93
+ val = orig_method.call(*args, &argblock)
94
+ end
95
+ rescue => e
96
+ exception = e
97
+ end
98
+ if exception
99
+ block.call(:class_exit_exception, klass, method, *args)
100
+ raise e if exception
101
+ else
102
+ block.call(:class_exit, klass, method, *args)
103
+ end
104
+ return val
105
+ end
106
+ end
107
+ #block.call(:class_wrap, klass, method, self.method(method))
108
+ end # klass.class_eval
109
+ end # klass.instance_methods.each
54
110
  end # def wrap
111
+
112
+ def wrap_classname(klassname, &block)
113
+ begin
114
+ klass = eval(klassname)
115
+ self.wrap(klass, &block)
116
+ return true
117
+ rescue NameError => e
118
+ @@deferred_wraps[klassname] = block
119
+ end
120
+ return false
121
+ end
122
+
123
+ def self.wrap_require
124
+ Kernel.class_eval do
125
+ alias_method :old_require, :require
126
+ def require(*args)
127
+ return Minstrel::Instrument::instrumented_require(*args)
128
+ end
129
+ end
130
+ end
131
+
132
+ def self.instrumented_require(*args)
133
+ ret = old_require(*args)
134
+ klasses = @@deferred_wraps.keys
135
+ klasses.each do |klassname|
136
+ block = @@deferred_wraps[klassname]
137
+ instrument = Minstrel::Instrument.new
138
+ if instrument.wrap_classname(klassname, &block)
139
+ puts "Wrap of #{klassname} successful"
140
+ @@deferred_wraps.delete(klassname)
141
+ end
142
+ end
143
+ return ret
144
+ end
55
145
  end; end # class Minstrel::Instrument
56
146
 
147
+ Minstrel::Instrument.wrap_require
148
+
57
149
  # Provide a way to instrument a class using the command line:
58
150
  # RUBY_INSTRUMENT=String ruby -rminstrel ./your/program
59
151
  if ENV["RUBY_INSTRUMENT"]
60
152
  ENV["RUBY_INSTRUMENT"].split(",").each do |klassname|
61
153
  instrument = Minstrel::Instrument.new
62
- klass = eval(klassname)
63
- instrument.wrap(klass) do |point, klass, method, *args|
64
- next if point == :end
154
+ instrument.wrap_classname(klassname) do |point, klass, method, *args|
65
155
  puts "#{point} #{klass.name}##{method}(#{args.inspect})"
66
156
  end
67
157
  end
data/minstrel.gemspec CHANGED
@@ -8,11 +8,13 @@ Gem::Specification.new do |spec|
8
8
 
9
9
  rev = Time.now.strftime("%Y%m%d%H%M%S")
10
10
  spec.name = "minstrel"
11
- spec.version = "0.1.#{rev}"
11
+ spec.version = "0.2.#{rev}"
12
12
  spec.summary = "minstrel - a ruby instrumentation tool"
13
13
  spec.description = "Instrument class methods"
14
14
  spec.files = files
15
15
  spec.require_paths << "lib"
16
+ spec.bindir = "bin"
17
+ spec.executables << "minstrel"
16
18
 
17
19
  spec.author = "Jordan Sissel"
18
20
  spec.email = "jls@semicomplete.com"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minstrel
3
3
  version: !ruby/object:Gem::Version
4
- hash: 40202230027777
4
+ hash: 40202230060921
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 20101115013901
10
- version: 0.1.20101115013901
8
+ - 2
9
+ - 20101115030455
10
+ version: 0.2.20101115030455
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jordan Sissel
@@ -21,8 +21,8 @@ dependencies: []
21
21
 
22
22
  description: Instrument class methods
23
23
  email: jls@semicomplete.com
24
- executables: []
25
-
24
+ executables:
25
+ - minstrel
26
26
  extensions: []
27
27
 
28
28
  extra_rdoc_files: []
@@ -31,6 +31,7 @@ files:
31
31
  - ./lib/minstrel.rb
32
32
  - ./README.textile
33
33
  - ./minstrel.gemspec
34
+ - bin/minstrel
34
35
  has_rdoc: true
35
36
  homepage: https://github.com/jordansissel/ruby-minstrel
36
37
  licenses: []