peekaboo 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|