aquarium 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES +59 -2
  2. data/README +33 -16
  3. data/RELEASE-PLAN +28 -5
  4. data/UPGRADE +11 -0
  5. data/examples/aspect_design_example.rb +2 -2
  6. data/examples/aspect_design_example_spec.rb +2 -2
  7. data/examples/design_by_contract_example.rb +4 -4
  8. data/examples/design_by_contract_example_spec.rb +4 -4
  9. data/examples/method_missing_example.rb +4 -1
  10. data/examples/method_missing_example_spec.rb +4 -1
  11. data/examples/method_tracing_example.rb +2 -2
  12. data/examples/method_tracing_example_spec.rb +16 -16
  13. data/lib/aquarium/aspects/advice.rb +47 -25
  14. data/lib/aquarium/aspects/aspect.rb +81 -39
  15. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +1 -1
  16. data/lib/aquarium/aspects/exclusion_handler.rb +2 -2
  17. data/lib/aquarium/aspects/join_point.rb +28 -28
  18. data/lib/aquarium/aspects/pointcut.rb +61 -15
  19. data/lib/aquarium/extras/design_by_contract.rb +7 -7
  20. data/lib/aquarium/finders.rb +0 -1
  21. data/lib/aquarium/finders/method_finder.rb +10 -20
  22. data/lib/aquarium/finders/type_finder.rb +141 -75
  23. data/lib/aquarium/utils.rb +1 -0
  24. data/lib/aquarium/utils/logic_error.rb +9 -0
  25. data/lib/aquarium/utils/method_utils.rb +4 -3
  26. data/lib/aquarium/utils/nil_object.rb +1 -0
  27. data/lib/aquarium/utils/type_utils.rb +19 -0
  28. data/lib/aquarium/version.rb +2 -2
  29. data/spec/aquarium/aspects/advice_chain_node_spec.rb +2 -2
  30. data/spec/aquarium/aspects/advice_spec.rb +28 -5
  31. data/spec/aquarium/aspects/aspect_invocation_spec.rb +522 -289
  32. data/spec/aquarium/aspects/aspect_spec.rb +59 -41
  33. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +7 -7
  34. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +2 -2
  35. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -2
  36. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
  37. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +34 -34
  38. data/spec/aquarium/aspects/join_point_spec.rb +79 -0
  39. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +13 -3
  40. data/spec/aquarium/aspects/pointcut_spec.rb +310 -63
  41. data/spec/aquarium/extras/design_by_contract_spec.rb +4 -4
  42. data/spec/aquarium/finders/method_finder_spec.rb +208 -54
  43. data/spec/aquarium/finders/type_finder_spec.rb +24 -88
  44. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +206 -0
  45. data/spec/aquarium/spec_example_classes.rb +75 -12
  46. data/spec/aquarium/utils/logic_error_spec.rb +10 -0
  47. data/spec/aquarium/utils/type_utils_sample_classes.rb +203 -0
  48. data/spec/aquarium/utils/type_utils_spec.rb +47 -1
  49. metadata +48 -39
  50. data/lib/aquarium/finders/object_finder.rb +0 -75
  51. data/spec/aquarium/finders/object_finder_spec.rb +0 -231
@@ -26,6 +26,7 @@ module Aquarium
26
26
  class AdviceChainNode
27
27
  include Enumerable
28
28
  def initialize options = {}, &proc_block
29
+ raise Aquarium::Utils::InvalidOptions.new("You must specify an advice block or Proc") if proc_block.nil?
29
30
  @proc = Proc.new &proc_block
30
31
  # assign :next_node and :static_join_point so the attributes are always created
31
32
  options[:next_node] ||= nil
@@ -38,19 +39,28 @@ module Aquarium
38
39
  end
39
40
  end
40
41
 
41
- def call jp, *args
42
+ def call jp, obj, *args
43
+ do_call @proc, "", jp, obj, *args
44
+ end
45
+
46
+ def invoke_original_join_point current_jp, obj, *args
47
+ do_call last, "While executing the original join_point: ", current_jp, obj, *args
48
+ end
49
+
50
+ def do_call proc_to, error_message_prefix, jp, obj, *args
42
51
  begin
43
- @proc.call jp, *args
52
+ proc_to.call jp, obj, *args
44
53
  rescue => e
45
54
  class_or_instance_method_separater = jp.instance_method? ? "#" : "."
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}\": "
55
+ context_message = error_message_prefix + "Exception raised while executing \"#{jp.context.advice_kind}\" advice for \"#{jp.type_or_object.inspect}#{class_or_instance_method_separater}#{jp.method_name}\": "
47
56
  backtrace = e.backtrace
48
57
  e2 = e.exception(context_message + e.message + " (join_point = #{jp.inspect})")
49
58
  e2.set_backtrace backtrace
50
59
  raise e2
51
60
  end
52
61
  end
53
-
62
+ protected :do_call
63
+
54
64
  # Supports Enumerable
55
65
  def each
56
66
  node = self
@@ -59,7 +69,14 @@ module Aquarium
59
69
  node = node.next_node
60
70
  end
61
71
  end
62
-
72
+
73
+ def last
74
+ last_node = nil
75
+ each { |node| last_node = node unless node.nil? } #; p "<br/>last_node: #{last_node.inspect .gsub(/\</,"&lt;").gsub(/\>/,"&gt;")}"}
76
+ # p "<br/><br/>returning last_node: #{last_node.inspect .gsub(/\</,"&lt;").gsub(/\>/,"&gt;")}"
77
+ last_node
78
+ end
79
+
63
80
  def size
64
81
  inject(0) {|memo, node| memo += 1}
65
82
  end
@@ -73,6 +90,12 @@ module Aquarium
73
90
  end
74
91
 
75
92
  NIL_OBJECT = Aquarium::Utils::NilObject.new
93
+
94
+ protected
95
+
96
+ def invoking_object join_point
97
+ join_point.instance_method? ? join_point.context.advised_object : join_point.target_type
98
+ end
76
99
  end
77
100
 
78
101
  # When invoking the original method, we use object.send(original_method_name, *args)
@@ -82,13 +105,12 @@ module Aquarium
82
105
  # Note that we extract the block passed to the original method call, if any,
83
106
  # from the context and pass it to method invocation.
84
107
  def initialize options = {}
85
- super(options) { |jp, *args|
108
+ super(options) { |jp, obj, *args|
86
109
  block_for_method = jp.context.block_for_method
87
- invoking_object = jp.instance_method? ? jp.context.advised_object : jp.target_type
88
- method = invoking_object.method(@alias_method_name)
110
+ method = invoking_object(jp).method(@alias_method_name)
89
111
  block_for_method.nil? ?
90
- invoking_object.send(@alias_method_name, *args) :
91
- invoking_object.send(@alias_method_name, *args, &block_for_method)
112
+ invoking_object(jp).send(@alias_method_name, *args) :
113
+ invoking_object(jp).send(@alias_method_name, *args, &block_for_method)
92
114
  # Buggy!!
93
115
  # method = invoking_object.method(@alias_method_name)
94
116
  # block_for_method.nil? ?
@@ -100,20 +122,20 @@ module Aquarium
100
122
 
101
123
  class BeforeAdviceChainNode < AdviceChainNode
102
124
  def initialize options = {}
103
- super(options) { |jp, *args|
125
+ super(options) { |jp, obj, *args|
104
126
  before_jp = jp.make_current_context_join_point :advice_kind => :before
105
- advice.call(before_jp, *args)
106
- next_node.call(jp, *args)
127
+ advice.call(before_jp, obj, *args)
128
+ next_node.call(jp, obj, *args)
107
129
  }
108
130
  end
109
131
  end
110
132
 
111
133
  class AfterReturningAdviceChainNode < AdviceChainNode
112
134
  def initialize options = {}
113
- super(options) { |jp, *args|
114
- returned_value = next_node.call(jp, *args)
135
+ super(options) { |jp, obj, *args|
136
+ returned_value = next_node.call(jp, obj, *args)
115
137
  next_jp = jp.make_current_context_join_point :advice_kind => :after_returning, :returned_value => returned_value
116
- advice.call(next_jp, *args)
138
+ advice.call(next_jp, obj, *args)
117
139
  next_jp.context.returned_value # allow advice to modify the returned value
118
140
  }
119
141
  end
@@ -124,13 +146,13 @@ module Aquarium
124
146
  class AfterRaisingAdviceChainNode < AdviceChainNode
125
147
  include Aquarium::Utils::ArrayUtils
126
148
  def initialize options = {}
127
- super(options) { |jp, *args|
149
+ super(options) { |jp, obj, *args|
128
150
  begin
129
- next_node.call(jp, *args)
151
+ next_node.call(jp, obj, *args)
130
152
  rescue Object => raised_exception
131
153
  if after_raising_exceptions_list_includes raised_exception
132
154
  next_jp = jp.make_current_context_join_point :advice_kind => :after_raising, :raised_exception => raised_exception
133
- advice.call(next_jp, *args)
155
+ advice.call(next_jp, obj, *args)
134
156
  raised_exception = next_jp.context.raised_exception # allow advice to modify raised exception
135
157
  end
136
158
  raise raised_exception
@@ -151,17 +173,17 @@ module Aquarium
151
173
 
152
174
  class AfterAdviceChainNode < AdviceChainNode
153
175
  def initialize options = {}
154
- super(options) { |jp, *args|
176
+ super(options) { |jp, obj, *args|
155
177
  # advice.call is invoked in each bloc, rather than once in an "ensure" clause, so the invocation in the rescue class
156
178
  # can allow the advice to change the exception that will be raised.
157
179
  begin
158
- returned_value = next_node.call(jp, *args)
180
+ returned_value = next_node.call(jp, obj, *args)
159
181
  next_jp = jp.make_current_context_join_point :advice_kind => :after, :returned_value => returned_value
160
- advice.call(next_jp, *args)
182
+ advice.call(next_jp, obj, *args)
161
183
  next_jp.context.returned_value # allow advice to modify the returned value
162
184
  rescue Object => raised_exception
163
185
  next_jp = jp.make_current_context_join_point :advice_kind => :after, :raised_exception => raised_exception
164
- advice.call(next_jp, *args)
186
+ advice.call(next_jp, obj, *args)
165
187
  raise next_jp.context.raised_exception
166
188
  end
167
189
  }
@@ -170,9 +192,9 @@ module Aquarium
170
192
 
171
193
  class AroundAdviceChainNode < AdviceChainNode
172
194
  def initialize options = {}
173
- super(options) { |jp, *args|
195
+ super(options) { |jp, obj, *args|
174
196
  around_jp = jp.make_current_context_join_point :advice_kind => :around, :proceed_proc => next_node
175
- advice.call(around_jp, *args)
197
+ advice.call(around_jp, obj, *args)
176
198
  }
177
199
  end
178
200
  end
@@ -1,4 +1,5 @@
1
1
  require 'aquarium/extensions'
2
+ require 'aquarium/finders/type_finder'
2
3
  require 'aquarium/utils'
3
4
  require 'aquarium/aspects/advice'
4
5
  require 'aquarium/aspects/exclusion_handler'
@@ -30,12 +31,15 @@ module Aquarium
30
31
  attr_accessor :verbose, :log
31
32
  attr_reader :specification, :pointcuts, :advice
32
33
 
34
+ OTHER_ALLOWED_OPTIONS_SINGULAR = %w[type_and_descendents type_and_ancestors exclude_type_and_descendents exclude_type_and_ancestors].map {|o| o.intern}
35
+ OTHER_ALLOWED_OPTIONS_PLURAL = %w[types_and_descendents types_and_ancestors exclude_types_and_descendents exclude_types_and_ancestors].map {|o| o.intern}
36
+
33
37
  ALLOWED_OPTIONS_SINGULAR = %w[advice type object method attribute method_option attribute_option pointcut default_object
34
38
  exclude_type exclude_object exclude_pointcut exclude_join_point exclude_method exclude_attribute noop].map {|o| o.intern}
35
39
 
36
- ALLOWED_OPTIONS_PLURAL = ALLOWED_OPTIONS_SINGULAR.map {|o| "#{o}s".intern}
40
+ ALLOWED_OPTIONS_PLURAL = OTHER_ALLOWED_OPTIONS_PLURAL + ALLOWED_OPTIONS_SINGULAR.map {|o| "#{o.to_s}s".intern}
37
41
 
38
- ALLOWED_OPTIONS = ALLOWED_OPTIONS_SINGULAR + ALLOWED_OPTIONS_PLURAL
42
+ ALLOWED_OPTIONS = ALLOWED_OPTIONS_PLURAL + ALLOWED_OPTIONS_SINGULAR + OTHER_ALLOWED_OPTIONS_SINGULAR
39
43
 
40
44
  ADVICE_OPTIONS_SYNONYMS_MAP = {
41
45
  :call => :advice,
@@ -44,33 +48,38 @@ module Aquarium
44
48
  }
45
49
 
46
50
  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,
51
+ :type => :types,
52
+ :type_and_descendents => :types_and_descendents,
53
+ :type_and_ancestors => :types_and_ancestors,
54
+ :within_type => :types,
55
+ :within_types => :types,
56
+ :object => :objects,
57
+ :within_object => :objects,
58
+ :within_objects => :objects,
59
+ :method => :methods,
60
+ :within_method => :methods,
61
+ :within_methods => :methods,
62
+ :attribute => :attributes,
63
+ :pointcut => :pointcuts,
64
+ :within_pointcut => :pointcuts,
65
+ :within_pointcuts => :pointcuts,
66
+ :exclude_type => :exclude_types,
67
+ :exclude_type_and_descendents => :exclude_types_and_descendents,
68
+ :exclude_type_and_ancestors => :exclude_types_and_ancestors,
69
+ :exclude_object => :exclude_objects,
70
+ :exclude_pointcut => :exclude_pointcuts,
71
+ :exclude_join_point => :exclude_join_points,
72
+ :exclude_method => :exclude_methods,
73
+ :exclude_attribute => :exclude_attributes,
66
74
  }
67
75
 
68
76
  # Aspect.new (:around | :before | :after | :after_returning | :after_raising ) \
69
77
  # (:pointcuts => [...]), | \
70
- # ((:types => [...] | :objects => [...]),
78
+ # ((:types => [...] | :types_and_ancestors => [...] | :types_and_descendents => [...] \
79
+ # :objects => [...]),
71
80
  # :methods => [], :method_options => [...], \
72
81
  # :attributes => [...], :attribute_options[...]), \
73
- # (:advice = advice | do |join_point, *args| ...; end)
82
+ # (:advice = advice | do |join_point, obj, *args| ...; end)
74
83
  #
75
84
  # where the parameters often have many synonyms (mostly to support a "humane
76
85
  # interface") and they are interpreted as followed:
@@ -109,6 +118,13 @@ module Aquarium
109
118
  # All the :types, :objects, :methods, :attributes, :method_options, and :attribute_options
110
119
  # are used to construct Pointcuts internally.
111
120
  #
121
+ # <tt>:types_and_descendents => type || [type_list]</tt>::
122
+ # <tt>:type_and_descendents => type || [type_list]</tt>::
123
+ # <tt>:types_and_ancestors => type || [type_list]</tt>::
124
+ # <tt>:type_and_ancestors => type || [type_list]</tt>::
125
+ # One or an array of types and either their descendents or ancestors.
126
+ # If you want both the descendents _and_ ancestors, use both options.
127
+ #
112
128
  # <tt>:objects => object || [object_list]</tt>::
113
129
  # <tt>:object => object || [object_list]</tt>::
114
130
  # <tt>:within_object => object || [object_list]</tt>::
@@ -164,6 +180,16 @@ module Aquarium
164
180
  # <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
165
181
  # Exclude the specified "things" from the matched join points.
166
182
  #
183
+ # <tt>:exclude_types_and_descendents => type || [type_list]</tt>::
184
+ # <tt>:exclude_type_and_descendents => type || [type_list]</tt>::
185
+ # <tt>:exclude_types_and_ancestors => type || [type_list]</tt>::
186
+ # <tt>:exclude_type_and_ancestors => type || [type_list]</tt>::
187
+ # Exclude the specified types and their descendents, ancestors.
188
+ # If you want to exclude both the descendents _and_ ancestors, use both options.
189
+ #
190
+ # The actual advice to execute is the block or you can pass a Proc using :advice => proc.
191
+ # Note that the advice takes a join_point argument, which will include a non-nil
192
+ # JoinPoint#Context object, the object being executed, and the argument list to the method.
167
193
  def initialize *options, &block
168
194
  process_input options, &block
169
195
  init_pointcuts
@@ -218,15 +244,22 @@ module Aquarium
218
244
  opts = rationalize_parameters options.flatten.dup
219
245
  # For non-hash inputs, use an empty string for the value
220
246
  @specification = Aquarium::Utils::MethodUtils.method_args_to_hash(*opts) {|option| ""}
221
- use_default_object_if_defined unless (types_given? || objects_given? || pointcuts_given?)
247
+ use_default_object_if_defined unless some_type_or_pc_option_given?
222
248
  use_first_nonadvice_symbol_as_method(opts) unless methods_given?
249
+ calculate_excluded_types
223
250
  @advice = determine_advice block
224
- if @advice.nil? && @specification[:noop].nil?
225
- bad_options "No advice block nor :advice argument was given."
226
- end
227
251
  validate_specification
228
252
  end
229
253
 
254
+ def calculate_excluded_types
255
+ type_finder_options = {}
256
+ %w[types types_and_ancestors types_and_descendents].each do |opt|
257
+ type_finder_options[opt.intern] = @specification["exclude_#{opt}".intern] if @specification["exclude_#{opt}".intern]
258
+ end
259
+ excluded_types = Aquarium::Finders::TypeFinder.new.find type_finder_options
260
+ @specification[:exclude_types_calculated] = excluded_types.matched.keys
261
+ end
262
+
230
263
  def determine_advice block
231
264
  # There can be only one advice; take the last one...
232
265
  block || (@specification[:advice].kind_of?(Array) ? @specification[:advice].last : @specification[:advice])
@@ -322,8 +355,8 @@ module Aquarium
322
355
  # Useful for debugging...
323
356
  def self.advice_chain_inspect advice_chain
324
357
  return "[nil]" if advice_chain.nil?
325
- "<br/>"+advice_chain.inspect do |advice_chain|
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}"
358
+ "<br/>"+advice_chain.inspect do |ac|
359
+ "#{ac.class.name}:#{ac.object_id}: join_point = #{ac.static_join_point}: aspect = #{ac.aspect.object_id}, next_node = #{advice_chain_inspect ac.next_node}"
327
360
  end.gsub(/\</,"&lt;").gsub(/\>/,"&gt;")+"<br/>"
328
361
  end
329
362
 
@@ -402,7 +435,7 @@ module Aquarium
402
435
  :advised_object => #{target_self},
403
436
  :parameters => args,
404
437
  :block_for_method => block_for_method)
405
- advice_chain.call advice_join_point, *args
438
+ advice_chain.call advice_join_point, #{target_self}, *args
406
439
  end
407
440
  #{join_point.visibility.to_s} :#{join_point.method_name}
408
441
  private :#{alias_method_name}
@@ -495,14 +528,16 @@ module Aquarium
495
528
  "_aspect_"
496
529
  end
497
530
 
531
+ def some_type_or_pc_option_given?
532
+ pointcuts_given? or some_type_option_given? or objects_given? #or default_objects_given?
533
+ end
534
+
535
+ def some_type_option_given?
536
+ types_given? or types_and_ancestors_given? or types_and_descendents_given?
537
+ end
538
+
498
539
  def self.determine_type_or_object join_point
499
540
  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
506
541
  end
507
542
 
508
543
  def self.make_type_or_object_key join_point
@@ -553,15 +588,21 @@ module Aquarium
553
588
  bad_options(":after can't be used with :after_returning.") if after_given_with? :after_returning
554
589
  bad_options(":after can't be used with :after_raising.") if after_given_with? :after_raising
555
590
  bad_options(":after_returning can't be used with :after_raising.") if after_returning_given_with? :after_raising
556
- unless pointcuts_given? or types_given? or objects_given? or default_objects_given?
557
- bad_options("At least one of :pointcut(s), :type(s), :object(s) is required.")
591
+ unless some_type_or_pc_option_given?
592
+ bad_options("At least one of :pointcut(s), :type(s), :type(s)_with_ancestors, :type(s)_with_descendents, :object(s) is required.")
558
593
  end
559
- if pointcuts_given? and (types_given? or objects_given?)
594
+ if pointcuts_given? and (some_type_option_given? or objects_given?)
560
595
  bad_options("Can't specify both :pointcut(s) and one or more of :type(s), and/or :object(s).")
561
596
  end
562
597
  @specification.each_key do |parameter|
563
598
  check_parameter parameter
564
599
  end
600
+ if @advice.nil? && @specification[:noop].nil?
601
+ bad_options "No advice block nor :advice argument was given."
602
+ end
603
+ if @advice.arity == -2
604
+ bad_options "It appears that your advice parameter list is the obsolete format |jp, *args|. The correct format is |jp, object, *args|"
605
+ end
565
606
  end
566
607
 
567
608
  def check_parameter parameter
@@ -571,6 +612,7 @@ module Aquarium
571
612
  def is_valid_parameter key
572
613
  ALLOWED_OPTIONS.include?(key) || ALLOWED_OPTIONS_SYNONYMS_MAP.keys.include?(key) ||
573
614
  ADVICE_OPTIONS_SYNONYMS_MAP.keys.include?(key) || KINDS_IN_PRIORITY_ORDER.include?(key) ||
615
+ key == :exclude_types_calculated ||
574
616
  parameter_is_a_method_name?(key) # i.e., use_first_nonadvice_symbol_as_method
575
617
  end
576
618
 
@@ -25,7 +25,7 @@ module Aquarium
25
25
  %w[after after_returning after_raising].each do |after_kind|
26
26
  module_eval(<<-AFTER, __FILE__, __LINE__)
27
27
  def before_and_#{after_kind} *options, &block
28
- advise(:before, :#{after_kind}, *options, &block)
28
+ advise :before, :#{after_kind}, *options, &block
29
29
  end
30
30
  AFTER
31
31
  end
@@ -28,8 +28,8 @@ module Aquarium
28
28
  unless @specification[:exclude_objects].nil?
29
29
  return true if @specification[:exclude_objects].include?(type_or_object)
30
30
  end
31
- unless @specification[:exclude_types].nil?
32
- return true if @specification[:exclude_types].find do |t|
31
+ unless @specification[:exclude_types_calculated].nil?
32
+ return true if @specification[:exclude_types_calculated].find do |t|
33
33
  case t
34
34
  when String: type_or_object.name.eql?(t)
35
35
  when Symbol: type_or_object.name.eql?(t.to_s)
@@ -8,6 +8,9 @@ module Aquarium
8
8
  module Aspects
9
9
  class JoinPoint
10
10
 
11
+ class ProceedMethodNotAvailable < Exception; end
12
+ class ContextNotDefined < Exception; end
13
+
11
14
  class Context
12
15
  attr_accessor :advice_kind, :advised_object, :parameters, :block_for_method, :returned_value, :raised_exception, :proceed_proc
13
16
 
@@ -26,12 +29,20 @@ module Aquarium
26
29
  end
27
30
 
28
31
  def proceed enclosing_join_point, *args, &block
29
- raise "It looks like you tried to call \"JoinPoint#proceed\" (or \"JoinPoint::Context#proceed\") from within advice that isn't \"around\" advice. Only around advice can call proceed. (Specific error: JoinPoint#proceed cannot be called because no \"@proceed_proc\" attribute was set on the corresponding JoinPoint::Context object.)" unless @proceed_proc
32
+ raise ProceedMethodNotAvailable.new("It looks like you tried to call \"JoinPoint#proceed\" (or \"JoinPoint::Context#proceed\") from within advice that isn't \"around\" advice. Only around advice can call proceed. (Specific error: JoinPoint#proceed cannot be called because no \"@proceed_proc\" attribute was set on the corresponding JoinPoint::Context object.)") if @proceed_proc.nil?
33
+ do_invoke :call, enclosing_join_point, *args, &block
34
+ end
35
+
36
+ def invoke_original_join_point enclosing_join_point, *args, &block
37
+ do_invoke :invoke_original_join_point, enclosing_join_point, *args, &block
38
+ end
39
+
40
+ def do_invoke method, enclosing_join_point, *args, &block
30
41
  args = parameters if (args.nil? or args.size == 0)
31
42
  enclosing_join_point.context.block_for_method = block if block
32
- proceed_proc.call enclosing_join_point, *args
43
+ proceed_proc.send method, enclosing_join_point, advised_object, *args
33
44
  end
34
- protected :proceed
45
+ protected :do_invoke
35
46
 
36
47
  alias :to_s :inspect
37
48
 
@@ -68,32 +79,9 @@ module Aquarium
68
79
  end
69
80
  end
70
81
 
71
- %w[type object].each do |attr|
72
- class_eval(<<-EOF, __FILE__, __LINE__)
73
- # Deprecated, as JoinPoint#type overrides Module#type in a non-substitutable way!
74
- # JoinPoint#target_#{attr} will be removed in the next release.
75
- # Use JoinPoint#target_#{attr} instead
76
- def #{attr}
77
- p "WARNING: JoinPoint##{attr} is deprecated. It will be removed in the next release."
78
- target_#{attr}
79
- end
80
- # Deprecated
81
- def #{attr}= new_#{attr}
82
- p "WARNING: JoinPoint##{attr}= is deprecated. It will be removed in the next release."
83
- target_#{attr}= new_#{attr}
84
- end
85
- EOF
86
- end
87
-
88
82
  attr_accessor :target_type, :target_object, :method_name, :visibility, :context
89
83
  attr_reader :instance_or_class_method
90
84
 
91
- # For "symmetry" with JoinPoint#target_type
92
- alias_method :object, :target_object
93
-
94
- # For "symmetry" with JoinPoint#target_type=
95
- alias_method :object=, :target_object=
96
-
97
85
  def instance_method?
98
86
  @instance_method
99
87
  end
@@ -124,14 +112,26 @@ module Aquarium
124
112
  results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object,
125
113
  :method => method_name,
126
114
  :options => [visibility, instance_or_class_method]
127
- raise Exception("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
115
+ raise Aquarium::Utils::LogicError("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
128
116
  return results.matched.size == 1 ? true : false
129
117
  end
130
118
 
119
+ # Invoke the "enclosed" join point, which could be aspect advice wrapping the original runtime join point.
120
+ # This method can only be called if the join point has a context object defined that represents an actual
121
+ # runtime "state".
131
122
  def proceed *args, &block
132
- context.method(:proceed).call self, *args, &block
123
+ raise ContextNotDefined.new(":proceed can't be called unless the join point has a context object.") if context.nil?
124
+ context.proceed self, *args, &block
133
125
  end
134
126
 
127
+ # Invoke the actual runtime join point, skipping any intermediate advice.
128
+ # This method can only be called if the join point has a context object defined that represents an actual
129
+ # runtime "state".
130
+ def invoke_original_join_point *args, &block
131
+ raise ContextNotDefined.new(":invoke_original_join_point can't be called unless the join point has a context object.") if context.nil?
132
+ context.invoke_original_join_point self, *args, &block
133
+ end
134
+
135
135
  def make_current_context_join_point context_options
136
136
  new_jp = dup
137
137
  if new_jp.context.nil?