aquarium 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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?