aquarium 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +20 -0
- data/README +17 -0
- data/UPGRADE +5 -0
- data/lib/aquarium/aspects/advice.rb +10 -9
- data/lib/aquarium/aspects/aspect.rb +274 -199
- data/lib/aquarium/aspects/default_object_handler.rb +22 -10
- data/lib/aquarium/aspects/exclusion_handler.rb +61 -0
- data/lib/aquarium/aspects/join_point.rb +18 -17
- data/lib/aquarium/aspects/pointcut.rb +44 -49
- data/lib/aquarium/finders/method_finder.rb +1 -1
- data/lib/aquarium/finders/object_finder.rb +1 -0
- data/lib/aquarium/utils/method_utils.rb +49 -8
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/advice_chain_node_spec.rb +5 -3
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +1334 -31
- data/spec/aquarium/aspects/aspect_spec.rb +164 -426
- data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +18 -2
- data/spec/aquarium/aspects/join_point_spec.rb +46 -4
- data/spec/aquarium/aspects/pointcut_spec.rb +80 -39
- data/spec/aquarium/finders/method_finder_spec.rb +25 -2
- data/spec/aquarium/finders/type_finder_spec.rb +1 -1
- data/spec/aquarium/spec_example_classes.rb +17 -0
- data/spec/aquarium/utils/method_utils_spec.rb +270 -46
- metadata +4 -3
data/CHANGES
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
== Version 0.1.8
|
2
|
+
|
3
|
+
V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also adds
|
4
|
+
several feature enhancements and refactorings. There are no known upgrade issues.
|
5
|
+
|
6
|
+
Bug fixes:
|
7
|
+
none
|
8
|
+
|
9
|
+
Enhancements:
|
10
|
+
13399 Add :exclusion options for methods and types.
|
11
|
+
14707 :exclude_ancestor_methods as synonym for :suppress_ancestor_methods
|
12
|
+
|
13
|
+
13399 adds new :exclude_(pointcuts|join_points|types|objects|methods|attributes) options for
|
14
|
+
Aspect.new, and Pointcut.new that make it easier to specify a list or regular expression for
|
15
|
+
various "items" and then to exclude particular items, e.g.,
|
16
|
+
Aspect.new :around, :types => /nterestingType/, :exclude_types => UninterestingType ...
|
17
|
+
|
18
|
+
The :exclude_ancestor_methods option is now preferred over :suppress_ancestor_methods,
|
19
|
+
since the former is more consistent with the new :exclude_* options.
|
20
|
+
|
1
21
|
== Version 0.1.7
|
2
22
|
|
3
23
|
Bug fixes:
|
data/README
CHANGED
@@ -194,6 +194,23 @@ You can specify a "Pointcut" that encapsulates one or more pre-defined Pointcuts
|
|
194
194
|
around :pointcuts = [jp, ...], ... # for pre-defined join point list
|
195
195
|
around :pointcut = {:type => T, :method => :m}, ... # same as around :type => T, :method => :m, ..
|
196
196
|
|
197
|
+
With release V0.1.8, you can specifically exclude particular pointcuts, join points, types, objects, methods, or attributes. This is useful when you specify a list or regular expression of "items" to match and you want to exclude some of the items.
|
198
|
+
Note that there is an open bug (#15202) that appears to affect advising types, unadvising the types, then advising objects
|
199
|
+
of the same types. (This is not likely to happen a lot in real applications, but it shows up when running specs.)
|
200
|
+
|
201
|
+
around ..., :exclude_pointcut = pc, ...
|
202
|
+
around ..., :exclude_pointcuts = [pc, ...]
|
203
|
+
around ..., :exclude_join_point = jp, ...
|
204
|
+
around ..., :exclude_join_points = [jp, ...]
|
205
|
+
around ..., :exclude_type = t, ...
|
206
|
+
around ..., :exclude_types = [t, ...]
|
207
|
+
around ..., :exclude_object = o, ...
|
208
|
+
around ..., :exclude_objects = [o, ...]
|
209
|
+
around ..., :exclude_method = m, ...
|
210
|
+
around ..., :exclude_methods = [m, ...]
|
211
|
+
around ..., :exclude_attribute = a, ...
|
212
|
+
around ..., :exclude_attributes = [a, ...]
|
213
|
+
|
197
214
|
You can advice methods before execution:
|
198
215
|
|
199
216
|
before :types => ...
|
data/UPGRADE
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
== Upgrading to Aquarium-0.1.8
|
2
|
+
|
3
|
+
V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also adds
|
4
|
+
several feature enhancements and refactorings. There are no known upgrade issues.
|
5
|
+
|
1
6
|
== Upgrading to Aquarium-0.1.7
|
2
7
|
|
3
8
|
This is primarily a bug-fix release, so there should be no upgrading or incompatibility issues.
|
@@ -7,15 +7,14 @@ require 'aquarium/utils/nil_object'
|
|
7
7
|
module Aquarium
|
8
8
|
module Aspects
|
9
9
|
module Advice
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def self.kinds; self.kinds_in_priority_order; end
|
10
|
+
|
11
|
+
KINDS_IN_PRIORITY_ORDER = [:around, :before, :after, :after_returning, :after_raising]
|
12
|
+
|
13
|
+
def self.kinds; KINDS_IN_PRIORITY_ORDER; end
|
15
14
|
|
16
15
|
def self.sort_by_priority_order advice_kinds
|
17
16
|
advice_kinds.sort do |x,y|
|
18
|
-
|
17
|
+
KINDS_IN_PRIORITY_ORDER.index(x.to_sym) <=> KINDS_IN_PRIORITY_ORDER.index(y.to_sym)
|
19
18
|
end.map {|x| x.to_sym}
|
20
19
|
end
|
21
20
|
end
|
@@ -28,10 +27,12 @@ module Aquarium
|
|
28
27
|
include Enumerable
|
29
28
|
def initialize options = {}, &proc_block
|
30
29
|
@proc = Proc.new &proc_block
|
31
|
-
|
30
|
+
# assign :next_node and :static_join_point so the attributes are always created
|
31
|
+
options[:next_node] ||= nil
|
32
|
+
options[:static_join_point] ||= nil
|
32
33
|
options.each do |key, value|
|
33
34
|
instance_variable_set "@#{key}".intern, value
|
34
|
-
(class << self; self; end).class_eval
|
35
|
+
(class << self; self; end).class_eval(<<-EOF, __FILE__, __LINE__)
|
35
36
|
attr_accessor(:#{key})
|
36
37
|
EOF
|
37
38
|
end
|
@@ -44,7 +45,7 @@ module Aquarium
|
|
44
45
|
class_or_instance_method_separater = jp.instance_method? ? "#" : "."
|
45
46
|
context_message = "Exception raised while executing \"#{jp.context.advice_kind}\" advice for \"#{jp.type_or_object.inspect}#{class_or_instance_method_separater}#{jp.method_name}\": "
|
46
47
|
backtrace = e.backtrace
|
47
|
-
e2 = e.exception(context_message + e.message)
|
48
|
+
e2 = e.exception(context_message + e.message + " (join_point = #{jp.inspect})")
|
48
49
|
e2.set_backtrace backtrace
|
49
50
|
raise e2
|
50
51
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'aquarium/extensions'
|
2
2
|
require 'aquarium/utils'
|
3
3
|
require 'aquarium/aspects/advice'
|
4
|
+
require 'aquarium/aspects/exclusion_handler'
|
4
5
|
require 'aquarium/aspects/join_point'
|
5
6
|
require 'aquarium/aspects/pointcut'
|
6
7
|
require 'aquarium/aspects/pointcut_composition'
|
@@ -19,6 +20,8 @@ module Aquarium
|
|
19
20
|
#
|
20
21
|
# See also Aquarium::Aspects::DSL::AspectDsl for more information.
|
21
22
|
class Aspect
|
23
|
+
include Advice
|
24
|
+
include ExclusionHandler
|
22
25
|
include DefaultObjectHandler
|
23
26
|
include Aquarium::Utils::ArrayUtils
|
24
27
|
include Aquarium::Utils::HashUtils
|
@@ -27,6 +30,41 @@ module Aquarium
|
|
27
30
|
attr_accessor :verbose, :log
|
28
31
|
attr_reader :specification, :pointcuts, :advice
|
29
32
|
|
33
|
+
ALLOWED_OPTIONS_SINGULAR = %w[advice type object method attribute method_option attribute_option pointcut default_object
|
34
|
+
exclude_type exclude_object exclude_pointcut exclude_join_point exclude_method exclude_attribute noop].map {|o| o.intern}
|
35
|
+
|
36
|
+
ALLOWED_OPTIONS_PLURAL = ALLOWED_OPTIONS_SINGULAR.map {|o| "#{o}s".intern}
|
37
|
+
|
38
|
+
ALLOWED_OPTIONS = ALLOWED_OPTIONS_SINGULAR + ALLOWED_OPTIONS_PLURAL
|
39
|
+
|
40
|
+
ADVICE_OPTIONS_SYNONYMS_MAP = {
|
41
|
+
:call => :advice,
|
42
|
+
:invoke => :advice,
|
43
|
+
:advise_with => :advice
|
44
|
+
}
|
45
|
+
|
46
|
+
ALLOWED_OPTIONS_SYNONYMS_MAP = {
|
47
|
+
:type => :types,
|
48
|
+
:within_type => :types,
|
49
|
+
:within_types => :types,
|
50
|
+
:object => :objects,
|
51
|
+
:within_object => :objects,
|
52
|
+
:within_objects => :objects,
|
53
|
+
:method => :methods,
|
54
|
+
:within_method => :methods,
|
55
|
+
:within_methods => :methods,
|
56
|
+
:attribute => :attributes,
|
57
|
+
:pointcut => :pointcuts,
|
58
|
+
:within_pointcut => :pointcuts,
|
59
|
+
:within_pointcuts => :pointcuts,
|
60
|
+
:exclude_type => :exclude_types,
|
61
|
+
:exclude_object => :exclude_objects,
|
62
|
+
:exclude_pointcut => :exclude_pointcuts,
|
63
|
+
:exclude_join_point => :exclude_join_points,
|
64
|
+
:exclude_method => :exclude_methods,
|
65
|
+
:exclude_attribute => :exclude_attributes,
|
66
|
+
}
|
67
|
+
|
30
68
|
# Aspect.new (:around | :before | :after | :after_returning | :after_raising ) \
|
31
69
|
# (:pointcuts => [...]), | \
|
32
70
|
# ((:types => [...] | :objects => [...]),
|
@@ -77,11 +115,13 @@ module Aquarium
|
|
77
115
|
# <tt>:within_objects => object || [object_list]</tt>::
|
78
116
|
# One or an array of objects to advise.
|
79
117
|
#
|
80
|
-
# <tt>:
|
118
|
+
# <tt>:default_objects => object || [object_list]</tt>::
|
119
|
+
# <tt>:default_object => object || [object_list]</tt>::
|
81
120
|
# An "internal" flag used by the methods that AspectDSL adds to Object. When no object
|
82
|
-
# or type is specified, the value of :
|
121
|
+
# or type is specified, the value of :default_objects will be used, if defined. The
|
83
122
|
# AspectDSL methods set the value to self, so that the user doesn't have to in the
|
84
|
-
# appropriate contexts. This flag is subject to change, so don't
|
123
|
+
# appropriate contexts, for convenience. This flag is subject to change, so don't
|
124
|
+
# use it explicitly!
|
85
125
|
#
|
86
126
|
# <tt>:methods => method || [method_list]</tt>::
|
87
127
|
# <tt>:method => method || [method_list]</tt>::
|
@@ -109,7 +149,21 @@ module Aquarium
|
|
109
149
|
# <tt>:writers</tt>, and/or <tt>:writer</tt> (synonymous). By default, both
|
110
150
|
# readers and writers are matched.
|
111
151
|
#
|
112
|
-
#
|
152
|
+
# <tt>:exclude_pointcuts => pc || [pc_list]</tt>::
|
153
|
+
# <tt>:exclude_pointcut => pc || [pc_list]</tt>::
|
154
|
+
# <tt>:exclude_join_points => jp || [jp_list]</tt>::
|
155
|
+
# <tt>:exclude_join_point => jp || [jp_list]</tt>::
|
156
|
+
# <tt>:exclude_types => type || [type_list]</tt>::
|
157
|
+
# <tt>:exclude_types => type || [type_list]</tt>::
|
158
|
+
# <tt>:exclude_type => type || [type_list]</tt>::
|
159
|
+
# <tt>:exclude_objects => object || [object_list]</tt>::
|
160
|
+
# <tt>:exclude_object => object || [object_list]</tt>::
|
161
|
+
# <tt>:exclude_methods => method || [method_list]</tt>::
|
162
|
+
# <tt>:exclude_method => method || [method_list]</tt>::
|
163
|
+
# <tt>:exclude_attributes => attribute || [attribute_list]</tt>::
|
164
|
+
# <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
|
165
|
+
# Exclude the specified "things" from the matched join points.
|
166
|
+
#
|
113
167
|
def initialize *options, &block
|
114
168
|
process_input options, &block
|
115
169
|
init_pointcuts
|
@@ -154,25 +208,30 @@ module Aquarium
|
|
154
208
|
protected
|
155
209
|
|
156
210
|
def process_input options, &block
|
157
|
-
@original_options = options
|
211
|
+
@original_options = options.flatten
|
158
212
|
make_specification options, &block
|
159
213
|
@verbose = @specification[:verbose] || false
|
160
214
|
@log = @specification[:log] || ""
|
161
|
-
validate_specification
|
162
215
|
end
|
163
216
|
|
164
217
|
def make_specification options, &block
|
165
|
-
opts = options.flatten.dup
|
166
|
-
|
167
|
-
@specification = Aquarium::Utils::MethodUtils.method_args_to_hash(*opts) {|option| ""}
|
218
|
+
opts = rationalize_parameters options.flatten.dup
|
219
|
+
# For non-hash inputs, use an empty string for the value
|
220
|
+
@specification = Aquarium::Utils::MethodUtils.method_args_to_hash(*opts) {|option| ""}
|
168
221
|
use_default_object_if_defined unless (types_given? || objects_given? || pointcuts_given?)
|
169
222
|
use_first_nonadvice_symbol_as_method(opts) unless methods_given?
|
170
|
-
@advice = block
|
223
|
+
@advice = determine_advice block
|
171
224
|
if @advice.nil? && @specification[:noop].nil?
|
172
|
-
|
225
|
+
bad_options "No advice block nor :advice argument was given."
|
173
226
|
end
|
227
|
+
validate_specification
|
174
228
|
end
|
175
229
|
|
230
|
+
def determine_advice block
|
231
|
+
# There can be only one advice; take the last one...
|
232
|
+
block || (@specification[:advice].kind_of?(Array) ? @specification[:advice].last : @specification[:advice])
|
233
|
+
end
|
234
|
+
|
176
235
|
def init_pointcuts
|
177
236
|
pointcuts = []
|
178
237
|
if pointcuts_given?
|
@@ -181,61 +240,81 @@ module Aquarium
|
|
181
240
|
pointcuts << pointcut
|
182
241
|
elsif pointcut.kind_of?(Aquarium::Aspects::JoinPoint)
|
183
242
|
pointcuts << Aquarium::Aspects::Pointcut.new(:join_point => pointcut)
|
184
|
-
else
|
243
|
+
else # a hash of Pointcut.new options?
|
185
244
|
pointcuts << Aquarium::Aspects::Pointcut.new(pointcut)
|
186
245
|
end
|
187
246
|
end
|
188
247
|
else
|
189
248
|
pc_options = {}
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
249
|
+
ALLOWED_OPTIONS_PLURAL.each do |option|
|
250
|
+
next if pointcut_new_doesnt_accept? option
|
251
|
+
self.instance_eval(<<-EOF, __FILE__, __LINE__)
|
252
|
+
pc_options[:#{option}] = #{option}_given if #{option}_given?
|
253
|
+
EOF
|
254
|
+
end
|
196
255
|
pointcuts << Aquarium::Aspects::Pointcut.new(pc_options)
|
197
256
|
end
|
198
|
-
@pointcuts = Set.new(pointcuts)
|
257
|
+
@pointcuts = Set.new(remove_excluded_join_points_and_empty_pointcuts(pointcuts))
|
199
258
|
end
|
200
259
|
|
260
|
+
def pointcut_new_doesnt_accept? option
|
261
|
+
case option
|
262
|
+
when :advices: true
|
263
|
+
when :pointcuts: true
|
264
|
+
when :default_objects: true
|
265
|
+
when :noops: true
|
266
|
+
else false
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def remove_excluded_join_points_and_empty_pointcuts pointcuts
|
271
|
+
pointcuts.reject do |pc|
|
272
|
+
pc.join_points_matched.delete_if do |jp|
|
273
|
+
join_point_excluded? jp
|
274
|
+
end
|
275
|
+
pc.empty?
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
201
279
|
def advise_join_points
|
202
280
|
advice = @advice.to_proc
|
203
281
|
@pointcuts.each do |pointcut|
|
204
282
|
interesting_join_points(pointcut).each do |join_point|
|
205
|
-
|
283
|
+
attr_name = Aspect.make_advice_chain_attr_sym(join_point)
|
284
|
+
add_advice_framework(join_point) if need_advice_framework?(join_point)
|
206
285
|
Aquarium::Aspects::Advice.sort_by_priority_order(specified_advice_kinds).reverse.each do |advice_kind|
|
207
|
-
|
208
|
-
add_advice_to_chain join_point, advice_chain, advice_kind, advice
|
286
|
+
add_advice_to_chain join_point, advice_kind, advice
|
209
287
|
end
|
210
288
|
end
|
211
289
|
end
|
212
290
|
end
|
213
291
|
|
214
|
-
# Ignore any inserted methods that are part of the aspect implementation,
|
215
|
-
# i.e., those that match the prefix returned by Aspect.aspect_method_prefix.
|
216
292
|
def interesting_join_points pointcut
|
217
293
|
pointcut.join_points_matched.reject do |join_point|
|
218
|
-
join_point
|
294
|
+
join_point_for_aspect_implementation_method? join_point
|
219
295
|
end
|
220
296
|
end
|
221
297
|
|
222
|
-
def
|
298
|
+
def join_point_for_aspect_implementation_method? join_point
|
299
|
+
join_point.method_name.to_s.index("#{Aspect.aspect_method_prefix}") == 0
|
300
|
+
end
|
301
|
+
|
302
|
+
def add_advice_to_chain join_point, advice_kind, advice
|
303
|
+
start_of_advice_chain = Aspect.get_advice_chain join_point
|
223
304
|
options = @specification.merge({
|
224
305
|
:aspect => self,
|
225
306
|
:advice_kind => advice_kind,
|
226
307
|
:advice => advice,
|
227
|
-
:next_node =>
|
308
|
+
:next_node => start_of_advice_chain,
|
228
309
|
:static_join_point => join_point})
|
229
|
-
#
|
230
|
-
|
231
|
-
Aspect.set_advice_chain(join_point.type_or_object, join_point.method_name, Aquarium::Aspects::AdviceChainNodeFactory.make_node(options))
|
232
|
-
advice_chain = Aspect.get_advice_chain join_point.type_or_object, join_point.method_name
|
310
|
+
# New node is new start of chain.
|
311
|
+
Aspect.set_advice_chain(join_point, Aquarium::Aspects::AdviceChainNodeFactory.make_node(options))
|
233
312
|
end
|
234
313
|
|
235
|
-
def get_jps
|
314
|
+
def get_jps which_jps
|
236
315
|
jps = Set.new
|
237
316
|
@pointcuts.each do |pointcut|
|
238
|
-
jps = jps.union(pointcut.send(
|
317
|
+
jps = jps.union(pointcut.send(which_jps))
|
239
318
|
end
|
240
319
|
jps
|
241
320
|
end
|
@@ -244,62 +323,93 @@ module Aquarium
|
|
244
323
|
def self.advice_chain_inspect advice_chain
|
245
324
|
return "[nil]" if advice_chain.nil?
|
246
325
|
"<br/>"+advice_chain.inspect do |advice_chain|
|
247
|
-
"#{advice_chain.class.name}:#{advice_chain.object_id}: aspect = #{advice_chain.aspect.object_id}, next_node = #{advice_chain_inspect advice_chain.next_node}"
|
326
|
+
"#{advice_chain.class.name}:#{advice_chain.object_id}: join_point = #{advice_chain.static_join_point}: aspect = #{advice_chain.aspect.object_id}, next_node = #{advice_chain_inspect advice_chain.next_node}"
|
248
327
|
end.gsub(/\</,"<").gsub(/\>/,">")+"<br/>"
|
249
328
|
end
|
250
|
-
|
329
|
+
|
330
|
+
def need_advice_framework? join_point
|
331
|
+
alias_method_name = (Aspect.make_saved_method_name join_point).intern
|
332
|
+
private_method_defined?(join_point, alias_method_name) == false
|
333
|
+
end
|
334
|
+
|
251
335
|
def add_advice_framework join_point
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
#{alias_original_method_text alias_method_name, join_point}
|
258
|
-
#{static_method_suffix join_point.instance_method?}
|
259
|
-
EVAL_WRAPPER
|
260
|
-
Aspect.set_advice_chain join_point.type_or_object, join_point.method_name, Aquarium::Aspects::AdviceChainNodeFactory.make_node(
|
336
|
+
alias_method_name = (Aspect.make_saved_method_name join_point).intern
|
337
|
+
type_to_advise = Aspect.type_to_advise_for join_point
|
338
|
+
# Note: Must set advice chain, a class variable on the type we're advising, FIRST.
|
339
|
+
# Otherwise the class_eval that follows will assume the @@ advice chain belongs to Aspect!
|
340
|
+
Aspect.set_advice_chain join_point, Aquarium::Aspects::AdviceChainNodeFactory.make_node(
|
261
341
|
:aspect => nil, # Belongs to all aspects that might advise this join point!
|
262
342
|
:advice_kind => :none,
|
263
343
|
:alias_method_name => alias_method_name,
|
264
344
|
:static_join_point => join_point)
|
265
|
-
|
345
|
+
type_being_advised_text = join_point.instance_method? ? "self.class" : "self"
|
346
|
+
unless Aspect.is_type_join_point?(join_point)
|
347
|
+
type_being_advised_text = "(class << self; self; end)"
|
348
|
+
end
|
349
|
+
type_to_advise2 = join_point.instance_method? ? type_to_advise : (class << type_to_advise; self; end)
|
350
|
+
type_to_advise2.class_eval(<<-EOF, __FILE__, __LINE__)
|
351
|
+
#{def_eigenclass_method_text join_point}
|
352
|
+
#{alias_original_method_text alias_method_name, join_point, type_being_advised_text}
|
353
|
+
EOF
|
266
354
|
end
|
267
355
|
|
268
|
-
def static_method_prefix
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
356
|
+
def static_method_prefix join_point
|
357
|
+
if join_point.instance_method?
|
358
|
+
"@@type_being_advised = self"
|
359
|
+
else
|
360
|
+
"@@type_being_advised = self"
|
361
|
+
"class << self"
|
362
|
+
end
|
273
363
|
end
|
274
364
|
|
275
|
-
def static_method_suffix
|
276
|
-
|
277
|
-
<<-EOF
|
278
|
-
end
|
279
|
-
EOF
|
365
|
+
def static_method_suffix join_point
|
366
|
+
join_point.instance_method? ? "" : "end"
|
280
367
|
end
|
281
|
-
|
282
|
-
#
|
283
|
-
#
|
284
|
-
#
|
285
|
-
def
|
286
|
-
|
368
|
+
|
369
|
+
# When advising an instance, create an override method that gets advised instead of the types method.
|
370
|
+
# Otherwise, all objects will be advised!
|
371
|
+
# Note: this also solves bug #15202.
|
372
|
+
def def_eigenclass_method_text join_point
|
373
|
+
Aspect.is_type_join_point?(join_point) ? "" : "def #{join_point.method_name} *args; super; end"
|
374
|
+
end
|
375
|
+
|
376
|
+
# For the temporary eigenclass method wrapper, alias it to a temporary name then undefine it, so it
|
377
|
+
# completely disappears. Next, remove_method on the method name so the object starts responding again
|
378
|
+
# to the original definition.
|
379
|
+
def undef_eigenclass_method_text join_point
|
380
|
+
Aspect.is_type_join_point?(join_point) ? "" : "remove_method :#{join_point.method_name}"
|
381
|
+
end
|
382
|
+
|
383
|
+
# TODO Move to JoinPoint
|
384
|
+
def self.is_type_join_point? join_point
|
385
|
+
Aquarium::Utils::TypeUtils.is_type? join_point.type_or_object
|
386
|
+
end
|
387
|
+
|
388
|
+
def self.type_to_advise_for join_point
|
389
|
+
join_point.target_type ? join_point.target_type : (class << join_point.target_object; self; end)
|
390
|
+
end
|
391
|
+
|
392
|
+
def alias_original_method_text alias_method_name, join_point, type_being_advised_text
|
287
393
|
target_self = join_point.instance_method? ? "self" : join_point.target_type.name
|
394
|
+
advice_chain_attr_sym = Aspect.make_advice_chain_attr_sym join_point
|
288
395
|
<<-EOF
|
289
396
|
alias_method :#{alias_method_name}, :#{join_point.method_name}
|
290
397
|
def #{join_point.method_name} *args, &block_for_method
|
291
|
-
advice_chain =
|
398
|
+
advice_chain = #{type_being_advised_text}.send :class_variable_get, "#{advice_chain_attr_sym}"
|
292
399
|
static_join_point = advice_chain.static_join_point
|
293
|
-
advice_join_point =
|
400
|
+
advice_join_point = static_join_point.make_current_context_join_point(
|
401
|
+
:advice_kind => :before,
|
402
|
+
:advised_object => #{target_self},
|
403
|
+
:parameters => args,
|
404
|
+
:block_for_method => block_for_method)
|
294
405
|
advice_chain.call advice_join_point, *args
|
295
406
|
end
|
296
407
|
#{join_point.visibility.to_s} :#{join_point.method_name}
|
297
408
|
private :#{alias_method_name}
|
298
409
|
EOF
|
299
410
|
end
|
300
|
-
|
411
|
+
|
301
412
|
def unalias_original_method_text alias_method_name, join_point
|
302
|
-
self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
|
303
413
|
<<-EOF
|
304
414
|
alias_method :#{join_point.method_name}, :#{alias_method_name}
|
305
415
|
#{join_point.visibility.to_s} :#{join_point.method_name}
|
@@ -307,134 +417,98 @@ module Aquarium
|
|
307
417
|
EOF
|
308
418
|
end
|
309
419
|
|
310
|
-
def remove_advice_chain_class_variable_text alias_method_name, join_point
|
311
|
-
self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
|
312
|
-
<<-EOF
|
313
|
-
advice_chain_name = :@@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}
|
314
|
-
remove_class_variable advice_chain_name
|
315
|
-
EOF
|
316
|
-
end
|
317
|
-
|
318
|
-
def self.make_advice_join_point static_join_point, object, method_parameters, block_for_method
|
319
|
-
static_join_point.make_current_context_join_point(
|
320
|
-
:advice_kind => :before,
|
321
|
-
:advised_object => object,
|
322
|
-
:parameters => method_parameters,
|
323
|
-
:block_for_method => block_for_method)
|
324
|
-
end
|
325
|
-
|
326
420
|
def remove_advice_for_aspect_at join_point
|
327
|
-
|
328
|
-
advice_chain =
|
329
|
-
if advice_chain.empty?
|
330
|
-
remove_advice_framework_for join_point
|
331
|
-
else
|
332
|
-
Aspect.set_advice_chain join_point.type_or_object, join_point.method_name, advice_chain
|
333
|
-
end
|
421
|
+
prune_nodes_in_advice_chain_for join_point
|
422
|
+
advice_chain = Aspect.get_advice_chain join_point
|
423
|
+
remove_advice_framework_for(join_point) if advice_chain.empty?
|
334
424
|
end
|
335
425
|
|
336
|
-
def
|
426
|
+
def prune_nodes_in_advice_chain_for join_point
|
427
|
+
advice_chain = Aspect.get_advice_chain join_point
|
337
428
|
# Use equal? for the aspects to compare object id only,
|
338
429
|
while advice_chain.empty? == false && advice_chain.aspect.equal?(self)
|
339
430
|
advice_chain = advice_chain.next_node
|
340
431
|
end
|
341
|
-
|
432
|
+
node = advice_chain
|
342
433
|
while node.empty? == false
|
343
434
|
while node.next_node.aspect.equal?(self)
|
344
435
|
node.next_node = node.next_node.next_node
|
345
436
|
end
|
346
437
|
node = node.next_node
|
347
438
|
end
|
348
|
-
advice_chain
|
439
|
+
Aspect.set_advice_chain join_point, advice_chain
|
349
440
|
end
|
350
441
|
|
351
442
|
def remove_advice_framework_for join_point
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
443
|
+
type_to_advise = Aspect.type_to_advise_for join_point
|
444
|
+
type_to_advise.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
|
445
|
+
#{restore_original_method_text join_point}
|
446
|
+
EVAL_WRAPPER
|
447
|
+
Aspect.remove_advice_chain join_point
|
357
448
|
end
|
358
449
|
|
359
|
-
def
|
360
|
-
alias_method_name = (
|
361
|
-
|
362
|
-
#{static_method_prefix join_point
|
450
|
+
def restore_original_method_text join_point
|
451
|
+
alias_method_name = (Aspect.make_saved_method_name join_point).intern
|
452
|
+
<<-EOF
|
453
|
+
#{static_method_prefix join_point}
|
363
454
|
#{unalias_original_method_text alias_method_name, join_point}
|
364
|
-
#{
|
365
|
-
#{
|
366
|
-
|
455
|
+
#{undef_eigenclass_method_text join_point}
|
456
|
+
#{static_method_suffix join_point}
|
457
|
+
EOF
|
367
458
|
end
|
368
459
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
alias_method join_point.method_name, saved
|
374
|
-
send join_point.visibility, join_point.method_name
|
375
|
-
undef_method saved.intern
|
376
|
-
end
|
377
|
-
advice_chain_name = "@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}".intern
|
378
|
-
join_point.target_object.method(:remove_instance_variable).call advice_chain_name
|
460
|
+
# TODO optimize calls to these *_advice_chain methods from other private methods.
|
461
|
+
def self.set_advice_chain join_point, advice_chain
|
462
|
+
advice_chain_attr_sym = self.make_advice_chain_attr_sym join_point
|
463
|
+
type_to_advise_for(join_point).send :class_variable_set, advice_chain_attr_sym, advice_chain
|
379
464
|
end
|
380
465
|
|
381
|
-
def self.
|
382
|
-
advice_chain_attr_sym = self.make_advice_chain_attr_sym
|
383
|
-
|
384
|
-
type_or_object.class_eval do
|
385
|
-
class_variable_set advice_chain_attr_sym, advice_chain
|
386
|
-
end
|
387
|
-
else
|
388
|
-
type_or_object.instance_eval do
|
389
|
-
instance_variable_set advice_chain_attr_sym, advice_chain
|
390
|
-
end
|
391
|
-
end
|
466
|
+
def self.get_advice_chain join_point
|
467
|
+
advice_chain_attr_sym = self.make_advice_chain_attr_sym join_point
|
468
|
+
type_to_advise_for(join_point).send :class_variable_get, advice_chain_attr_sym
|
392
469
|
end
|
393
|
-
|
394
|
-
def self.
|
395
|
-
advice_chain_attr_sym = self.make_advice_chain_attr_sym
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
instance_variable_get advice_chain_attr_sym
|
403
|
-
end
|
404
|
-
end
|
470
|
+
|
471
|
+
def self.remove_advice_chain join_point
|
472
|
+
advice_chain_attr_sym = self.make_advice_chain_attr_sym join_point
|
473
|
+
type_to_advise_for(join_point).send :remove_class_variable, advice_chain_attr_sym
|
474
|
+
end
|
475
|
+
|
476
|
+
def private_method_defined? join_point, alias_method_name
|
477
|
+
type_to_advise = Aspect.type_to_advise_for join_point
|
478
|
+
type_to_advise.send(:private_instance_methods).include? alias_method_name.to_s
|
405
479
|
end
|
406
480
|
|
407
|
-
def self.make_advice_chain_attr_sym
|
408
|
-
|
409
|
-
|
481
|
+
def self.make_advice_chain_attr_sym join_point
|
482
|
+
class_or_object_prefix = is_type_join_point?(join_point) ? "class_" : ""
|
483
|
+
type_or_object_key = make_type_or_object_key join_point
|
484
|
+
valid_name = Aquarium::Utils::NameUtils.make_valid_attr_name_from_method_name join_point.method_name
|
485
|
+
"@@#{Aspect.aspect_method_prefix}#{class_or_object_prefix}advice_chain_#{type_or_object_key}_#{valid_name}".intern
|
410
486
|
end
|
411
487
|
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
else
|
417
|
-
type_or_object.private_methods.include? method_name.to_s
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
def self.advice_chain_attr_name type_or_object, method_name
|
422
|
-
type_or_object_key = Aquarium::Utils::NameUtils.make_type_or_object_key(type_or_object)
|
423
|
-
class_or_object_prefix = Aquarium::Utils::TypeUtils.is_type?(type_or_object) ? "class_" : ""
|
424
|
-
valid_name = Aquarium::Utils::NameUtils.make_valid_attr_name_from_method_name method_name
|
425
|
-
"#{self.aspect_method_prefix}#{class_or_object_prefix}advice_chain_#{type_or_object_key}_#{valid_name}"
|
488
|
+
def self.make_saved_method_name join_point
|
489
|
+
type_or_object_key = make_type_or_object_key join_point
|
490
|
+
valid_name = Aquarium::Utils::NameUtils.make_valid_attr_name_from_method_name join_point.method_name
|
491
|
+
"#{Aspect.aspect_method_prefix}saved_#{type_or_object_key}_#{valid_name}"
|
426
492
|
end
|
427
493
|
|
428
494
|
def self.aspect_method_prefix
|
429
495
|
"_aspect_"
|
430
496
|
end
|
431
497
|
|
432
|
-
def
|
433
|
-
|
434
|
-
|
435
|
-
|
498
|
+
def self.determine_type_or_object join_point
|
499
|
+
join_point.type_or_object
|
500
|
+
# type_or_object = join_point.type_or_object
|
501
|
+
# method_type = join_point.instance_or_class_method
|
502
|
+
# if is_type_join_point? join_point
|
503
|
+
# type_or_object = Aquarium::Utils::MethodUtils.definer type_or_object, join_point.method_name, "#{method_type}_method_only".intern
|
504
|
+
# end
|
505
|
+
# type_or_object
|
436
506
|
end
|
437
|
-
|
507
|
+
|
508
|
+
def self.make_type_or_object_key join_point
|
509
|
+
Aquarium::Utils::NameUtils.make_type_or_object_key determine_type_or_object(join_point)
|
510
|
+
end
|
511
|
+
|
438
512
|
def specified_advice_kinds
|
439
513
|
@specification.keys.select do |key|
|
440
514
|
Aquarium::Aspects::Advice.kinds.include? key
|
@@ -442,25 +516,10 @@ module Aquarium
|
|
442
516
|
end
|
443
517
|
|
444
518
|
def rationalize_parameters opts
|
445
|
-
return unless opts.last.kind_of?(Hash)
|
446
|
-
option_synonyms = {
|
447
|
-
:type => :types,
|
448
|
-
:within_type => :types,
|
449
|
-
:within_types => :types,
|
450
|
-
:object => :objects,
|
451
|
-
:within_object => :objects,
|
452
|
-
:within_objects => :objects,
|
453
|
-
:method => :methods,
|
454
|
-
:within_method => :methods,
|
455
|
-
:within_methods => :methods,
|
456
|
-
:attribute => :attributes,
|
457
|
-
:pointcut => :pointcuts,
|
458
|
-
:within_pointcut => :pointcuts,
|
459
|
-
:within_pointcuts => :pointcuts
|
460
|
-
}
|
519
|
+
return opts unless opts.last.kind_of?(Hash)
|
461
520
|
hash = opts.pop.dup
|
462
521
|
opts.push hash
|
463
|
-
|
522
|
+
ALLOWED_OPTIONS_SYNONYMS_MAP.each do |syn, actual|
|
464
523
|
if hash.has_key? syn
|
465
524
|
hash[actual] = make_array(hash[actual], hash[syn])
|
466
525
|
hash.delete syn
|
@@ -468,19 +527,18 @@ module Aquarium
|
|
468
527
|
end
|
469
528
|
# Only one advice argument allowed.
|
470
529
|
unless hash.has_key?(:advice)
|
471
|
-
|
472
|
-
:call => :advice,
|
473
|
-
:invoke => :advice,
|
474
|
-
:advise_with => :advice
|
475
|
-
}
|
476
|
-
advice_synonyms.each do |syn, actual|
|
530
|
+
ADVICE_OPTIONS_SYNONYMS_MAP.each do |syn, actual|
|
477
531
|
if hash.has_key? syn
|
478
532
|
hash[actual] = hash[syn]
|
479
533
|
hash.delete syn
|
480
534
|
end
|
481
535
|
end
|
482
536
|
end
|
483
|
-
|
537
|
+
ALLOWED_OPTIONS_PLURAL.each do |opt|
|
538
|
+
case opt
|
539
|
+
when :advices: next
|
540
|
+
when :noops: next
|
541
|
+
end
|
484
542
|
hash[opt] = Set.new(make_array(hash[opt]))
|
485
543
|
end
|
486
544
|
opts
|
@@ -495,17 +553,31 @@ module Aquarium
|
|
495
553
|
bad_options(":after can't be used with :after_returning.") if after_given_with? :after_returning
|
496
554
|
bad_options(":after can't be used with :after_raising.") if after_given_with? :after_raising
|
497
555
|
bad_options(":after_returning can't be used with :after_raising.") if after_returning_given_with? :after_raising
|
498
|
-
unless pointcuts_given? or types_given? or objects_given?
|
556
|
+
unless pointcuts_given? or types_given? or objects_given? or default_objects_given?
|
499
557
|
bad_options("At least one of :pointcut(s), :type(s), :object(s) is required.")
|
500
558
|
end
|
501
559
|
if pointcuts_given? and (types_given? or objects_given?)
|
502
560
|
bad_options("Can't specify both :pointcut(s) and one or more of :type(s), and/or :object(s).")
|
503
561
|
end
|
504
|
-
|
505
|
-
|
506
|
-
|
562
|
+
@specification.each_key do |parameter|
|
563
|
+
check_parameter parameter
|
564
|
+
end
|
507
565
|
end
|
508
566
|
|
567
|
+
def check_parameter parameter
|
568
|
+
bad_options("Unrecognized parameter: :#{parameter}") unless is_valid_parameter(parameter)
|
569
|
+
end
|
570
|
+
|
571
|
+
def is_valid_parameter key
|
572
|
+
ALLOWED_OPTIONS.include?(key) || ALLOWED_OPTIONS_SYNONYMS_MAP.keys.include?(key) ||
|
573
|
+
ADVICE_OPTIONS_SYNONYMS_MAP.keys.include?(key) || KINDS_IN_PRIORITY_ORDER.include?(key) ||
|
574
|
+
parameter_is_a_method_name?(key) # i.e., use_first_nonadvice_symbol_as_method
|
575
|
+
end
|
576
|
+
|
577
|
+
def parameter_is_a_method_name? name
|
578
|
+
@specification[:methods].include? name
|
579
|
+
end
|
580
|
+
|
509
581
|
def advice_kinds_given
|
510
582
|
Aquarium::Aspects::Advice.kinds.inject([]) {|ary, kind| ary << @specification[kind] if @specification[kind]; ary}
|
511
583
|
end
|
@@ -514,21 +586,23 @@ module Aquarium
|
|
514
586
|
not advice_kinds_given.empty?
|
515
587
|
end
|
516
588
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
end
|
524
|
-
def after_returning_given_with? other_advice_kind_sym
|
525
|
-
@specification[:after_returning] and @specification[other_advice_kind_sym]
|
589
|
+
%w[around after after_returning].each do |advice_kind|
|
590
|
+
class_eval(<<-EOF, __FILE__, __LINE__)
|
591
|
+
def #{advice_kind}_given_with? other_advice_kind_sym
|
592
|
+
@specification[:#{advice_kind}] and @specification[other_advice_kind_sym]
|
593
|
+
end
|
594
|
+
EOF
|
526
595
|
end
|
527
|
-
|
528
|
-
|
596
|
+
|
597
|
+
ALLOWED_OPTIONS_PLURAL.each do |name|
|
598
|
+
case name
|
599
|
+
when :advices : next
|
600
|
+
when :default_objects : next
|
601
|
+
when :noops : next
|
602
|
+
end
|
529
603
|
class_eval(<<-EOF, __FILE__, __LINE__)
|
530
604
|
def #{name}_given
|
531
|
-
@specification[:#{name}]
|
605
|
+
make_array(@specification[:#{name}])
|
532
606
|
end
|
533
607
|
|
534
608
|
def #{name}_given?
|
@@ -550,7 +624,8 @@ module Aquarium
|
|
550
624
|
end
|
551
625
|
|
552
626
|
def bad_options message
|
553
|
-
raise Aquarium::Utils::InvalidOptions.new("Invalid options given. " + message +
|
627
|
+
raise Aquarium::Utils::InvalidOptions.new("Invalid options given. " + message +
|
628
|
+
" (options: #{@original_options.inspect}, mapped to specification: #{@specification.inspect})")
|
554
629
|
end
|
555
630
|
end
|
556
631
|
end
|