cheap_advice 1.0.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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +70 -0
- data/Rakefile +79 -0
- data/VERSION +1 -0
- data/cheap_advice.slides.textile +257 -0
- data/example/ex01.rb +51 -0
- data/example/ex02-trace-0.yml +17 -0
- data/example/ex02-trace-1.yml +17 -0
- data/example/ex02-trace-2.yml +23 -0
- data/example/ex02-trace-4.yml +20 -0
- data/example/ex02-trace-5.yml +22 -0
- data/example/ex02-trace-6.yml +25 -0
- data/example/ex02.rb +99 -0
- data/lib/cheap_advice.rb +511 -0
- data/lib/cheap_advice/configuration.rb +217 -0
- data/lib/cheap_advice/trace.rb +228 -0
- data/spec/cheap_advice_spec.rb +342 -0
- data/spec/spec_helper.rb +12 -0
- metadata +192 -0
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'cheap_advice'
|
2
|
+
|
3
|
+
class CheapAdvice
|
4
|
+
#
|
5
|
+
class Configuration
|
6
|
+
class Error < ::CheapAdvice::Error; end
|
7
|
+
|
8
|
+
# Configuration input hash.
|
9
|
+
attr_accessor :config
|
10
|
+
|
11
|
+
# Hash mapping advice names to CheapAdvice objects.
|
12
|
+
attr_accessor :advice
|
13
|
+
|
14
|
+
# Array of CheapAdvice::Advised methods.
|
15
|
+
attr_accessor :advised
|
16
|
+
|
17
|
+
# Hash of file names that were explicity required before applying advice.
|
18
|
+
attr_accessor :required
|
19
|
+
|
20
|
+
# Flag
|
21
|
+
attr_accessor :config_changed
|
22
|
+
alias :config_changed? :config_changed
|
23
|
+
|
24
|
+
attr_accessor :verbose
|
25
|
+
|
26
|
+
def initialize opts = nil
|
27
|
+
opts ||= EMPTY_Hash
|
28
|
+
@verbose = false
|
29
|
+
opts.each do | k, v |
|
30
|
+
send(:"#{k}=", v)
|
31
|
+
end
|
32
|
+
@advice ||= { }
|
33
|
+
@targets = [ ]
|
34
|
+
@advised = [ ]
|
35
|
+
@required = { }
|
36
|
+
end
|
37
|
+
|
38
|
+
def config_changed!
|
39
|
+
@config_changed = true
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def configure_if_changed!
|
44
|
+
if config_changed?
|
45
|
+
configure!
|
46
|
+
@config_changed = false
|
47
|
+
end
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure!
|
52
|
+
disable!
|
53
|
+
|
54
|
+
# First pass: parse target and defaults.
|
55
|
+
c = [ ]
|
56
|
+
d = { }
|
57
|
+
get_config.each do | target_name, target_config |
|
58
|
+
annotate_error "target=#{target_name}" do
|
59
|
+
t = parse_target(target_name)
|
60
|
+
# _log { "#{target_name.inspect} => #{t.inspect}" }
|
61
|
+
case target_config
|
62
|
+
when true, false
|
63
|
+
target_config = { :enabled => target_config }
|
64
|
+
end
|
65
|
+
t.update(target_config) if target_config
|
66
|
+
[ :advice, :require ].each do | k |
|
67
|
+
t[k] = as_array(t[k]) if t.key?(k)
|
68
|
+
end
|
69
|
+
case
|
70
|
+
when t[:meth].nil? && t[:mod].nil? # global default.
|
71
|
+
d[nil] = t
|
72
|
+
when t[:meth].nil? # module default.
|
73
|
+
d[t[:mod]] = t
|
74
|
+
else
|
75
|
+
c << t # real target
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
d[nil] ||= { }
|
80
|
+
|
81
|
+
# Second pass: merge defaults with target.
|
82
|
+
@targets = [ ]
|
83
|
+
c.each do | t |
|
84
|
+
x = merge!(d[nil].dup, d[t[:mod]] || EMPTY_Hash)
|
85
|
+
t = merge!(x, t)
|
86
|
+
# _log { "target = #{t.inspect}" }
|
87
|
+
next if t[:enabled] == false
|
88
|
+
@targets << t
|
89
|
+
end
|
90
|
+
|
91
|
+
enable!
|
92
|
+
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
def disable!
|
97
|
+
@targets.each do | t |
|
98
|
+
(t[:advice] || EMPTY_Array).each do | advice_name |
|
99
|
+
advice_name = advice_name.to_sym
|
100
|
+
if advised = (t[:advised] || EMPTY_Hash)[advice_name]
|
101
|
+
advised.disable!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@advised.clear
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def enable!
|
110
|
+
@advised.clear
|
111
|
+
@targets.each do | t |
|
112
|
+
t_str = target_as_string(t)
|
113
|
+
annotate_error "target=#{t_str.inspect}" do
|
114
|
+
(t[:require] || EMPTY_Array).each do | r |
|
115
|
+
_log { "#{t_str}: require #{r}" }
|
116
|
+
unless @required[r]
|
117
|
+
require r
|
118
|
+
@required[r] = true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
(t[:advice] || EMPTY_Array).each do | advice_name |
|
124
|
+
advice_name = advice_name.to_sym
|
125
|
+
annotate_error "target=#{target_as_string(t)} advice=#{advice_name.inspect}" do
|
126
|
+
unless advice = @advice[advice_name]
|
127
|
+
raise Error, "no advice by that name"
|
128
|
+
end
|
129
|
+
options = t[:options][nil]
|
130
|
+
options = merge!(options, t[:options][advice_name])
|
131
|
+
# _log { "#{t.inspect} options => #{options.inspect}" }
|
132
|
+
|
133
|
+
advised = advice.advise!(t[:mod], t[:meth], t[:kind], options)
|
134
|
+
|
135
|
+
(t[:advised] ||= { })[advice_name] = advised
|
136
|
+
|
137
|
+
@advised << advised
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
def _log msg = nil
|
145
|
+
return self unless @verbose
|
146
|
+
msg ||= yield if block_given?
|
147
|
+
$stderr.puts "#{self.class}: #{msg}"
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
def annotate_error x
|
152
|
+
yield
|
153
|
+
rescue Exception => err
|
154
|
+
msg = "in #{x.inspect}: #{err.inspect}"
|
155
|
+
_log { "ERROR: #{msg}\n #{err.backtrace * "\n "}" }
|
156
|
+
raise Error, msg, err.backtrace
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_config
|
160
|
+
case @config
|
161
|
+
when Hash
|
162
|
+
@config
|
163
|
+
when Proc
|
164
|
+
@config.call(self)
|
165
|
+
when nil
|
166
|
+
raise Error, "no config"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def as_array x
|
171
|
+
x = EMPTY_Array if x == nil
|
172
|
+
x = x.split(/\s+|\s*,\s*/) if String === x
|
173
|
+
raise "Unexpected Hash" if Hash === x
|
174
|
+
x
|
175
|
+
end
|
176
|
+
|
177
|
+
def parse_target x
|
178
|
+
case x
|
179
|
+
when nil
|
180
|
+
{ }
|
181
|
+
when Hash
|
182
|
+
x
|
183
|
+
when String, Symbol
|
184
|
+
if x.to_s =~ /\A([a-z0-9_:]+)(?:([#\.])([a-z0-9_]+[=\!\?]?))?\Z/i
|
185
|
+
{ :mod => $1,
|
186
|
+
:kind => $2 && ($2 == '.' ? :module : :instance),
|
187
|
+
:meth => $3,
|
188
|
+
}
|
189
|
+
else
|
190
|
+
raise Error, "cannot parse #{x.inspect}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
def target_as_string t
|
195
|
+
"#{t[:mod]}#{t[:kind] == :instance ? '#' : '.'}#{t[:meth]}"
|
196
|
+
end
|
197
|
+
|
198
|
+
def merge! dst, src
|
199
|
+
case dst
|
200
|
+
when nil, Hash
|
201
|
+
case src
|
202
|
+
when Hash
|
203
|
+
dst = dst ? dst.dup : { }
|
204
|
+
src.each do | k, v |
|
205
|
+
dst[k] = merge!(dst[k], v)
|
206
|
+
end
|
207
|
+
else
|
208
|
+
dst = src
|
209
|
+
end
|
210
|
+
else
|
211
|
+
dst = src
|
212
|
+
end
|
213
|
+
dst
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'cheap_advice'
|
2
|
+
|
3
|
+
require 'time' # Time#iso8601
|
4
|
+
|
5
|
+
class CheapAdvice
|
6
|
+
# Sample Tracing Advice factory.
|
7
|
+
module Trace
|
8
|
+
def self.new opts = nil
|
9
|
+
opts ||= { }
|
10
|
+
trace = CheapAdvice.new(:around, opts) do | ar, body |
|
11
|
+
a = ar.advice
|
12
|
+
ad = ar.advised
|
13
|
+
logger = ad.logger[:name] || ad.logger_default[:name]
|
14
|
+
logger = a.logger[logger] || a.logger_default[logger]
|
15
|
+
|
16
|
+
formatter = nil
|
17
|
+
if ad[:log_before] != false
|
18
|
+
a.log(logger) do
|
19
|
+
formatter = a.new_formatter(logger)
|
20
|
+
ar[:time_before] = Time.now
|
21
|
+
formatter.record(ar, :before)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
body.call
|
26
|
+
|
27
|
+
if ad[:log_after] != false
|
28
|
+
a.log(logger) do
|
29
|
+
formatter ||= a.new_formatter(logger)
|
30
|
+
ar[:time_after] = Time.now
|
31
|
+
if ar.error
|
32
|
+
ar[:error] = ar.error if ad[:log_error] != false
|
33
|
+
else
|
34
|
+
ar[:result] = ar.result if ad[:log_result] != false
|
35
|
+
end
|
36
|
+
formatter.record(ar, :after)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
trace.extend(Behavior)
|
41
|
+
trace.advised_extend = Behavior
|
42
|
+
trace
|
43
|
+
end
|
44
|
+
|
45
|
+
module Behavior
|
46
|
+
def logger
|
47
|
+
# $stderr.puts " #{self.class} @options = #{@options.inspect}"
|
48
|
+
@options[:logger] ||= { }
|
49
|
+
end
|
50
|
+
def logger_default
|
51
|
+
logger[nil] ||= { }
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_formatter logger
|
55
|
+
formatter(logger).new(logger, *formatter_options(logger))
|
56
|
+
end
|
57
|
+
|
58
|
+
def formatter logger
|
59
|
+
logger[:formatter] ||=
|
60
|
+
logger_default[:formatter] ||=
|
61
|
+
DefaultFormatter
|
62
|
+
end
|
63
|
+
|
64
|
+
def formatter_options logger
|
65
|
+
logger[:formatter_options] ||=
|
66
|
+
logger_default[:formatter_options] ||=
|
67
|
+
[ ]
|
68
|
+
end
|
69
|
+
|
70
|
+
def log_prefix logger, ar
|
71
|
+
pre =
|
72
|
+
logger[:log_prefix] ||=
|
73
|
+
logger_default[:log_prefix] ||=
|
74
|
+
EMPTY_String
|
75
|
+
case pre
|
76
|
+
when Proc
|
77
|
+
pre.call(ar)
|
78
|
+
else
|
79
|
+
pre
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def log logger, msg = nil
|
84
|
+
return msg unless logger
|
85
|
+
msg ||= yield if block_given?
|
86
|
+
return msg if msg.nil?
|
87
|
+
dst = logger[:target]
|
88
|
+
case dst
|
89
|
+
when nil
|
90
|
+
nil
|
91
|
+
when IO
|
92
|
+
dst.seek(0, IO::SEEK_END)
|
93
|
+
dst.puts msg.to_s
|
94
|
+
dst.flush
|
95
|
+
when Proc
|
96
|
+
dst.call(msg)
|
97
|
+
else
|
98
|
+
dst.send(logger[:method] || :debug, msg)
|
99
|
+
end
|
100
|
+
msg
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_all msg = nil
|
104
|
+
logger.values.each do | dst |
|
105
|
+
log(dst) { msg ||= yield if block_given?; msg }
|
106
|
+
end
|
107
|
+
msg
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
class BaseFormatter
|
113
|
+
attr_reader :logger
|
114
|
+
|
115
|
+
def initialize logger, *args
|
116
|
+
@logger = logger
|
117
|
+
end
|
118
|
+
|
119
|
+
def format obj, mode
|
120
|
+
case mode
|
121
|
+
when :rcvr
|
122
|
+
obj && obj.to_s
|
123
|
+
when :module
|
124
|
+
obj && obj.name
|
125
|
+
when :time
|
126
|
+
obj && obj.iso8601(6)
|
127
|
+
when :error
|
128
|
+
obj.inspect
|
129
|
+
when :result
|
130
|
+
obj.inspect
|
131
|
+
when :method
|
132
|
+
ad = ar.meth_to_s
|
133
|
+
else
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class DefaultFormatter < BaseFormatter
|
140
|
+
def format obj, mode
|
141
|
+
case mode
|
142
|
+
when :error
|
143
|
+
return "ERROR #{obj.inspect}"
|
144
|
+
when :result
|
145
|
+
return "=> #{obj.inspect}"
|
146
|
+
when :time
|
147
|
+
return super
|
148
|
+
else
|
149
|
+
obj = super || obj
|
150
|
+
end
|
151
|
+
|
152
|
+
obj = obj.inspect
|
153
|
+
if mode == :args
|
154
|
+
obj = obj.to_s.gsub(/\A\[|\]\Z/, '')
|
155
|
+
end
|
156
|
+
obj
|
157
|
+
end
|
158
|
+
|
159
|
+
# Formats the ActivationRecord for the logger.
|
160
|
+
def record ar, mode
|
161
|
+
ad = ar.advised
|
162
|
+
msg = nil
|
163
|
+
|
164
|
+
case mode
|
165
|
+
when :before, :after
|
166
|
+
msg = ad.log_prefix(logger, ar).to_s
|
167
|
+
msg = msg.dup if msg.frozen?
|
168
|
+
ar[:args] ||= format(ar.args, :args) if ad[:log_args] != false
|
169
|
+
ar[:meth] ||= "#{ad.meth_to_s} #{ar.rcvr.class}"
|
170
|
+
msg << "#{format(ar[:"time_#{mode}"], :time)} #{ar[:meth]}"
|
171
|
+
msg << " #{format(ar.rcvr, :rcvr)}" if ad[:log_rcvr]
|
172
|
+
msg << " ( #{ar[:args]} )" if ar[:args]
|
173
|
+
end
|
174
|
+
|
175
|
+
case mode
|
176
|
+
when :before
|
177
|
+
msg << " {"
|
178
|
+
when :after
|
179
|
+
msg << " }"
|
180
|
+
if ar.error
|
181
|
+
msg << " #{format(ar[:error], :error )}" if ad[:log_error] != false
|
182
|
+
else
|
183
|
+
msg << " #{format(ar[:result], :result)}" if ad[:log_result] != false
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
msg
|
188
|
+
end
|
189
|
+
end # class
|
190
|
+
|
191
|
+
class YamlFormatter < BaseFormatter
|
192
|
+
def to_hash ar, mode
|
193
|
+
ad = ar.advised
|
194
|
+
|
195
|
+
data = (ar.advice[:log_data] || EMPTY_Hash).dup
|
196
|
+
data.update(ar.advised[:log_data] || EMPTY_Hash)
|
197
|
+
# pp [ :'ar.data=', ar.data ]
|
198
|
+
data.update(ar.data)
|
199
|
+
# pp [ :'data=', data ]
|
200
|
+
if x = ad.log_prefix(logger, ar)
|
201
|
+
data[:log_prefix] = x
|
202
|
+
end
|
203
|
+
data[:meth] = ar.meth
|
204
|
+
data[:mod] = Module === (x = ar.mod) ? x.name : x
|
205
|
+
data[:kind] = ar.kind
|
206
|
+
data[:signature] = ar.meth_to_s
|
207
|
+
data[:rcvr] = format(ar.rcvr, :rcvr) if ad[:log_rcvr]
|
208
|
+
data[:rcvr_class] = ar.rcvr.class.name
|
209
|
+
if x = data[:time_after] &&
|
210
|
+
data[:time_before] &&
|
211
|
+
(data[:time_after].to_f - data[:time_before].to_f)
|
212
|
+
data[:time_elapsed] = x
|
213
|
+
end
|
214
|
+
data
|
215
|
+
end
|
216
|
+
|
217
|
+
def record ar, mode
|
218
|
+
case mode
|
219
|
+
when :after
|
220
|
+
data = to_hash(ar, mode)
|
221
|
+
YAML.dump(data)
|
222
|
+
else
|
223
|
+
nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class CheapAdvice
|
4
|
+
module Test
|
5
|
+
|
6
|
+
module M
|
7
|
+
attr_accessor :_m
|
8
|
+
def m(arg)
|
9
|
+
@_m = 1 + arg
|
10
|
+
end
|
11
|
+
def self.mm(arg)
|
12
|
+
3 + arg
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Foo
|
17
|
+
include M
|
18
|
+
attr_accessor :foo, :bar
|
19
|
+
attr_reader :_baz, :_bar
|
20
|
+
(class << self; self; end).instance_eval do
|
21
|
+
attr_accessor :_baz
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.baz(arg)
|
25
|
+
self._baz = 3 + arg
|
26
|
+
end
|
27
|
+
|
28
|
+
def baz(arg)
|
29
|
+
@_baz = 5 + arg
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_it(arg)
|
33
|
+
yield(arg + 7) + 2
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class Bar
|
39
|
+
include M
|
40
|
+
attr_accessor :bar
|
41
|
+
attr_reader :_baz
|
42
|
+
|
43
|
+
def baz(arg)
|
44
|
+
@_baz = 7 + arg
|
45
|
+
end
|
46
|
+
|
47
|
+
def calls_private_method(arg)
|
48
|
+
private_method(arg)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def private_method(arg)
|
53
|
+
arg
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
def protected_method(arg)
|
58
|
+
arg
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Baz < Bar
|
63
|
+
def calls_protected_method(arg)
|
64
|
+
protected_method(arg)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
describe "CheapAdvice" do
|
73
|
+
attr_reader :tracing_advice
|
74
|
+
attr_reader :f, :b
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
@tracing_advice = CheapAdvice.new(:around) do | ar, body |
|
78
|
+
ar.advice.log " TRACE: before #{ar.rcvr.class}\##{ar.meth}(#{ar.args.join(", ")})"
|
79
|
+
ar.advice.log " foo = #{@foo.inspect}"
|
80
|
+
ar.advice.log " bar = #{@bar.inspect}"
|
81
|
+
result = body.call
|
82
|
+
ar.advice.log " TRACE: after #{ar.rcvr.class}\##{ar.meth}(#{ar.args.join(", ")}) => #{result.inspect}"
|
83
|
+
ar.result = "yo!"
|
84
|
+
ar.advice.log " TRACE: return #{ar.result.inspect}"
|
85
|
+
"oy!" # Not relevant.
|
86
|
+
end
|
87
|
+
@tracing_advice.instance_eval do
|
88
|
+
def log msg = nil
|
89
|
+
return @log unless msg
|
90
|
+
(@log ||= [ ]) << msg.dup
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "handles simple tracing_advice example." do
|
96
|
+
@f = CheapAdvice::Test::Foo.new
|
97
|
+
@b = CheapAdvice::Test::Bar.new
|
98
|
+
|
99
|
+
assert_without_advice
|
100
|
+
tracing_advice.log.should == nil
|
101
|
+
|
102
|
+
tracing_advice.advise!( [CheapAdvice::Test::Foo, CheapAdvice::Test::Bar], [ :bar, :bar=, :baz ])
|
103
|
+
f.foo = 10
|
104
|
+
f.foo.should == 10
|
105
|
+
f.baz(10).should == "yo!"
|
106
|
+
b.bar = 101
|
107
|
+
b.bar.should == "yo!"
|
108
|
+
b.baz(10).should == "yo!"
|
109
|
+
tracing_advice.log.should_not == nil
|
110
|
+
tracing_advice.log.size.should == 20
|
111
|
+
|
112
|
+
tracing_advice.unadvise!
|
113
|
+
assert_without_advice
|
114
|
+
end
|
115
|
+
|
116
|
+
def assert_without_advice
|
117
|
+
f.foo = 10
|
118
|
+
f.foo.should == 10
|
119
|
+
f.baz(10).should == 15
|
120
|
+
f._baz.should == 15
|
121
|
+
|
122
|
+
b.bar = 101
|
123
|
+
b.bar.should == 101
|
124
|
+
b.baz(10).should == 17
|
125
|
+
b._baz.should == 17
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'handles methods with blocks.' do
|
129
|
+
ars = [ ]
|
130
|
+
basic_advice = CheapAdvice.new(:before) do | ar |
|
131
|
+
ars << ar
|
132
|
+
end
|
133
|
+
basic_advice.advised.size.should == 0
|
134
|
+
|
135
|
+
@f = CheapAdvice::Test::Foo.new
|
136
|
+
|
137
|
+
assert_do_it f
|
138
|
+
ars.size.should == 0
|
139
|
+
|
140
|
+
basic_advice.advise! CheapAdvice::Test::Foo, 'do_it'
|
141
|
+
|
142
|
+
basic_advice.advised.size.should == 1
|
143
|
+
advised = basic_advice.advised.first
|
144
|
+
advised.mod.should == CheapAdvice::Test::Foo
|
145
|
+
advised.meth.should == :do_it
|
146
|
+
advised.enabled.should == true
|
147
|
+
|
148
|
+
assert_do_it f
|
149
|
+
ars.size.should == 1
|
150
|
+
|
151
|
+
advised.unadvise!
|
152
|
+
advised.enabled.should == false
|
153
|
+
|
154
|
+
assert_do_it f
|
155
|
+
ars.size.should == 1
|
156
|
+
end
|
157
|
+
|
158
|
+
def assert_do_it f
|
159
|
+
arg = nil
|
160
|
+
result = f.do_it(10) do | _arg |
|
161
|
+
_arg.should == 17
|
162
|
+
arg = _arg
|
163
|
+
end
|
164
|
+
arg.should == 17
|
165
|
+
result.should == 19
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'handles applying the same advice only once.' do
|
169
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
170
|
+
end
|
171
|
+
null_advice.advised.size.should == 0
|
172
|
+
|
173
|
+
advised = null_advice.advise! CheapAdvice::Test::Foo, :do_it
|
174
|
+
null_advice.advised.size.should == 1
|
175
|
+
|
176
|
+
advised_again = null_advice.advise! CheapAdvice::Test::Foo, :do_it
|
177
|
+
advised_again.object_id.should == advised.object_id
|
178
|
+
|
179
|
+
null_advice.advised.size.should == 1
|
180
|
+
|
181
|
+
advised = null_advice.advise! CheapAdvice::Test::Foo, :baz, :class
|
182
|
+
null_advice.advised.size.should == 2
|
183
|
+
|
184
|
+
advised_again = null_advice.advise! CheapAdvice::Test::Foo, :baz, :class
|
185
|
+
advised_again.object_id.should == advised.object_id
|
186
|
+
|
187
|
+
null_advice.advised.size.should == 2
|
188
|
+
|
189
|
+
advised.unadvise!
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'handles String for class and method names.' do
|
193
|
+
advice_called = 0
|
194
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
195
|
+
advice_called += 1
|
196
|
+
end
|
197
|
+
null_advice.advised.size.should == 0
|
198
|
+
|
199
|
+
|
200
|
+
@f = CheapAdvice::Test::Foo.new
|
201
|
+
|
202
|
+
advice_called.should == 0
|
203
|
+
|
204
|
+
advised = null_advice.advise!('CheapAdvice::Test::Foo', 'baz')
|
205
|
+
null_advice.advised.size.should == 1
|
206
|
+
|
207
|
+
@f.baz(5).should == 10
|
208
|
+
@f._baz.should == 10
|
209
|
+
advice_called.should == 1
|
210
|
+
|
211
|
+
advised.unadvise!
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'handles Module instance method advice.' do
|
215
|
+
advice_called = 0
|
216
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
217
|
+
advice_called += 1
|
218
|
+
end
|
219
|
+
null_advice.advised.size.should == 0
|
220
|
+
|
221
|
+
|
222
|
+
@f = CheapAdvice::Test::Foo.new
|
223
|
+
@b = CheapAdvice::Test::Bar.new
|
224
|
+
|
225
|
+
advice_called.should == 0
|
226
|
+
|
227
|
+
advised = null_advice.advise!('CheapAdvice::Test::M', 'm')
|
228
|
+
null_advice.advised.size.should == 1
|
229
|
+
|
230
|
+
@f.m(5).should == 6
|
231
|
+
@f._m.should == 6
|
232
|
+
|
233
|
+
@b.m(5).should == 6
|
234
|
+
@b._m.should == 6
|
235
|
+
|
236
|
+
advice_called.should == 2
|
237
|
+
|
238
|
+
advised.unadvise!
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'handles Module singleton method advice.' do
|
242
|
+
advice_called = 0
|
243
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
244
|
+
advice_called += 1
|
245
|
+
end
|
246
|
+
null_advice.advised.size.should == 0
|
247
|
+
|
248
|
+
advice_called.should == 0
|
249
|
+
|
250
|
+
advised = null_advice.advise!(CheapAdvice::Test::M, :mm, :module)
|
251
|
+
null_advice.advised.size.should == 1
|
252
|
+
|
253
|
+
CheapAdvice::Test::M.mm(5).should == 8
|
254
|
+
|
255
|
+
advice_called.should == 1
|
256
|
+
|
257
|
+
advised.unadvise!
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'handles Class method advice.' do
|
261
|
+
advice_called = 0
|
262
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
263
|
+
advice_called += 1
|
264
|
+
end
|
265
|
+
null_advice.advised.size.should == 0
|
266
|
+
|
267
|
+
|
268
|
+
@f = CheapAdvice::Test::Foo.new
|
269
|
+
|
270
|
+
advice_called.should == 0
|
271
|
+
CheapAdvice::Test::Foo._baz.should == nil
|
272
|
+
|
273
|
+
advised = null_advice.advise!(CheapAdvice::Test::Foo, :baz, :class)
|
274
|
+
null_advice.advised.size.should == 1
|
275
|
+
|
276
|
+
CheapAdvice::Test::Foo.baz(5).should == 8
|
277
|
+
CheapAdvice::Test::Foo._baz.should == 8
|
278
|
+
@f.baz(5).should == 10
|
279
|
+
@f._baz.should == 10
|
280
|
+
advice_called.should == 1
|
281
|
+
|
282
|
+
advised.unadvise!
|
283
|
+
|
284
|
+
CheapAdvice::Test::Foo.baz(7).should == 10
|
285
|
+
CheapAdvice::Test::Foo._baz.should == 10
|
286
|
+
@f.baz(5).should == 10
|
287
|
+
@f._baz.should == 10
|
288
|
+
advice_called.should == 1
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'handles private method advice.' do
|
292
|
+
advice_called = 0
|
293
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
294
|
+
advice_called += 1
|
295
|
+
end
|
296
|
+
null_advice.advised.size.should == 0
|
297
|
+
|
298
|
+
advice_called.should == 0
|
299
|
+
|
300
|
+
advised = null_advice.advise!(CheapAdvice::Test::Bar, :private_method)
|
301
|
+
advised.scope.should == :private
|
302
|
+
null_advice.advised.size.should == 1
|
303
|
+
|
304
|
+
@b = CheapAdvice::Test::Bar.new
|
305
|
+
|
306
|
+
@b.calls_private_method(5).should == 5
|
307
|
+
advice_called.should == 1
|
308
|
+
|
309
|
+
advised.unadvise!
|
310
|
+
|
311
|
+
@b.calls_private_method(5).should == 5
|
312
|
+
advice_called.should == 1
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
it 'handles protected method advice.' do
|
317
|
+
advice_called = 0
|
318
|
+
null_advice = CheapAdvice.new(:before) do | ar |
|
319
|
+
advice_called += 1
|
320
|
+
end
|
321
|
+
null_advice.advised.size.should == 0
|
322
|
+
|
323
|
+
advice_called.should == 0
|
324
|
+
|
325
|
+
advised = null_advice.advise!(CheapAdvice::Test::Bar, :protected_method)
|
326
|
+
advised.scope.should == :protected
|
327
|
+
null_advice.advised.size.should == 1
|
328
|
+
|
329
|
+
@b = CheapAdvice::Test::Baz.new
|
330
|
+
|
331
|
+
@b.calls_protected_method(5).should == 5
|
332
|
+
advice_called.should == 1
|
333
|
+
|
334
|
+
advised.unadvise!
|
335
|
+
|
336
|
+
@b.calls_protected_method(5).should == 5
|
337
|
+
advice_called.should == 1
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
|