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 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
- def self.kinds_in_priority_order
11
- [:around, :before, :after, :after_returning, :after_raising]
12
- end
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
- self.kinds_in_priority_order.index(x.to_sym) <=> self.kinds_in_priority_order.index(y.to_sym)
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
- options[:next_node] ||= nil # assign so the attribute is always created
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 <<-EOF
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>:default_object => object</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 :default_object will be used, if defined. The
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 use it explicitly!
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
- # See also the project README for extensive examples of these options.
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
- rationalize_parameters opts
167
- @specification = Aquarium::Utils::MethodUtils.method_args_to_hash(*opts) {|option| ""} # set other hash values to an empty string
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 || @specification[:advice]
223
+ @advice = determine_advice block
171
224
  if @advice.nil? && @specification[:noop].nil?
172
- raise Aquarium::Utils::InvalidOptions.new("No advice block or argument specified.")
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
- pc_options[:types] = types_given.to_a if types_given?
191
- pc_options[:objects] = objects_given.to_a if objects_given?
192
- pc_options[:methods] = methods_given.to_a if methods_given?
193
- pc_options[:method_options] = method_options_given.to_a if method_options_given?
194
- pc_options[:attributes] = attributes_given.to_a if attributes_given?
195
- pc_options[:attribute_options] = attribute_options_given.to_a if attribute_options_given?
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
- add_advice_framework join_point
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
- advice_chain = Aspect.get_advice_chain join_point.type_or_object, join_point.method_name
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.method_name.to_s =~ /^#{Aspect.aspect_method_prefix}/
294
+ join_point_for_aspect_implementation_method? join_point
219
295
  end
220
296
  end
221
297
 
222
- def add_advice_to_chain join_point, advice_chain, advice_kind, advice
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 => advice_chain,
308
+ :next_node => start_of_advice_chain,
228
309
  :static_join_point => join_point})
229
- # The returned node becomes the lead node in the chain.
230
- advice_chain = Aspect.get_advice_chain join_point.type_or_object, join_point.method_name
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 message
314
+ def get_jps which_jps
236
315
  jps = Set.new
237
316
  @pointcuts.each do |pointcut|
238
- jps = jps.union(pointcut.send(message))
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(/\</,"&lt;").gsub(/\>/,"&gt;")+"<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
- type_to_advise = join_point.target_type || (class << join_point.target_object; self; end)
253
- alias_method_name = (saved_method_name join_point).intern
254
- return if private_method_defined? join_point.type_or_object, alias_method_name
255
- type_to_advise.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
256
- #{static_method_prefix join_point.instance_method?}
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
- advice_chain = Aspect.get_advice_chain join_point.type_or_object, join_point.method_name
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 instance_method
269
- return "" if instance_method
270
- <<-EOF
271
- class << self
272
- EOF
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 instance_method
276
- return "" if instance_method
277
- <<-EOF
278
- end
279
- EOF
365
+ def static_method_suffix join_point
366
+ join_point.instance_method? ? "" : "end"
280
367
  end
281
-
282
- # Note that we make the alias for the original method private, so it doesn't pollute the "interface"
283
- # of the advised classes. This also means that we have to use the Class.method(name).call()
284
- # idiom when invoking it.
285
- def alias_original_method_text alias_method_name, join_point
286
- self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
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 = Aspect.get_advice_chain #{self_name}, :#{join_point.method_name}
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 = Aspect.make_advice_join_point static_join_point, #{target_self}, args, block_for_method
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
- advice_chain = Aspect.get_advice_chain join_point.type_or_object, join_point.method_name
328
- advice_chain = prune_nodes_in 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 prune_nodes_in advice_chain
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
- keeper_node = node = advice_chain
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
- if Aquarium::Utils::TypeUtils.is_type?(join_point.type_or_object)
353
- restore_type_method join_point
354
- else
355
- restore_object_method join_point
356
- end
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 restore_type_method join_point
360
- alias_method_name = (saved_method_name join_point).intern
361
- join_point.target_type.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
362
- #{static_method_prefix join_point.instance_method?}
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
- #{static_method_suffix join_point.instance_method?}
365
- #{remove_advice_chain_class_variable_text alias_method_name, join_point}
366
- EVAL_WRAPPER
455
+ #{undef_eigenclass_method_text join_point}
456
+ #{static_method_suffix join_point}
457
+ EOF
367
458
  end
368
459
 
369
- def restore_object_method join_point
370
- saved = saved_method_name join_point
371
- singleton = class << join_point.target_object; self; end
372
- singleton.class_eval do
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.set_advice_chain type_or_object, method_name, advice_chain
382
- advice_chain_attr_sym = self.make_advice_chain_attr_sym type_or_object, method_name
383
- if Aquarium::Utils::TypeUtils.is_type?(type_or_object)
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.get_advice_chain type_or_object, method_name
395
- advice_chain_attr_sym = self.make_advice_chain_attr_sym type_or_object, method_name
396
- if Aquarium::Utils::TypeUtils.is_type?(type_or_object)
397
- type_or_object.class_eval do
398
- class_variable_get advice_chain_attr_sym
399
- end
400
- else
401
- type_or_object.instance_eval do
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 type_or_object, method_name
408
- ats = Aquarium::Utils::TypeUtils.is_type?(type_or_object) ? "@@" : "@"
409
- chain_class_var = (ats + self.advice_chain_attr_name(type_or_object, method_name)).intern
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
- def private_method_defined? type_or_object, method_name
414
- if Aquarium::Utils::TypeUtils.is_type? type_or_object
415
- type_or_object.private_instance_methods.include? method_name.to_s
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 saved_method_name join_point
433
- to_key = Aquarium::Utils::NameUtils.make_type_or_object_key(join_point.type_or_object)
434
- valid_name = Aquarium::Utils::NameUtils.make_valid_attr_name_from_method_name join_point.method_name
435
- "#{Aspect.aspect_method_prefix}saved_#{to_key}_#{valid_name}"
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
- option_synonyms.each do |syn, actual|
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
- advice_synonyms = {
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
- [:types, :objects, :methods, :method_options, :pointcuts, :default_object].each do |opt|
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? #or objects_given_excluding_default?
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
- # unless methods_given? or pointcuts_given? or types_given? #or objects_given_excluding_default?
505
- # specification_too_short
506
- # end
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
- def around_given_with? other_advice_kind_sym
518
- @specification[:around] and @specification[other_advice_kind_sym]
519
- end
520
-
521
- def after_given_with? other_advice_kind_sym
522
- @specification[:after] and @specification[other_advice_kind_sym]
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
- %w[pointcuts types objects methods attributes method_options attribute_options].each do |name|
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 + " (options: #{@original_options.inspect})")
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