aquarium 0.1.7 → 0.1.8
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/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
|