peekaboo 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/README.md +51 -2
- data/Rakefile +1 -1
- data/lib/peekaboo.rb +61 -4
- data/lib/peekaboo/configuration.rb +24 -0
- data/lib/peekaboo/version.rb +3 -2
- data/spec/peekaboo/configuration_spec.rb +78 -11
- data/spec/peekaboo_spec.rb +56 -4
- data/spec/spec_helper.rb +3 -1
- metadata +4 -4
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -25,11 +25,11 @@ When creating a new class, include Peekaboo and then call `enable_tracing_on`, p
|
|
25
25
|
enable_tracing_on :foo, :bar
|
26
26
|
|
27
27
|
def foo
|
28
|
-
|
28
|
+
#...
|
29
29
|
end
|
30
30
|
|
31
31
|
def bar
|
32
|
-
|
32
|
+
#...
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -51,6 +51,15 @@ Now, with tracing enabled, Peekaboo will report when/where those methods are cal
|
|
51
51
|
|
52
52
|
@obj.baz :one, 2, "three"
|
53
53
|
|
54
|
+
**NOTE:** Once a class has already included `Peekaboo`, you can call `enable_tracing_on` directly on the class.
|
55
|
+
|
56
|
+
class SomeClass
|
57
|
+
include Peekaboo
|
58
|
+
# methods n' such
|
59
|
+
end
|
60
|
+
|
61
|
+
SomeClass.enable_tracing_on :this, :that, :the_other
|
62
|
+
|
54
63
|
## Configuration
|
55
64
|
|
56
65
|
The default tracer for Peekaboo is an instance of `Logger` streaming to `STDOUT`.
|
@@ -67,6 +76,46 @@ If this doesn't suit your needs, it is a trivial task to set the tracer to anoth
|
|
67
76
|
config.trace_with @custom_logger_object
|
68
77
|
end
|
69
78
|
|
79
|
+
### Auto-inclusion ( Exciting NEW FEATURE )
|
80
|
+
|
81
|
+
Want to use tracing in classes without having to open up their definitions?
|
82
|
+
Simply provide a list of classes to the configuration.
|
83
|
+
|
84
|
+
Peekaboo.configure do |config|
|
85
|
+
config.autoinclude_with Zip, Zap, Boom
|
86
|
+
end
|
87
|
+
|
88
|
+
# Then inside your code somewhere
|
89
|
+
Zip.enable_tracing_on # ...
|
90
|
+
Zap.enable_tracing_on # ...
|
91
|
+
Boom.enable_tracing_on # ...
|
92
|
+
|
93
|
+
By configuring auto-inclusion, `Peekaboo` will load itself into your class *dynamically* at runtime.
|
94
|
+
All that's left for you to do is call `enable_tracing_on` with a list of methods you want to trace.
|
95
|
+
|
96
|
+
Easy, huh? *It gets better!*
|
97
|
+
|
98
|
+
This feature also works with class hierarchies, meaning that, if you setup auto-inclusion on a given class,
|
99
|
+
it will be enabled for any class that inherits from that class.
|
100
|
+
|
101
|
+
**This does NOT mean that Peekaboo gets loaded into every subclass, but only that it's available for dynamic inclusion.**
|
102
|
+
|
103
|
+
class Weapon
|
104
|
+
end
|
105
|
+
|
106
|
+
class Firearm < Weapon
|
107
|
+
end
|
108
|
+
|
109
|
+
class Pistol < Firearm
|
110
|
+
end
|
111
|
+
|
112
|
+
Peeakboo.configure do |config|
|
113
|
+
config.autoinclude_with Weapon
|
114
|
+
end
|
115
|
+
|
116
|
+
Pistol.enable_tracing_one # Peekaboo loaded, Weapon & Firearm still left unchanged
|
117
|
+
Firearm.enable_tracing_on # Peekaboo loaded, Weapon left unchanged
|
118
|
+
|
70
119
|
## Issues
|
71
120
|
|
72
121
|
Please report any bugs or issues to the [Issue Tracking System](http://github.com/sgarcia/peekaboo/issues/).
|
data/Rakefile
CHANGED
@@ -6,12 +6,12 @@ require 'yard'
|
|
6
6
|
|
7
7
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
8
8
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
9
|
-
t.warning = true
|
10
9
|
t.verbose = true
|
11
10
|
end
|
12
11
|
|
13
12
|
Rcov::RcovTask.new(:rcov) do |t|
|
14
13
|
t.test_files = FileList['spec/**/*_spec.rb']
|
14
|
+
t.rcov_opts = ['--exclude', '/gems/,spec'] # /gems/,/Library/,spec,.bundle,config
|
15
15
|
t.verbose = true
|
16
16
|
end
|
17
17
|
|
data/lib/peekaboo.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
1
|
require 'peekaboo/configuration'
|
2
2
|
|
3
|
+
# The workhorse of this "Unobtrusive Tracing System".
|
3
4
|
module Peekaboo
|
4
|
-
|
5
5
|
class << self
|
6
|
+
# @return [Configuration] the current configuration
|
6
7
|
def configuration
|
7
8
|
@configuration ||= Configuration.new
|
8
9
|
end
|
9
10
|
|
11
|
+
# Convenience method added to assist in configuring the system
|
12
|
+
# ( see {Configuration} for details on all options ).
|
13
|
+
#
|
14
|
+
# @example Configuring the system inside your project
|
15
|
+
# Peekaboo.configure do |config|
|
16
|
+
# config.trace_with MyCustomerLogger.new
|
17
|
+
# config.autoinclude_with SomeBaseClass, AnotherSoloClass
|
18
|
+
# end
|
10
19
|
def configure
|
11
20
|
yield configuration
|
12
21
|
end
|
13
22
|
|
23
|
+
# Callback used to hook tracing system into any class.
|
24
|
+
#
|
25
|
+
# @param [Class] klass including class
|
14
26
|
def included klass
|
15
27
|
klass.const_set :PEEKABOO_METHOD_LIST, []
|
28
|
+
klass.instance_variable_set :@_hooked_by_peekaboo, true
|
16
29
|
klass.extend SingletonMethods
|
17
30
|
|
18
31
|
def klass.method_added name
|
@@ -20,7 +33,28 @@ module Peekaboo
|
|
20
33
|
end
|
21
34
|
end
|
22
35
|
|
23
|
-
#
|
36
|
+
# Modifies a class, and its child classes, to dynamically include module
|
37
|
+
# at runtime. This method is used by {Configuration#autoinclude_with}.
|
38
|
+
#
|
39
|
+
# @param [Class] klass class to modify
|
40
|
+
def setup_autoinclusion klass
|
41
|
+
def klass.method_missing(method_name, *args, &block)
|
42
|
+
if method_name.to_s =~ /^enable_tracing_on$/
|
43
|
+
instance_eval { include Peekaboo }
|
44
|
+
enable_tracing_on *args
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Takes a class object and method name, aliases the original method,
|
52
|
+
# and redefines the method with injected tracing.
|
53
|
+
#
|
54
|
+
# @note Should I add execution time to tracing? Configurable?
|
55
|
+
#
|
56
|
+
# @param [Class] klass method owner
|
57
|
+
# @param [Symbol] name method to trace
|
24
58
|
def wrap_method klass, name
|
25
59
|
return if @_adding_a_method
|
26
60
|
@_adding_a_method = true
|
@@ -48,21 +82,44 @@ module Peekaboo
|
|
48
82
|
end
|
49
83
|
end
|
50
84
|
|
85
|
+
# Contains methods added to every class that includes the *Peekaboo* module,
|
86
|
+
# either through _direct_ or _auto_ inclusion.
|
51
87
|
module SingletonMethods
|
88
|
+
# @return [Array<Symbol>]
|
89
|
+
# a list of instance methods that are being traced inside calling class
|
52
90
|
def peek_list
|
53
91
|
self::PEEKABOO_METHOD_LIST
|
54
92
|
end
|
55
93
|
|
94
|
+
# Enables instance method tracing on calling class.
|
95
|
+
#
|
96
|
+
# @example Trace a couple of methods
|
97
|
+
# class SomeClass
|
98
|
+
# include Peekaboo
|
99
|
+
#
|
100
|
+
# def method1; end
|
101
|
+
# def method2; end
|
102
|
+
# def method3; end
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# # Tracing will be performed on method1(), method2(), but NOT method3()
|
106
|
+
# SomeClass.enable_tracing_on :method1, :method2
|
107
|
+
#
|
108
|
+
# @param [*Symbol] method_names
|
109
|
+
# the list of methods that you want to trace
|
110
|
+
# @raise [RuntimeError]
|
111
|
+
# when attempting to add a method that is already being traced
|
56
112
|
def enable_tracing_on *method_names
|
113
|
+
include Peekaboo unless @_hooked_by_peekaboo
|
114
|
+
|
57
115
|
method_names.each do |method_name|
|
58
116
|
unless peek_list.include? method_name
|
59
117
|
peek_list << method_name
|
60
|
-
Peekaboo.wrap_method
|
118
|
+
Peekaboo.wrap_method self, method_name if self.instance_methods(false).include? method_name.to_s
|
61
119
|
else
|
62
120
|
raise "Already tracing `#{method_name}'"
|
63
121
|
end
|
64
122
|
end
|
65
123
|
end
|
66
124
|
end
|
67
|
-
|
68
125
|
end
|
@@ -1,10 +1,34 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Peekaboo
|
5
|
+
# Rome wasn't built in a day...
|
6
|
+
# Documentation soon to come.
|
4
7
|
class Configuration
|
5
8
|
|
6
9
|
TRACE_LEVELS = [ :debug, :info, :warn, :error, :fatal, :unknown ]
|
7
10
|
|
11
|
+
def initialize
|
12
|
+
@autoincluded = Set.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def autoincluded
|
16
|
+
@autoincluded.to_a
|
17
|
+
end
|
18
|
+
|
19
|
+
def autoinclude_with *klasses
|
20
|
+
if klasses.all? { |klass| klass.instance_of? Class }
|
21
|
+
@autoincluded.merge klasses
|
22
|
+
|
23
|
+
autoincluded.each do |klass|
|
24
|
+
next if klass.included_modules.include? Peekaboo.to_s
|
25
|
+
Peekaboo.setup_autoinclusion klass
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise 'Auto-inclusion can only be used with classes'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
8
32
|
def tracer
|
9
33
|
@tracer ||= Logger.new STDOUT
|
10
34
|
end
|
data/lib/peekaboo/version.rb
CHANGED
@@ -6,29 +6,96 @@ describe Peekaboo::Configuration do
|
|
6
6
|
@config = Peekaboo::Configuration.new
|
7
7
|
end
|
8
8
|
|
9
|
-
context "
|
10
|
-
it "should
|
9
|
+
context "autoinclusion" do
|
10
|
+
it "should not reference any class by default" do
|
11
|
+
@config.autoincluded.should be_empty
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should raise an exception when trying to add a non-class objects" do
|
15
|
+
lambda {
|
16
|
+
[ [Object.new], ["", Hash] ].each { |object_list| @config.autoinclude_with *object_list }
|
17
|
+
}.should raise_exception("Auto-inclusion can only be used with classes")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow class objects to be added" do
|
21
|
+
test_class1 = new_test_class
|
22
|
+
test_class2 = new_test_class
|
23
|
+
test_class3 = new_test_class
|
24
|
+
|
25
|
+
lambda {
|
26
|
+
[ [test_class1],
|
27
|
+
[test_class1, test_class2],
|
28
|
+
[test_class1, test_class2, test_class3]
|
29
|
+
].each { |klass_list| @config.autoinclude_with *klass_list }
|
30
|
+
}.should_not raise_exception
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should maintain a list of classes to use" do
|
34
|
+
test_class1 = new_test_class
|
35
|
+
test_class2 = new_test_class
|
36
|
+
test_class3 = new_test_class
|
37
|
+
|
38
|
+
@config.autoinclude_with test_class1, test_class2, test_class3
|
39
|
+
@config.autoincluded.should =~ [ test_class1, test_class2, test_class3 ]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should ensure its list of classes is unique" do
|
43
|
+
test_class1 = new_test_class
|
44
|
+
test_class2 = new_test_class
|
45
|
+
|
46
|
+
@config.autoinclude_with test_class1, test_class2, test_class1, test_class2
|
47
|
+
@config.autoincluded.should =~ [ test_class1, test_class2 ]
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should auto-include Peekaboo into any class in its list" do
|
51
|
+
test_class = new_test_class
|
52
|
+
@config.autoinclude_with test_class
|
53
|
+
lambda { test_class.enable_tracing_on }.should_not raise_exception
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should auto-include Peekaboo into any class that inherits from a class in its list" do
|
57
|
+
parent_class = new_test_class
|
58
|
+
child_class = Class.new(parent_class)
|
59
|
+
@config.autoinclude_with parent_class
|
60
|
+
lambda { child_class.enable_tracing_on }.should_not raise_exception
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "tracing" do
|
65
|
+
it "should have a default implementation" do
|
11
66
|
@config.tracer.should_not be_nil
|
12
67
|
end
|
13
68
|
|
14
|
-
it "should
|
69
|
+
it "should maintain the 'Logger' interface by default" do
|
15
70
|
[ :debug, :info, :warn, :error, :fatal, :unknown ].each { |logger_msg|
|
16
71
|
@config.tracer.should respond_to(logger_msg)
|
17
72
|
}
|
18
73
|
end
|
19
|
-
end
|
20
74
|
|
21
|
-
|
22
|
-
|
23
|
-
|
75
|
+
it "should use any tracer that maintains the 'Logger' interface" do
|
76
|
+
tmp_file = Tempfile.new 'some-log.txt'
|
77
|
+
some_logger_obj = MyLogger = Class.new do
|
78
|
+
def debug(msg) ; end
|
79
|
+
def info(msg) ; end
|
80
|
+
def warn(msg) ; end
|
81
|
+
def error(msg) ; end
|
82
|
+
def fatal(msg) ; end
|
83
|
+
def unknown(msg) ; end
|
84
|
+
end.new
|
85
|
+
|
86
|
+
tracers = [ Logger.new(tmp_file.path), some_logger_obj ]
|
87
|
+
|
88
|
+
tracers.each do |some_tracer|
|
89
|
+
@config.trace_with some_tracer
|
90
|
+
@config.tracer.should == some_tracer
|
91
|
+
end
|
24
92
|
|
25
|
-
|
26
|
-
@config.tracer.should == new_tracer
|
93
|
+
tmp_file.close!
|
27
94
|
end
|
28
95
|
|
29
|
-
it "should
|
96
|
+
it "should raise an exception when attempting to use an object that does not maintaint the 'Logger' interface" do
|
30
97
|
lambda {
|
31
|
-
@config.trace_with 'garbage
|
98
|
+
@config.trace_with 'garbage object'
|
32
99
|
}.should raise_exception('Tracer must respond to debug(), info(), warn(), error(), fatal(), and unknown()')
|
33
100
|
end
|
34
101
|
end
|
data/spec/peekaboo_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe Peekaboo do
|
|
4
4
|
|
5
5
|
context ".configuration" do
|
6
6
|
it "should hold a reference to the current configuration" do
|
7
|
-
Peekaboo.configuration.should be_an_instance_of
|
7
|
+
Peekaboo.configuration.should be_an_instance_of Peekaboo::Configuration
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
@@ -23,7 +23,7 @@ describe Peekaboo do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should be a singleton method added to any including class" do
|
26
|
-
@test_class.should respond_to
|
26
|
+
@test_class.should respond_to :enable_tracing_on
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should store a list of methods to trace on any including class" do
|
@@ -36,7 +36,7 @@ describe Peekaboo do
|
|
36
36
|
@test_class.enable_tracing_on :some_method
|
37
37
|
lambda {
|
38
38
|
@test_class.enable_tracing_on :some_method
|
39
|
-
}.should raise_exception
|
39
|
+
}.should raise_exception "Already tracing `some_method'"
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -53,7 +53,7 @@ describe Peekaboo do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
it "should not take place on unlisted methods" do
|
56
|
-
@tracer.should_not_receive
|
56
|
+
@tracer.should_not_receive :info
|
57
57
|
@test_instance.method_no_tracing
|
58
58
|
end
|
59
59
|
|
@@ -103,4 +103,56 @@ describe Peekaboo do
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
+
context "autoinclusion tracing" do
|
107
|
+
before(:all) do
|
108
|
+
@tracer = Peekaboo.configuration.tracer
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should inject functionality into an auto-included class" do
|
112
|
+
test_class = new_test_class
|
113
|
+
Peekaboo.configure { |config| config.autoinclude_with test_class }
|
114
|
+
test_class.enable_tracing_on :method_no_args
|
115
|
+
|
116
|
+
@tracer.should_receive(:info).with trace_message "Invoking: #{test_class}#method_no_args with [] ==> Returning: nil"
|
117
|
+
test_class.new.method_no_args
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should inject functionality into any class that inherits from an auto-included class" do
|
121
|
+
parent_class = new_test_class
|
122
|
+
child_class = Class.new(parent_class) do
|
123
|
+
def another_method() ; end
|
124
|
+
end
|
125
|
+
|
126
|
+
Peekaboo.configure { |config| config.autoinclude_with parent_class }
|
127
|
+
child_class.enable_tracing_on :another_method
|
128
|
+
|
129
|
+
@tracer.should_receive(:info).with trace_message "Invoking: #{child_class}#another_method with [] ==> Returning: nil"
|
130
|
+
child_class.new.another_method
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not inject functionality into classes that are not auto-included" do
|
134
|
+
test_class = new_test_class
|
135
|
+
another_test_class = new_test_class
|
136
|
+
|
137
|
+
Peekaboo.configure { |config| config.autoinclude_with test_class }
|
138
|
+
|
139
|
+
lambda { another_test_class.enable_tracing_on :method_no_args }.should raise_exception NoMethodError
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should maintain unique tracing method lists across an inheritance chain" do
|
143
|
+
parent_class = new_test_class
|
144
|
+
child_class = Class.new(parent_class) do
|
145
|
+
def another_method() ; end
|
146
|
+
end
|
147
|
+
|
148
|
+
Peekaboo.configure { |config| config.autoinclude_with parent_class }
|
149
|
+
|
150
|
+
parent_class.enable_tracing_on :method_no_args
|
151
|
+
child_class.enable_tracing_on :another_method
|
152
|
+
|
153
|
+
parent_class.peek_list.should =~ [:method_no_args]
|
154
|
+
child_class.peek_list.should =~ [:another_method]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
106
158
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
$:.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
|
-
require 'peekaboo'
|
5
4
|
require 'ruby-debug'
|
5
|
+
require 'tempfile'
|
6
6
|
require 'spec'
|
7
7
|
require 'spec/autorun'
|
8
8
|
|
9
|
+
require 'peekaboo'
|
10
|
+
|
9
11
|
Spec::Runner.configure do |config|
|
10
12
|
def new_test_class
|
11
13
|
Class.new do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: peekaboo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sonny Ruben Garcia
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-28 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|