peekaboo 0.2.1 → 0.3.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/.gitignore +2 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +13 -1
- data/Gemfile +8 -0
- data/README.md +59 -32
- data/Rakefile +4 -5
- data/lib/peekaboo.rb +78 -76
- data/lib/peekaboo/singleton_methods.rb +126 -0
- data/lib/peekaboo/version.rb +3 -3
- data/peekaboo.gemspec +2 -3
- data/spec/peekaboo_spec.rb +267 -11
- data/spec/spec_helper.rb +42 -14
- metadata +12 -39
data/.gitignore
CHANGED
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## 0.3.0 (November 8, 2010)
|
4
|
+
|
5
|
+
Features:
|
6
|
+
|
7
|
+
- Adds support for class & instance method tracing via `.enable_tracing_for`
|
8
|
+
- Adds convenience methods for inspecting traced methods within a class
|
9
|
+
- `.traced_method_map`
|
10
|
+
- `.traced_instance_methods`
|
11
|
+
- `.traced_singleton_methods`
|
12
|
+
- Deprecates `.enable_tracing_on` & `.peek_list`
|
13
|
+
|
1
14
|
## 0.2.1 (November 4, 2010)
|
2
15
|
|
3
16
|
Bugfix:
|
@@ -16,4 +29,3 @@ Features:
|
|
16
29
|
|
17
30
|
- Adds support for instance method tracing
|
18
31
|
- Configurable tracer via `Peekaboo.configure`
|
19
|
-
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -1,67 +1,94 @@
|
|
1
|
-
# Peekaboo
|
1
|
+
# Peekaboo - Unobtrusive method tracing for Ruby classes
|
2
2
|
|
3
3
|
Do you find yourself constantly adding log statements to a lot of the methods in your project?
|
4
4
|
Does it lead to a lot of duplication and make your code feel less "elegant"?
|
5
5
|
Peekaboo offers an alternative approach to tracing method calls, their provided arguments, and their return values.
|
6
6
|
Simply specify which methods you want to trace inside a class and let peekaboo take care of the rest.
|
7
7
|
|
8
|
-
|
8
|
+
**( Compatible with Ruby versions 1.8.7 and 1.9.2 )**
|
9
9
|
|
10
|
-
|
10
|
+
## Usage
|
11
|
+
|
12
|
+
### Installation
|
13
|
+
|
14
|
+
Install via Rubygems:
|
11
15
|
|
12
16
|
$ gem install peekaboo
|
13
17
|
|
14
|
-
###
|
18
|
+
### Concept
|
15
19
|
|
16
|
-
Peekaboo uses method wrapping and an internal tracer to capture
|
20
|
+
Peekaboo uses method wrapping and an internal tracer to capture data about method calls.
|
17
21
|
Its tracer adheres to the API established by the Logger class ( i.e. debug, info, warn, etc... ).
|
18
22
|
For now, the only trace level supported is "info", but there are plans to support all trace levels in the future.
|
19
|
-
Also, this first cut only provides tracing for _instance_ methods.
|
20
23
|
|
21
|
-
|
24
|
+
Including Peekaboo into your class definition initializes the system within the context of that class and adds
|
25
|
+
a number of class methods that can be used to create and inspect traced methods.
|
22
26
|
|
27
|
+
require 'peekaboo'
|
28
|
+
|
23
29
|
class Example
|
24
30
|
include Peekaboo
|
25
|
-
|
26
|
-
|
27
|
-
def foo
|
28
|
-
#...
|
29
|
-
end
|
30
|
-
|
31
|
-
def bar
|
32
|
-
#...
|
33
|
-
end
|
31
|
+
# ...
|
34
32
|
end
|
35
33
|
|
36
|
-
|
37
|
-
In that case, simply reopen the class definition and follow the same steps listed above.
|
34
|
+
It is also possible to enable tracing without explicitly including Peekaboo. See the ["Auto-inclusion"](#Auto-inclusion) for details.
|
38
35
|
|
39
|
-
|
40
|
-
|
36
|
+
### Method Tracing
|
37
|
+
|
38
|
+
Once Peekaboo has been enabled within a class you can call `enable_tracing_for`, inside the class definition or
|
39
|
+
directly on the class object, passing it a structured hash of method names. The hash should contain 1 or 2 keys,
|
40
|
+
`:singleton_methods` and `:instance_methods`, each pointing to an array of symbolized method names.
|
41
|
+
|
42
|
+
# Calling inside class definition
|
41
43
|
class Example
|
42
|
-
|
43
|
-
|
44
|
+
enable_tracing_for :singleton_methods => [:first_class_method, :second_class_method],
|
45
|
+
:instance_methods => [:an_instance_method]
|
46
|
+
|
47
|
+
# method n' such...
|
44
48
|
end
|
49
|
+
|
50
|
+
# Calling on class object
|
51
|
+
Example.enable_tracing_for # same arguments as above
|
45
52
|
|
46
53
|
Now, with tracing enabled, Peekaboo will report when/where those methods are called along with their input and output values.
|
47
54
|
|
55
|
+
# Peekaboo tracer receives the following message when .first_class_method is called below:
|
56
|
+
# "File:Line ( Example.first_class_method called with [] ==> Returning: 'whatever gets returned' )"
|
57
|
+
Example.first_class_method
|
58
|
+
|
48
59
|
# @obj is an instance of Example
|
49
60
|
# Peekaboo tracer receives the following message when #baz is called below:
|
50
|
-
# "File:Line ( Example#
|
51
|
-
|
52
|
-
@obj.baz :one, 2, "three"
|
61
|
+
# "File:Line ( Example#an_instance_method called with [:one, 2, "three"] ==> Returning: 'whatever gets returned' )"
|
62
|
+
@obj.an_instance_method :one, 2, "three"
|
53
63
|
|
54
|
-
|
64
|
+
### Pre-registration of Methods
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
|
66
|
+
Sometimes, in Ruby, we need to define methods at runtime based on some aspect of our application. Fortunately,
|
67
|
+
Peekaboo allows you to _register_ a method signature for tracing without enforcing that the method actually exists.
|
68
|
+
If any methods that you register get added to your type during program execution, Peekaboo will trace calls to
|
69
|
+
those methods in exactly the same fashion as before.
|
70
|
+
|
71
|
+
class DynamicEntity
|
72
|
+
# #might_need_it is not yet defined
|
73
|
+
enable_tracing_for :instance_methods => [:might_need_it]
|
59
74
|
end
|
60
75
|
|
61
|
-
|
76
|
+
# somewhere else in the codebase the pre-registered method gets defined
|
77
|
+
DynamicEntity.class_eval do
|
78
|
+
def might_need_it
|
79
|
+
# ...
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
DynamicEntity.new.might_need_it # calls out to the Peekaboo tracer
|
62
84
|
|
63
85
|
## Configuration
|
64
86
|
|
87
|
+
There are a number of ways to configure Peekaboo for your project. Please read each section below for information
|
88
|
+
on a particular configuration option.
|
89
|
+
|
90
|
+
### Method Tracer
|
91
|
+
|
65
92
|
The default tracer for Peekaboo is an instance of `Logger` streaming to `STDOUT`.
|
66
93
|
If this doesn't suit your needs, it is a trivial task to set the tracer to another object using the Peekaboo configuration.
|
67
94
|
|
@@ -76,7 +103,7 @@ If this doesn't suit your needs, it is a trivial task to set the tracer to anoth
|
|
76
103
|
config.trace_with @custom_logger_object
|
77
104
|
end
|
78
105
|
|
79
|
-
### Auto-inclusion
|
106
|
+
### Auto-inclusion
|
80
107
|
|
81
108
|
Want to use tracing in classes without having to open up their definitions?
|
82
109
|
Simply provide a list of classes to the configuration.
|
@@ -127,7 +154,7 @@ If you're looking to contribute please read the contribution guidelines before s
|
|
127
154
|
|
128
155
|
## License
|
129
156
|
|
130
|
-
Copyright (c)
|
157
|
+
Copyright (c) 2010 Sonny Ruben Garcia
|
131
158
|
|
132
159
|
Permission is hereby granted, free of charge, to any person obtaining
|
133
160
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
3
4
|
require 'spec/rake/spectask'
|
4
5
|
require 'rcov/rcovtask'
|
5
6
|
require 'yard'
|
@@ -11,10 +12,8 @@ end
|
|
11
12
|
|
12
13
|
Rcov::RcovTask.new(:rcov) do |t|
|
13
14
|
t.test_files = FileList['spec/**/*_spec.rb']
|
14
|
-
t.rcov_opts = ['--exclude', '/gems/,spec']
|
15
|
+
t.rcov_opts = ['--exclude', '/gems/,spec']
|
15
16
|
t.verbose = true
|
16
17
|
end
|
17
18
|
|
18
|
-
YARD::Rake::YardocTask.new(:yard)
|
19
|
-
t.files = ["lib/**/*.rb", "-", "CHANGELOG.md"]
|
20
|
-
end
|
19
|
+
YARD::Rake::YardocTask.new(:yard)
|
data/lib/peekaboo.rb
CHANGED
@@ -1,71 +1,95 @@
|
|
1
1
|
require 'peekaboo/configuration'
|
2
|
+
require 'peekaboo/singleton_methods'
|
2
3
|
|
3
|
-
#
|
4
|
+
# This system has been designed to provide you with an easy and unobtrusive way to trace:
|
5
|
+
# * _When_ certain methods are being called
|
6
|
+
# * _What_ values they are being supplied
|
7
|
+
# * _What_ values they return
|
8
|
+
# * _If_ they raise an exception
|
9
|
+
#
|
10
|
+
# Its API supports both class and instance method tracing inside any of your custom types.
|
11
|
+
# You can enable tracing for existing methods and/or pre-register method signatures for any of your types.
|
12
|
+
# The latter option gives you the ability to trace any methods that are defined _dynamically_ at runtime.
|
13
|
+
#
|
14
|
+
# ( see {SingletonMethods#enable_tracing_for} for details )
|
15
|
+
#
|
16
|
+
# You can also setup *auto-inclusion*, which will allow you _dynamically_ include this module into any of
|
17
|
+
# your types at runtime. This alleviates the hassle of having to "+include Peekaboo+" inside all of the
|
18
|
+
# classes that you intend use it.
|
19
|
+
#
|
20
|
+
# ( see {Configuration#autoinclude_with} for details )
|
4
21
|
module Peekaboo
|
5
22
|
class << self
|
6
|
-
# @
|
23
|
+
# @private
|
7
24
|
def configuration
|
8
25
|
@configuration ||= Configuration.new
|
9
26
|
end
|
10
27
|
|
11
|
-
#
|
12
|
-
# ( see {Configuration} for details on all options ).
|
28
|
+
# Use this to configure various aspects of tracing in your application.
|
13
29
|
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# config.trace_with MyCustomerLogger.new
|
17
|
-
# config.autoinclude_with SomeBaseClass, AnotherSoloClass
|
18
|
-
# end
|
30
|
+
# See {Configuration} for option details.
|
31
|
+
# @yieldparam [Configuration] config current configuration
|
19
32
|
def configure
|
20
33
|
yield configuration
|
21
34
|
end
|
22
35
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# @param [Class] klass including class
|
36
|
+
# @private
|
26
37
|
def included klass
|
27
|
-
klass.const_set :
|
38
|
+
klass.const_set :PEEKABOO_METHOD_MAP, { :singleton_methods => Set.new, :instance_methods => Set.new }.freeze
|
28
39
|
klass.instance_variable_set :@_hooked_by_peekaboo, true
|
29
40
|
klass.extend SingletonMethods
|
30
41
|
|
31
42
|
def klass.method_added name
|
32
|
-
Peekaboo.
|
43
|
+
Peekaboo.wrap self, name, :instance if traced_instance_methods.include? name
|
44
|
+
end
|
45
|
+
|
46
|
+
def klass.singleton_method_added name
|
47
|
+
Peekaboo.wrap self, name, :singleton if traced_singleton_methods.include? name
|
33
48
|
end
|
34
49
|
end
|
35
50
|
|
36
|
-
#
|
37
|
-
# at runtime. This method is used by {Configuration#autoinclude_with}.
|
38
|
-
#
|
39
|
-
# @param [Class] klass class to modify
|
51
|
+
# @private
|
40
52
|
def setup_autoinclusion klass
|
53
|
+
# @note changes made to this methods to support backwards
|
54
|
+
# compatibility with {#enable_tracing_on}. This will become
|
55
|
+
# much simpler when that method is removed.
|
41
56
|
def klass.method_missing(method_name, *args, &block)
|
42
|
-
if method_name.to_s =~ /^
|
57
|
+
if method_name.to_s =~ /^enable_tracing_(on|for)$/
|
43
58
|
instance_eval { include Peekaboo }
|
44
|
-
|
59
|
+
__send__ method_name, *args
|
45
60
|
else
|
46
61
|
super
|
47
62
|
end
|
48
63
|
end
|
49
64
|
end
|
50
65
|
|
51
|
-
#
|
52
|
-
|
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
|
58
|
-
def wrap_method klass, name
|
66
|
+
# @private
|
67
|
+
def wrap klass, method_name, target
|
59
68
|
return if @_adding_a_method
|
60
|
-
|
61
|
-
|
62
|
-
|
69
|
+
begin
|
70
|
+
@_adding_a_method = true
|
71
|
+
original_method = "original_#{method_name}"
|
72
|
+
case target
|
73
|
+
when :singleton then wrap_singleton_method klass, method_name, original_method
|
74
|
+
when :instance then wrap_instance_method klass, method_name, original_method
|
75
|
+
else raise 'Only :class and :instance are valid targets'
|
76
|
+
end
|
77
|
+
rescue => exe
|
78
|
+
raise exe
|
79
|
+
ensure
|
80
|
+
@_adding_a_method = false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def wrap_instance_method klass, method_name, original_method_name
|
63
87
|
method_wrapping = %{
|
64
|
-
alias_method :#{
|
65
|
-
def #{
|
66
|
-
trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}\##{
|
88
|
+
alias_method :#{original_method_name}, :#{method_name}
|
89
|
+
def #{method_name} *args, &block
|
90
|
+
trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}\##{method_name} with \#{args.inspect} "
|
67
91
|
begin
|
68
|
-
result = #{
|
92
|
+
result = #{original_method_name} *args, &block
|
69
93
|
trace << "==> Returning: \#{result.inspect} )"
|
70
94
|
result
|
71
95
|
rescue Exception => exe
|
@@ -77,50 +101,28 @@ module Peekaboo
|
|
77
101
|
end
|
78
102
|
}
|
79
103
|
klass.class_eval method_wrapping
|
80
|
-
|
81
|
-
@_adding_a_method = false
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Contains methods added to every class that includes the *Peekaboo* module,
|
86
|
-
# either through _direct_ or _auto_ inclusion.
|
87
|
-
module SingletonMethods
|
88
|
-
# @return [Array<Symbol>]
|
89
|
-
# a list of instance methods that are being traced inside calling class
|
90
|
-
def peek_list
|
91
|
-
self::PEEKABOO_METHOD_LIST
|
92
104
|
end
|
93
105
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# when attempting to add a method that is already being traced
|
112
|
-
def enable_tracing_on *method_names
|
113
|
-
include Peekaboo unless @_hooked_by_peekaboo
|
114
|
-
|
115
|
-
method_names.each do |method_name|
|
116
|
-
unless peek_list.include? method_name
|
117
|
-
peek_list << method_name
|
118
|
-
method_list = self.instance_methods(false).map(&:to_sym)
|
119
|
-
Peekaboo.wrap_method self, method_name if method_list.include? method_name
|
120
|
-
else
|
121
|
-
raise "Already tracing `#{method_name}'"
|
106
|
+
def wrap_singleton_method klass, method_name, original_method_name
|
107
|
+
method_wrapping = %{
|
108
|
+
class << self
|
109
|
+
alias_method :#{original_method_name}, :#{method_name}
|
110
|
+
def #{method_name} *args, &block
|
111
|
+
trace = "\#{caller(1)[0]}\n\t( Invoking: #{klass}.#{method_name} with \#{args.inspect} "
|
112
|
+
begin
|
113
|
+
result = #{original_method_name} *args, &block
|
114
|
+
trace << "==> Returning: \#{result.inspect} )"
|
115
|
+
result
|
116
|
+
rescue Exception => exe
|
117
|
+
trace << "!!! Raising: \#{exe.message.inspect} )"
|
118
|
+
raise exe
|
119
|
+
ensure
|
120
|
+
Peekaboo.configuration.tracer.info trace
|
121
|
+
end
|
122
|
+
end
|
122
123
|
end
|
123
|
-
|
124
|
+
}
|
125
|
+
klass.instance_eval method_wrapping
|
124
126
|
end
|
125
127
|
end
|
126
128
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Peekaboo
|
2
|
+
# Contains methods added to every class that includes the
|
3
|
+
# {Peekaboo} module, either by _direct_ or _auto_ inclusion.
|
4
|
+
module SingletonMethods
|
5
|
+
# Provides access to traced methods.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# CustomType.traced_method_map # => {:instance_methods=>#<Set: {:foo}>, :singleton_methods=>#<Set: {:bar}>}
|
9
|
+
#
|
10
|
+
# @return [Hash] all methods registered for tracing
|
11
|
+
def traced_method_map
|
12
|
+
self::PEEKABOO_METHOD_MAP
|
13
|
+
end
|
14
|
+
|
15
|
+
# Provides convenient access to traced instance methods.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# CustomType.traced_instance_methods # => #<Set: {:foo}>
|
19
|
+
#
|
20
|
+
# @return [Set<Symbol>] all instance methods registered for tracing
|
21
|
+
def traced_instance_methods
|
22
|
+
traced_method_map[:instance_methods]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Provides convenient access to traced singleton methods.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# CustomType.traced_singleton_methods # => #<Set: {:bar}>
|
29
|
+
#
|
30
|
+
# @return [Set<Symbol>] all singleton methods registered for tracing
|
31
|
+
def traced_singleton_methods
|
32
|
+
traced_method_map[:singleton_methods]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Enables singleton and instance method tracing. If the _method-to-trace_
|
36
|
+
# is not currently defined in the calling class, *Peekaboo* will register
|
37
|
+
# that signature so that tracing is enabled at the time it is added.
|
38
|
+
#
|
39
|
+
# @example Tracing singleton methods
|
40
|
+
# CustomType.enable_tracing_for :singleton_methods => [:a, :b, :c]
|
41
|
+
# @example Tracing instance methods
|
42
|
+
# CustomType.enable_tracing_for :instance_methods => [:one, :two, :three]
|
43
|
+
# @example Tracing a mix of methods
|
44
|
+
# CustomType.enable_tracing_for :singleton_methods => [:this, :that],
|
45
|
+
# :instance_methods => [:the_other]
|
46
|
+
#
|
47
|
+
# @param [Hash] method_map a list of methods to trace
|
48
|
+
# @option method_map [Array<Symbol>] :singleton_methods ([]) singleton method list
|
49
|
+
# @option method_map [Array<Symbol>] :instance_methods ([]) instance method list
|
50
|
+
def enable_tracing_for method_map
|
51
|
+
include Peekaboo unless @_hooked_by_peekaboo
|
52
|
+
|
53
|
+
method_map = { :singleton_methods => [], :instance_methods => [] }.merge method_map
|
54
|
+
|
55
|
+
_register_traceables_ method_map[:instance_methods], :instance
|
56
|
+
_register_traceables_ method_map[:singleton_methods], :singleton
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Registers a list of method signatures and optionally enables tracing on them.
|
62
|
+
# Tracing will only be "enabled" if the method exists and has not already been registered.
|
63
|
+
#
|
64
|
+
# @param [Array<Symbol>] method_list methods to register
|
65
|
+
# @param [Symbol] target specifies the receiver, either +:singleton+ or +:instance+
|
66
|
+
def _register_traceables_ method_list, target
|
67
|
+
method_list.each do |method_name|
|
68
|
+
target_method_list = __send__ :"traced_#{target}_methods"
|
69
|
+
|
70
|
+
unless target_method_list.include? method_name
|
71
|
+
target_method_list << method_name
|
72
|
+
existing_methods = self.__send__(:"#{target}_methods", false).map(&:to_sym)
|
73
|
+
Peekaboo.wrap self, method_name, target if existing_methods.include? method_name
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
#################### DEPRECATED ####################
|
80
|
+
|
81
|
+
|
82
|
+
public
|
83
|
+
|
84
|
+
# @return [Array<Symbol>]
|
85
|
+
# a list of instance methods that are being traced inside calling class
|
86
|
+
# @deprecated
|
87
|
+
# this method will be removed in version 0.4.0, use {#traced_method_map} instead
|
88
|
+
def peek_list
|
89
|
+
traced_instance_methods.to_a
|
90
|
+
end
|
91
|
+
|
92
|
+
# Enables instance method tracing on calling class.
|
93
|
+
#
|
94
|
+
# @example Trace a couple of methods
|
95
|
+
# class SomeClass
|
96
|
+
# include Peekaboo
|
97
|
+
#
|
98
|
+
# def method1; end
|
99
|
+
# def method2; end
|
100
|
+
# def method3; end
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# # Tracing will be performed on method1(), method2(), but NOT method3()
|
104
|
+
# SomeClass.enable_tracing_on :method1, :method2
|
105
|
+
#
|
106
|
+
# @param [*Symbol] method_names
|
107
|
+
# the list of methods that you want to trace
|
108
|
+
# @raise [RuntimeError]
|
109
|
+
# when attempting to add a method that is already being traced
|
110
|
+
# @deprecated
|
111
|
+
# this method will be removed in version 0.4.0, use {#enable_tracing_for} instead
|
112
|
+
def enable_tracing_on *method_names
|
113
|
+
include Peekaboo unless @_hooked_by_peekaboo
|
114
|
+
|
115
|
+
method_names.each do |method_name|
|
116
|
+
unless peek_list.include? method_name
|
117
|
+
traced_instance_methods << method_name
|
118
|
+
method_list = self.instance_methods(false).map(&:to_sym)
|
119
|
+
Peekaboo.wrap self, method_name, :instance if method_list.include? method_name
|
120
|
+
else
|
121
|
+
raise "Already tracing `#{method_name}'"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/peekaboo/version.rb
CHANGED
data/peekaboo.gemspec
CHANGED
@@ -12,10 +12,9 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.homepage = "http://github.com/sgarcia/peekaboo"
|
13
13
|
gem.authors = ["Sonny Ruben Garcia"]
|
14
14
|
|
15
|
-
gem.add_development_dependency "
|
16
|
-
gem.add_development_dependency "rcov", "0.9.9"
|
17
|
-
gem.add_development_dependency "yard", "0.6.1"
|
15
|
+
gem.add_development_dependency "bundler", "1.0.3"
|
18
16
|
|
19
17
|
gem.files = `git ls-files`.split("\n")
|
20
18
|
gem.test_files = `git ls-files -- spec/*`.split("\n")
|
19
|
+
gem.extra_rdoc_files = ["CHANGELOG.md"]
|
21
20
|
end
|
data/spec/peekaboo_spec.rb
CHANGED
@@ -1,22 +1,278 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
|
3
3
|
describe Peekaboo do
|
4
|
-
|
5
|
-
context "
|
6
|
-
it "should
|
4
|
+
|
5
|
+
context "configuration" do
|
6
|
+
it "should be properly referenced" do
|
7
7
|
Peekaboo.configuration.should be_an_instance_of Peekaboo::Configuration
|
8
8
|
end
|
9
|
-
|
10
|
-
|
11
|
-
context ".configure" do
|
12
|
-
it "should yield the current configuration" do
|
9
|
+
|
10
|
+
it "should yield itself" do
|
13
11
|
yielded_object = nil
|
14
12
|
Peekaboo.configure { |x| yielded_object = x }
|
15
13
|
Peekaboo.configuration.should == yielded_object
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
19
|
-
context "
|
17
|
+
context "standard tracing" do
|
18
|
+
before(:each) do
|
19
|
+
@tracer = Peekaboo.configuration.tracer
|
20
|
+
@test_class = new_test_class.instance_eval { include Peekaboo }
|
21
|
+
@test_class.enable_tracing_for :singleton_methods => [:say_hello, :hello, :add, :happy?, :comma_list, :kaboom],
|
22
|
+
:instance_methods => [:say_goodbye, :goodbye, :subtract, :sad?, :pipe_list, :crash]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should guard its map of traced methods" do
|
26
|
+
lambda {
|
27
|
+
@test_class.traced_method_map[:something] = 'unwanted'
|
28
|
+
}.should raise_exception
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should store a list of traced methods" do
|
32
|
+
@test_class.traced_singleton_methods.should include :say_hello, :hello, :add, :happy?, :comma_list, :kaboom
|
33
|
+
@test_class.traced_instance_methods.should include :say_goodbye, :goodbye, :subtract, :sad?, :pipe_list, :crash
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should ensure that traced methods are uniquely stored" do
|
37
|
+
lambda {
|
38
|
+
@test_class.enable_tracing_for :singleton_methods => [:say_hello]
|
39
|
+
}.should_not change(@test_class.traced_singleton_methods, :size).from(6)
|
40
|
+
|
41
|
+
lambda {
|
42
|
+
@test_class.enable_tracing_for :singleton_methods => [:object_id]
|
43
|
+
}.should change(@test_class.traced_singleton_methods, :size).from(6).to(7)
|
44
|
+
|
45
|
+
lambda {
|
46
|
+
@test_class.enable_tracing_for :instance_methods => [:say_goodbye]
|
47
|
+
}.should_not change(@test_class.traced_instance_methods, :size).from(6)
|
48
|
+
|
49
|
+
lambda {
|
50
|
+
@test_class.enable_tracing_for :instance_methods => [:object_id]
|
51
|
+
}.should change(@test_class.traced_instance_methods, :size).from(6).to(7)
|
52
|
+
end
|
53
|
+
|
54
|
+
context "on class methods" do
|
55
|
+
it "should not take place on unlisted methods" do
|
56
|
+
@tracer.should_not_receive :info
|
57
|
+
@test_class.object_id
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should show listed methods with no arguments" do
|
61
|
+
@tracer.should_receive(:info).
|
62
|
+
with trace_message %{Invoking: #{@test_class}.say_hello with [] ==> Returning: "hello"}
|
63
|
+
@test_class.say_hello
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should show listed methods with required arguments" do
|
67
|
+
@tracer.should_receive(:info).
|
68
|
+
with trace_message %{Invoking: #{@test_class}.hello with ["developer"] ==> Returning: "hello developer"}
|
69
|
+
@test_class.hello 'developer'
|
70
|
+
|
71
|
+
@tracer.should_receive(:info).
|
72
|
+
with trace_message %{Invoking: #{@test_class}.add with [1, 2] ==> Returning: 3}
|
73
|
+
@test_class.add 1, 2
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should show methods with optional arguments" do
|
77
|
+
@tracer.should_receive(:info).
|
78
|
+
with trace_message %{Invoking: #{@test_class}.happy? with [] ==> Returning: true}
|
79
|
+
@test_class.happy?
|
80
|
+
|
81
|
+
@tracer.should_receive(:info).
|
82
|
+
with trace_message %{Invoking: #{@test_class}.happy? with [false] ==> Returning: false}
|
83
|
+
@test_class.happy? false
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should show methods with variable arguments" do
|
87
|
+
@tracer.should_receive(:info).
|
88
|
+
with trace_message %{Invoking: #{@test_class}.comma_list with [] ==> Returning: ""}
|
89
|
+
@test_class.comma_list
|
90
|
+
|
91
|
+
@tracer.should_receive(:info).
|
92
|
+
with trace_message %{Invoking: #{@test_class}.comma_list with [:too, "cool"] ==> Returning: "too,cool"}
|
93
|
+
@test_class.comma_list :too, 'cool'
|
94
|
+
|
95
|
+
@tracer.should_receive(:info).
|
96
|
+
with trace_message %{Invoking: #{@test_class}.comma_list with [1, "to", 5] ==> Returning: "1,to,5"}
|
97
|
+
@test_class.comma_list 1, 'to', 5
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should show methods that raise an exception" do
|
101
|
+
lambda do
|
102
|
+
@tracer.should_receive(:info).
|
103
|
+
with trace_message %{Invoking: #{@test_class}.kaboom with [] !!! Raising: "fire, fire"}
|
104
|
+
@test_class.kaboom
|
105
|
+
end.should raise_exception
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should work when methods are added after the fact" do
|
109
|
+
@test_class.enable_tracing_for :singleton_methods => [:dog]
|
110
|
+
def @test_class.dog
|
111
|
+
'woof'
|
112
|
+
end
|
113
|
+
|
114
|
+
@tracer.should_receive(:info).
|
115
|
+
with trace_message %{Invoking: #{@test_class}.dog with [] ==> Returning: "woof"}
|
116
|
+
@test_class.dog
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "on instance methods" do
|
121
|
+
before(:each) do
|
122
|
+
@test_instance = @test_class.new
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should not take place on unlisted methods" do
|
126
|
+
@tracer.should_not_receive :info
|
127
|
+
@test_instance.object_id
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should show listed methods with no arguments" do
|
131
|
+
@tracer.should_receive(:info).
|
132
|
+
with trace_message %{Invoking: #{@test_class}#say_goodbye with [] ==> Returning: "goodbye"}
|
133
|
+
@test_instance.say_goodbye
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should show listed methods with required arguments" do
|
137
|
+
@tracer.should_receive(:info).
|
138
|
+
with trace_message %{Invoking: #{@test_class}#goodbye with ["bugs"] ==> Returning: "goodbye bugs"}
|
139
|
+
@test_instance.goodbye 'bugs'
|
140
|
+
|
141
|
+
@tracer.should_receive(:info).
|
142
|
+
with trace_message %{Invoking: #{@test_class}#subtract with [5, 4] ==> Returning: 1}
|
143
|
+
@test_instance.subtract 5, 4
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should show methods with optional arguments" do
|
147
|
+
@tracer.should_receive(:info).
|
148
|
+
with trace_message %{Invoking: #{@test_class}#sad? with [] ==> Returning: false}
|
149
|
+
@test_instance.sad?
|
150
|
+
|
151
|
+
@tracer.should_receive(:info).
|
152
|
+
with trace_message %{Invoking: #{@test_class}#sad? with [true] ==> Returning: true}
|
153
|
+
@test_instance.sad? true
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should show methods with variable arguments" do
|
157
|
+
@tracer.should_receive(:info).
|
158
|
+
with trace_message %{Invoking: #{@test_class}#pipe_list with [] ==> Returning: ""}
|
159
|
+
@test_instance.pipe_list
|
160
|
+
|
161
|
+
@tracer.should_receive(:info).
|
162
|
+
with trace_message %{Invoking: #{@test_class}#pipe_list with [:alf, "is"] ==> Returning: "alf|is"}
|
163
|
+
@test_instance.pipe_list :alf, "is"
|
164
|
+
|
165
|
+
@tracer.should_receive(:info).
|
166
|
+
with trace_message %{Invoking: #{@test_class}#pipe_list with [:alf, "is", 0] ==> Returning: "alf|is|0"}
|
167
|
+
@test_instance.pipe_list :alf, "is", 0
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should show methods that raise an exception" do
|
171
|
+
lambda do
|
172
|
+
@tracer.should_receive(:info).
|
173
|
+
with trace_message %{Invoking: #{@test_class}#crash with [] !!! Raising: "twisted code"}
|
174
|
+
@test_instance.crash
|
175
|
+
end.should raise_exception
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should work when methods are added after the fact" do
|
179
|
+
@test_class.enable_tracing_for :instance_methods => [:frog]
|
180
|
+
@test_class.class_eval do
|
181
|
+
def frog
|
182
|
+
'ribbit'
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
@tracer.should_receive(:info).
|
187
|
+
with trace_message %{Invoking: #{@test_class}#frog with [] ==> Returning: "ribbit"}
|
188
|
+
@test_instance.frog
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "autoinclusion tracing" do
|
194
|
+
before(:each) do
|
195
|
+
@tracer = Peekaboo.configuration.tracer
|
196
|
+
@base_class = new_test_class
|
197
|
+
Peekaboo.configure { |config| config.autoinclude_with @base_class }
|
198
|
+
end
|
199
|
+
|
200
|
+
context "on class methods" do
|
201
|
+
it "should inject functionality into an auto-included class" do
|
202
|
+
@base_class.enable_tracing_for :singleton_methods => [:say_hello]
|
203
|
+
|
204
|
+
@tracer.should_receive(:info).
|
205
|
+
with trace_message %{Invoking: #{@base_class}.say_hello with [] ==> Returning: "hello"}
|
206
|
+
@base_class.say_hello
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should inject functionality into any class that inherits from an auto-included class" do
|
210
|
+
child_class = Class.new(@base_class) { def self.say_hola; 'hola'; end }
|
211
|
+
child_class.enable_tracing_for :singleton_methods => [:say_hola]
|
212
|
+
|
213
|
+
@tracer.should_receive(:info).
|
214
|
+
with trace_message %{Invoking: #{child_class}.say_hola with [] ==> Returning: "hola"}
|
215
|
+
child_class.say_hola
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should not inject functionality into classes that are not auto-included" do
|
219
|
+
not_a_child_class = new_test_class
|
220
|
+
lambda {
|
221
|
+
not_a_child_class.enable_tracing_for :singleton_methods => [:say_hello]
|
222
|
+
}.should raise_exception NoMethodError
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should maintain unique tracing method lists across an inheritance chain" do
|
226
|
+
child_class = Class.new(@base_class) { def self.say_hola; 'hola'; end }
|
227
|
+
@base_class.enable_tracing_for :singleton_methods => [:say_hello]
|
228
|
+
child_class.enable_tracing_for :singleton_methods => [:say_hola]
|
229
|
+
|
230
|
+
@base_class.traced_singleton_methods.to_a.should =~ [:say_hello]
|
231
|
+
child_class.traced_singleton_methods.to_a.should =~ [:say_hola]
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context "on instance methods" do
|
236
|
+
it "should inject functionality into an auto-included class" do
|
237
|
+
@base_class.enable_tracing_for :instance_methods => [:say_goodbye]
|
238
|
+
|
239
|
+
@tracer.should_receive(:info).
|
240
|
+
with trace_message %{Invoking: #{@base_class}#say_goodbye with [] ==> Returning: "goodbye"}
|
241
|
+
@base_class.new.say_goodbye
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should inject functionality into any class that inherits from an auto-included class" do
|
245
|
+
child_class = Class.new(@base_class) { def say_adios; 'adios'; end }
|
246
|
+
child_class.enable_tracing_for :instance_methods => [:say_adios]
|
247
|
+
|
248
|
+
@tracer.should_receive(:info).
|
249
|
+
with trace_message %{Invoking: #{child_class}#say_adios with [] ==> Returning: "adios"}
|
250
|
+
child_class.new.say_adios
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should not inject functionality into classes that are not auto-included" do
|
254
|
+
not_a_child_class = new_test_class
|
255
|
+
lambda {
|
256
|
+
not_a_child_class.enable_tracing_for :instance_methods => [:say_goodbye]
|
257
|
+
}.should raise_exception NoMethodError
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should maintain unique tracing method lists across an inheritance chain" do
|
261
|
+
child_class = Class.new(@base_class) { def say_adios; 'adios'; end }
|
262
|
+
@base_class.enable_tracing_for :instance_methods => [:say_goodbye]
|
263
|
+
child_class.enable_tracing_for :instance_methods => [:say_adios]
|
264
|
+
|
265
|
+
@base_class.traced_instance_methods.to_a.should =~ [:say_goodbye]
|
266
|
+
child_class.traced_instance_methods.to_a.should =~ [:say_adios]
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
### OLD SPECIFICATIONS: Remove when moving to version 0.4.0 ###
|
273
|
+
|
274
|
+
|
275
|
+
context ".enable_tracing_on (old)" do
|
20
276
|
before(:each) do
|
21
277
|
@test_class = new_test_class
|
22
278
|
@test_class.instance_eval { include Peekaboo }
|
@@ -29,7 +285,7 @@ describe Peekaboo do
|
|
29
285
|
it "should store a list of methods to trace on any including class" do
|
30
286
|
methods_to_trace = [:method_no_args, :method_one_arg]
|
31
287
|
@test_class.enable_tracing_on *methods_to_trace
|
32
|
-
@test_class
|
288
|
+
@test_class.peek_list.should =~ methods_to_trace
|
33
289
|
end
|
34
290
|
|
35
291
|
it "should raise an exception when trying to add a method that is already being traced" do
|
@@ -40,7 +296,7 @@ describe Peekaboo do
|
|
40
296
|
end
|
41
297
|
end
|
42
298
|
|
43
|
-
context "instance method tracing" do
|
299
|
+
context "instance method tracing (old)" do
|
44
300
|
before(:all) do
|
45
301
|
@test_class = new_test_class
|
46
302
|
@test_class.instance_eval do
|
@@ -105,7 +361,7 @@ describe Peekaboo do
|
|
105
361
|
end
|
106
362
|
end
|
107
363
|
|
108
|
-
context "autoinclusion tracing" do
|
364
|
+
context "autoinclusion tracing (old)" do
|
109
365
|
before(:all) do
|
110
366
|
@tracer = Peekaboo.configuration.tracer
|
111
367
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,27 +11,55 @@ require 'peekaboo'
|
|
11
11
|
Spec::Runner.configure do |config|
|
12
12
|
def new_test_class
|
13
13
|
Class.new do
|
14
|
-
|
14
|
+
class << self
|
15
|
+
def say_hello
|
16
|
+
'hello'
|
17
|
+
end
|
18
|
+
def hello name
|
19
|
+
"hello #{name}"
|
20
|
+
end
|
21
|
+
def add a, b
|
22
|
+
a + b
|
23
|
+
end
|
24
|
+
def happy? option = true
|
25
|
+
option
|
26
|
+
end
|
27
|
+
def comma_list *args
|
28
|
+
args.join ','
|
29
|
+
end
|
30
|
+
def kaboom
|
31
|
+
raise 'fire, fire'
|
32
|
+
end
|
15
33
|
end
|
16
|
-
|
17
|
-
def
|
34
|
+
|
35
|
+
def say_goodbye
|
36
|
+
'goodbye'
|
18
37
|
end
|
19
|
-
|
20
|
-
|
38
|
+
def goodbye name
|
39
|
+
"goodbye #{name}"
|
21
40
|
end
|
22
|
-
|
23
|
-
|
41
|
+
def subtract a, b
|
42
|
+
a - b
|
24
43
|
end
|
25
|
-
|
26
|
-
|
44
|
+
def sad? option = false
|
45
|
+
option
|
27
46
|
end
|
28
|
-
|
29
|
-
|
47
|
+
def pipe_list *args
|
48
|
+
args.join '|'
|
30
49
|
end
|
31
|
-
|
32
|
-
|
33
|
-
raise 'something went wrong'
|
50
|
+
def crash
|
51
|
+
raise 'twisted code'
|
34
52
|
end
|
53
|
+
|
54
|
+
### OLD TEST METHODS ###
|
55
|
+
|
56
|
+
def method_no_tracing;end
|
57
|
+
def method_no_args;end
|
58
|
+
def method_one_arg arg1;end
|
59
|
+
def method_two_args arg1, arg2;end
|
60
|
+
def method_optional_args optional = 'default';end
|
61
|
+
def method_variable_args *args;end
|
62
|
+
def method_raises;raise 'something went wrong';end
|
35
63
|
end
|
36
64
|
end
|
37
65
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Sonny Ruben Garcia
|
@@ -14,11 +14,11 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-11-
|
17
|
+
date: 2010-11-08 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: bundler
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -27,56 +27,29 @@ dependencies:
|
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
segments:
|
29
29
|
- 1
|
30
|
-
- 3
|
31
30
|
- 0
|
32
|
-
|
31
|
+
- 3
|
32
|
+
version: 1.0.3
|
33
33
|
type: :development
|
34
34
|
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: rcov
|
37
|
-
prerelease: false
|
38
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - "="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
- 9
|
46
|
-
- 9
|
47
|
-
version: 0.9.9
|
48
|
-
type: :development
|
49
|
-
version_requirements: *id002
|
50
|
-
- !ruby/object:Gem::Dependency
|
51
|
-
name: yard
|
52
|
-
prerelease: false
|
53
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
|
-
requirements:
|
56
|
-
- - "="
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
segments:
|
59
|
-
- 0
|
60
|
-
- 6
|
61
|
-
- 1
|
62
|
-
version: 0.6.1
|
63
|
-
type: :development
|
64
|
-
version_requirements: *id003
|
65
35
|
description: Allows you to log method call information ( input arguments, class/method name, and return value ) without being overly intrusive.
|
66
36
|
email: sonny.ruben@gmail.com
|
67
37
|
executables: []
|
68
38
|
|
69
39
|
extensions: []
|
70
40
|
|
71
|
-
extra_rdoc_files:
|
72
|
-
|
41
|
+
extra_rdoc_files:
|
42
|
+
- CHANGELOG.md
|
73
43
|
files:
|
74
44
|
- .gitignore
|
45
|
+
- .yardopts
|
75
46
|
- CHANGELOG.md
|
47
|
+
- Gemfile
|
76
48
|
- README.md
|
77
49
|
- Rakefile
|
78
50
|
- lib/peekaboo.rb
|
79
51
|
- lib/peekaboo/configuration.rb
|
52
|
+
- lib/peekaboo/singleton_methods.rb
|
80
53
|
- lib/peekaboo/version.rb
|
81
54
|
- peekaboo.gemspec
|
82
55
|
- spec/peekaboo/configuration_spec.rb
|