peekaboo 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|