aquarium 0.4.1 → 0.4.2

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 (55) hide show
  1. data/Aquarium-IDEA.ipr +252 -0
  2. data/Aquarium-IDEA.iws +493 -0
  3. data/Aquarium.ipr +1 -1
  4. data/Aquarium.iws +133 -138
  5. data/CHANGES +63 -0
  6. data/ParseTreePlay.rb +25 -0
  7. data/README +55 -3
  8. data/RELEASE-PLAN +9 -1
  9. data/TODO.rb +175 -15
  10. data/examples/aspect_design_example.rb +13 -1
  11. data/examples/aspect_design_example_spec.rb +20 -2
  12. data/examples/introductions_example.rb +35 -0
  13. data/examples/introductions_example_spec.rb +37 -0
  14. data/examples/method_missing_example.rb +2 -1
  15. data/lib/aquarium/aspects/advice.rb +127 -74
  16. data/lib/aquarium/aspects/aspect.rb +139 -72
  17. data/lib/aquarium/aspects/default_objects_handler.rb +6 -4
  18. data/lib/aquarium/aspects/exclusion_handler.rb +15 -3
  19. data/lib/aquarium/aspects/join_point.rb +60 -55
  20. data/lib/aquarium/aspects/pointcut.rb +153 -124
  21. data/lib/aquarium/aspects/pointcut_composition.rb +1 -1
  22. data/lib/aquarium/dsl/aspect_dsl.rb +13 -5
  23. data/lib/aquarium/dsl/object_dsl.rb +4 -2
  24. data/lib/aquarium/extras/design_by_contract.rb +9 -5
  25. data/lib/aquarium/finders.rb +1 -0
  26. data/lib/aquarium/finders/finder_result.rb +13 -5
  27. data/lib/aquarium/finders/method_finder.rb +75 -70
  28. data/lib/aquarium/finders/pointcut_finder.rb +166 -0
  29. data/lib/aquarium/finders/type_finder.rb +104 -62
  30. data/lib/aquarium/utils/array_utils.rb +1 -1
  31. data/lib/aquarium/utils/invalid_options.rb +2 -0
  32. data/lib/aquarium/utils/name_utils.rb +3 -2
  33. data/lib/aquarium/utils/nil_object.rb +7 -3
  34. data/lib/aquarium/utils/options_utils.rb +38 -27
  35. data/lib/aquarium/utils/set_utils.rb +2 -2
  36. data/lib/aquarium/utils/type_utils.rb +11 -0
  37. data/lib/aquarium/version.rb +1 -1
  38. data/spec/aquarium/aspects/advice_spec.rb +147 -32
  39. data/spec/aquarium/aspects/aspect_invocation_spec.rb +252 -43
  40. data/spec/aquarium/aspects/aspect_spec.rb +148 -88
  41. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +40 -34
  42. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +39 -3
  43. data/spec/aquarium/aspects/join_point_spec.rb +190 -227
  44. data/spec/aquarium/aspects/pointcut_spec.rb +24 -1
  45. data/spec/aquarium/dsl/aspect_dsl_spec.rb +17 -17
  46. data/spec/aquarium/finders/method_finder_spec.rb +8 -2
  47. data/spec/aquarium/finders/pointcut_finder_spec.rb +193 -0
  48. data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +90 -0
  49. data/spec/aquarium/finders/type_finder_spec.rb +17 -0
  50. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +4 -4
  51. data/spec/aquarium/finders/type_finder_with_nested_types.rb +47 -0
  52. data/spec/aquarium/utils/nil_object_spec.rb +21 -0
  53. data/spec/aquarium/utils/type_utils_sample_nested_types.rb +51 -0
  54. data/spec/aquarium/utils/type_utils_spec.rb +18 -1
  55. metadata +13 -3
@@ -3,10 +3,12 @@ require 'set'
3
3
 
4
4
  module Aquarium
5
5
  module Aspects
6
- # Some classes and modules support a :default_objects flag and use it if no type or
7
- # object is specified. For "convenience", requires that classes and modules including
8
- # this module have a hash @specification defined with keys :default_objects, :types,
9
- # and :objects.
6
+ # Some classes support a <tt>:default_objects</tt> option and use it if no type or
7
+ # object is specified. In other words, the <tt>:default_objects</tt> option is ignored
8
+ # if <tt>:types</tt> or <tt>:objects</tt> is present.
9
+ # This module handles this behavior for all the classes that include it. These classes
10
+ # are assumed to have <tt>@specification</tt> defined with keys
11
+ # <tt>:default_objects</tt>, <tt>:types</tt>, and <tt>:objects</tt>.
10
12
  module DefaultObjectsHandler
11
13
  include Aquarium::Utils::ArrayUtils
12
14
 
@@ -1,22 +1,33 @@
1
+ require 'aquarium/utils/set_utils'
1
2
 
2
3
  module Aquarium
3
4
  module Aspects
4
5
 
5
- # Defines methods shared by several classes that take :exclude_* arguments.
6
+ # Defines methods shared by several classes that take <tt>:exclude_*</tt> arguments.
6
7
  module ExclusionHandler
8
+ include Aquarium::Utils::HashUtils
7
9
 
8
10
  def join_point_excluded? jp
9
11
  is_excluded_pointcut?(jp) or is_excluded_join_point?(jp) or is_excluded_type_or_object?(jp.type_or_object) or is_excluded_method?(jp.method_name)
10
12
  end
11
13
 
12
14
  def is_excluded_pointcut? jp
13
- return false if @specification[:exclude_pointcuts].nil?
14
- @specification[:exclude_pointcuts].find do |pc|
15
+ return false if all_excluded_pointcuts.empty?
16
+ all_excluded_pointcuts.find do |pc|
15
17
  pc.join_points_matched.find do |jp2|
16
18
  jp2 == jp || jp2.eql?(jp)
17
19
  end
18
20
  end
19
21
  end
22
+
23
+ def set_calculated_excluded_pointcuts excluded_pointcuts
24
+ @calculated_excluded_pointcuts = excluded_pointcuts
25
+ @all_excluded_pointcuts = @specification[:exclude_pointcuts] | Set.new(@calculated_excluded_pointcuts)
26
+ end
27
+
28
+ def all_excluded_pointcuts
29
+ @all_excluded_pointcuts ||= @specification[:exclude_pointcuts]
30
+ end
20
31
 
21
32
  # Using @specification[:exclude_join_points].include?(jp) doesn't always work correctly (it probably uses equal?())!
22
33
  def is_excluded_join_point? jp
@@ -56,6 +67,7 @@ module Aquarium
56
67
  return false if regexs.empty?
57
68
  regexs.find {|re| method.to_s =~ re}
58
69
  end
70
+
59
71
  end
60
72
  end
61
73
  end
@@ -1,4 +1,5 @@
1
1
  require 'aquarium/utils'
2
+ require 'aquarium/aspects/advice'
2
3
 
3
4
  def bad_attributes message, options
4
5
  raise Aquarium::Utils::InvalidOptions.new("Invalid attributes. " + message + ". Options were: #{options.inspect}")
@@ -6,57 +7,54 @@ end
6
7
 
7
8
  module Aquarium
8
9
  module Aspects
10
+ # == JoinPoint
11
+ # Encapsulates information about a Join Point that might be advised. JoinPoint objects are <i>almost</i>
12
+ # value objects; you can change the context object.
13
+ # TODO Separate out the read-only part from the variable part. This might require an API change!
9
14
  class JoinPoint
10
15
 
11
16
  class ProceedMethodNotAvailable < Exception; end
12
- class ContextNotDefined < Exception; end
17
+ class ContextNotCorrectlyDefined < Exception; end
13
18
 
19
+ # == JoinPoint::Context
20
+ # Encapsulates current runtime context information for a join point, such as the values of method parameters, a raised
21
+ # exception (for <tt>:after</tt> or <tt>after_raising</tt> advice), <i>etc.</i>
22
+ # Context objects are <i>partly</i> value objects.
23
+ # TODO Separate out the read-only part from the variable part. This might require an API change!
14
24
  class Context
15
- attr_accessor :advice_kind, :advised_object, :parameters, :block_for_method, :returned_value, :raised_exception, :proceed_proc, :current_advice_node
25
+ attr_accessor :advice_kind, :advised_object, :parameters, :proceed_proc, :current_advice_node
26
+ attr_accessor :returned_value, :raised_exception, :block_for_method
16
27
 
17
28
  alias :target_object :advised_object
18
- alias :target_object= :advised_object=
19
29
 
20
- # Create a join point object. It must have one and only type _or_ object and one method or the special keywords <tt>:all</tt>.
21
- # Usage:
22
- # join_point = JoinPoint.new.find :type => ..., :method_name => ... [, (:class_method | :instance_method) => (true | false) ]
23
- # where
24
- # <tt>:type => type_or_type_name_or_regexp</tt>::
25
- # A single type, type name or regular expression matching only one type. One and only one
26
- # type _or_ object is required. An error is raised otherwise.
27
- #
28
- # <tt>:method_name => method_name_or_sym</tt>::
29
- # <tt>:method => method_name_or_sym</tt>::
30
- # A single method name or symbol. Only one is allowed, although the special flag <tt>:all</tt> is allowed.
31
- #
32
- # <tt>(:class_method | :instance_method) => (true | false)</tt>::
33
- # Is the method a class or instance method? Defaults to <tt>:instance_method => true</tt>.
34
- def initialize options
30
+ NIL_OBJECT = Aquarium::Utils::NilObject.new
31
+
32
+ def initialize options = {}
35
33
  update options
36
- assert_valid options
37
34
  end
38
35
 
39
36
  def update options
40
37
  options.each do |key, value|
41
38
  instance_variable_set "@#{key}", value
42
39
  end
40
+ @advice_kind ||= Advice::UNKNOWN_ADVICE_KIND
41
+ @advised_object ||= NIL_OBJECT
42
+ @parameters ||= []
43
43
  end
44
-
44
+
45
45
  def proceed enclosing_join_point, *args, &block
46
- 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?
47
- do_invoke proceed_proc, :call, enclosing_join_point, *args, &block
46
+ 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 invoked because no \"@proceed_proc\" attribute was set on the corresponding JoinPoint::Context object.)") if @proceed_proc.nil?
47
+ enclosing_join_point.context.parameters = args unless args.nil? or args.empty?
48
+ enclosing_join_point.context.block_for_method = block if block
49
+ proceed_proc.call enclosing_join_point
48
50
  end
49
51
 
50
52
  def invoke_original_join_point enclosing_join_point, *args, &block
51
- do_invoke current_advice_node, :invoke_original_join_point, enclosing_join_point, *args, &block
52
- end
53
-
54
- def do_invoke proc_to_send, method, enclosing_join_point, *args, &block
55
- args = parameters if (args.nil? or args.size == 0)
53
+ raise ContextNotCorrectlyDefined.new("It looks like you tried to call \"JoinPoint#invoke_original_join_point\" (or \"JoinPoint::Context#invoke_original_join_point\") using a join point without a completely formed context object. (Specific error: The original join point cannot be invoked because no \"@current_advice_node\" attribute was set on the corresponding JoinPoint::Context object.)") if @current_advice_node.nil?
54
+ enclosing_join_point.context.parameters = args unless args.nil? or args.empty?
56
55
  enclosing_join_point.context.block_for_method = block if block
57
- proc_to_send.send method, enclosing_join_point, advised_object, *args
56
+ current_advice_node.invoke_original_join_point enclosing_join_point
58
57
  end
59
- protected :do_invoke
60
58
 
61
59
  alias :to_s :inspect
62
60
 
@@ -66,7 +64,7 @@ module Aquarium
66
64
  return 1 if other.nil?
67
65
  result = self.class <=> other.class
68
66
  return result unless result == 0
69
- result = (self.advice_kind.nil? and other.advice_kind.nil?) ? 0 : self.advice_kind <=> other.advice_kind
67
+ result = Advice.compare_advice_kinds self.advice_kind, other.advice_kind
70
68
  return result unless result == 0
71
69
  result = (self.advised_object.object_id.nil? and other.advised_object.object_id.nil?) ? 0 : self.advised_object.object_id <=> other.advised_object.object_id
72
70
  return result unless result == 0
@@ -84,17 +82,10 @@ module Aquarium
84
82
  alias :== :eql?
85
83
  alias :=== :eql?
86
84
 
87
- protected
88
-
89
- def assert_valid options
90
- bad_attributes("Must specify an :advice_kind", options) unless advice_kind
91
- bad_attributes("Must specify an :advised_object", options) unless advised_object
92
- bad_attributes("Must specify a :parameters", options) unless parameters
93
- end
94
85
  end
95
86
 
96
- attr_accessor :target_type, :target_object, :method_name, :visibility, :context
97
- attr_reader :instance_or_class_method
87
+ attr_accessor :context
88
+ attr_reader :target_type, :target_object, :method_name, :visibility, :instance_or_class_method
98
89
 
99
90
  def instance_method?
100
91
  @instance_method
@@ -104,6 +95,23 @@ module Aquarium
104
95
  !@instance_method
105
96
  end
106
97
 
98
+ # Create a join point object, specifying either one type or one object and a method.
99
+ # Only method join points are currently supported by Aquarium.
100
+ #
101
+ # The supported options are
102
+ # <tt>:type => type | type_name | type_name_regexp</tt>::
103
+ # A single type, type name or regular expression matching only one type. One and only one
104
+ # type _or_ object is required. An error is raised otherwise.
105
+ # <tt>:object => object</tt>::
106
+ # A single object. One and only one type _or_ object is required. An error is raised otherwise.
107
+ # <tt>:method_name | :method => method_name_or_symbol</tt>::
108
+ # A single method name or symbol. Only one is allowed, although the special flag <tt>:all</tt>
109
+ # is allowed, as long as only one method will be found, subject to the next option.
110
+ # <tt>:class_method | :instance_method => true | false</tt>::
111
+ # Is the method a class or instance method? Defaults to <tt>:instance_method => true</tt>.
112
+ #
113
+ # Note: The range of options is not as rich as for Pointcut, because it is expected that JoinPoint objects
114
+ # will be explicitly created only rarely by users of Aquarium. Most of the time, Pointcuts will be created.
107
115
  def initialize options = {}
108
116
  @target_type = resolve_type options
109
117
  @target_object = options[:object]
@@ -112,9 +120,16 @@ module Aquarium
112
120
  @instance_method = options[:instance_method].nil? ? (!class_method) : options[:instance_method]
113
121
  @instance_or_class_method = @instance_method ? :instance : :class
114
122
  @visibility = Aquarium::Utils::MethodUtils.visibility(type_or_object, @method_name, class_or_instance_method_flag)
123
+ @context = options[:context] || JoinPoint::Context.new
115
124
  assert_valid options
116
125
  end
117
126
 
127
+ def dup
128
+ jp = super
129
+ jp.context = @context.dup unless @context.nil?
130
+ jp
131
+ end
132
+
118
133
  def type_or_object
119
134
  target_type || target_object
120
135
  end
@@ -130,34 +145,23 @@ module Aquarium
130
145
  return results.matched.size == 1 ? true : false
131
146
  end
132
147
 
133
- # Invoke the "enclosed" join point, which could be aspect advice wrapping the original runtime join point.
148
+ # Invoke the join point itself (which could actually be aspect advice wrapping the original join point...).
134
149
  # This method can only be called if the join point has a context object defined that represents an actual
135
150
  # runtime "state".
136
151
  def proceed *args, &block
137
- raise ContextNotDefined.new(":proceed can't be called unless the join point has a context object.") if context.nil?
152
+ raise ContextNotCorrectlyDefined.new(":proceed can't be called unless the join point has a context object.") if context.nil?
138
153
  context.proceed self, *args, &block
139
154
  end
140
155
 
141
- # Invoke the actual runtime join point, skipping any intermediate advice.
156
+ # Invoke the join point itself, skipping any intermediate advice.
142
157
  # This method can only be called if the join point has a context object defined that represents an actual
143
158
  # runtime "state".
159
+ # Use this method cautiously, at it could be "surprising" if some advice is not executed!
144
160
  def invoke_original_join_point *args, &block
145
- raise ContextNotDefined.new(":invoke_original_join_point can't be called unless the join point has a context object.") if context.nil?
161
+ raise ContextNotCorrectlyDefined.new(":invoke_original_join_point can't be called unless the join point has a context object.") if context.nil?
146
162
  context.invoke_original_join_point self, *args, &block
147
163
  end
148
164
 
149
- def make_current_context_join_point context_options
150
- new_jp = dup
151
- if new_jp.context.nil?
152
- new_jp.context = JoinPoint::Context.new context_options
153
- else
154
- new_jp.context = context.dup
155
- new_jp.context.update context_options
156
- end
157
- new_jp
158
- end
159
-
160
- # Needed for comparing this field in #compare_field
161
165
  def instance_method
162
166
  @instance_method
163
167
  end
@@ -238,6 +242,7 @@ module Aquarium
238
242
 
239
243
  public
240
244
 
245
+ # A "convenience" JoinPoint supporting the "Null Object Pattern."
241
246
  NIL_OBJECT = Aquarium::Utils::NilObject.new
242
247
  end
243
248
  end
@@ -26,139 +26,168 @@ module Aquarium
26
26
  attr_reader :specification
27
27
 
28
28
  # Construct a Pointcut for methods in types or objects.
29
- # Pointcut.new :join_points => [...] | :type{s} => [...] | :object{s} => [...] \
30
- # {, :method{s} => [], :method_options => [...], \
29
+ # Pointcut.new :join_points => [...] | :type{s} => [...] | :object{s} => [...]
30
+ # {, :method{s} => [], :method_options => [...],
31
31
  # :attribute{s} => [...], :attribute_options[...]}
32
32
  # where the "{}" indicate optional elements. Most of the arguments have many
33
33
  # synonyms, shown below, to promote an English-like DSL.
34
34
  #
35
- # <tt>:join_points => join_point || [join_point_list]</tt>::
36
- # <tt>:join_point => join_point || [join_point_list]</tt>::
37
- # <tt>:for_join_points => join_point || [join_point_list]</tt>::
38
- # <tt>:for_join_point => join_point || [join_point_list]</tt>::
39
- # <tt>:on_join_points => join_point || [join_point_list]</tt>::
40
- # <tt>:on_join_point => join_point || [join_point_list]</tt>::
41
- # <tt>:within_join_points => join_point || [join_point_list]</tt>::
42
- # <tt>:within_join_point => join_point || [join_point_list]</tt>::
43
- # One or an array of join_points.
35
+ # The options include the following.
36
+ # ==== Join Points
37
+ # Specify one or an array of join_points.
38
+ # * <tt>:join_points => join_point || [join_point_list]</tt>
39
+ # * <tt>:join_point => join_point || [join_point_list]</tt>
40
+ # * <tt>:for_join_points => join_point || [join_point_list]</tt>
41
+ # * <tt>:for_join_point => join_point || [join_point_list]</tt>
42
+ # * <tt>:on_join_points => join_point || [join_point_list]</tt>
43
+ # * <tt>:on_join_point => join_point || [join_point_list]</tt>
44
+ # * <tt>:within_join_points => join_point || [join_point_list]</tt>
45
+ # * <tt>:within_join_point => join_point || [join_point_list]</tt>
44
46
  #
45
- # <tt>:types => type || [type_list]</tt>::
46
- # <tt>:type => type || [type_list]</tt>::
47
- # <tt>:for_types => type || [type_list]</tt>::
48
- # <tt>:for_type => type || [type_list]</tt>::
49
- # <tt>:on_types => type || [type_list]</tt>::
50
- # <tt>:on_type => type || [type_list]</tt>::
51
- # <tt>:within_types => type || [type_list]</tt>::
52
- # <tt>:within_type => type || [type_list]</tt>::
53
- # One or an array of types, type names and/or type regular expessions to match.
47
+ # ===== Types
48
+ # Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.)
49
+ # * <tt>:types => type || [type_list]</tt>
50
+ # * <tt>:type => type || [type_list]</tt>
51
+ # * <tt>:for_types => type || [type_list]</tt>
52
+ # * <tt>:for_type => type || [type_list]</tt>
53
+ # * <tt>:on_types => type || [type_list]</tt>
54
+ # * <tt>:on_type => type || [type_list]</tt>
55
+ # * <tt>:within_types => type || [type_list]</tt>
56
+ # * <tt>:within_type => type || [type_list]</tt>
54
57
  #
55
- # <tt>:types_and_descendents => type || [type_list]</tt>::
56
- # <tt>:type_and_descendents => type || [type_list]</tt>::
57
- # <tt>:types_and_ancestors => type || [type_list]</tt>::
58
- # <tt>:type_and_ancestors => type || [type_list]</tt>::
59
- # <tt>:for_types_and_ancestors => type || [type_list]</tt>::
60
- # <tt>:for_type_and_ancestors => type || [type_list]</tt>::
61
- # <tt>:on_types_and_descendents => type || [type_list]</tt>::
62
- # <tt>:on_type_and_descendents => type || [type_list]</tt>::
63
- # <tt>:on_types_and_ancestors => type || [type_list]</tt>::
64
- # <tt>:on_type_and_ancestors => type || [type_list]</tt>::
65
- # <tt>:within_types_and_descendents => type || [type_list]</tt>::
66
- # <tt>:within_type_and_descendents => type || [type_list]</tt>::
67
- # <tt>:within_types_and_ancestors => type || [type_list]</tt>::
68
- # <tt>:within_type_and_ancestors => type || [type_list]</tt>::
69
- # One or an array of types and either their descendents or ancestors.
70
- # If you want both the descendents _and_ ancestors, use both options.
58
+ # ===== Types and Ancestors or Descendents
59
+ # Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.)
60
+ # The ancestors or descendents will also be found. To find <i>both</i> ancestors and descendents, use
61
+ # both options.
62
+ # * <tt>:types_and_descendents => type || [type_list]</tt>
63
+ # * <tt>:type_and_descendents => type || [type_list]</tt>
64
+ # * <tt>:types_and_ancestors => type || [type_list]</tt>
65
+ # * <tt>:type_and_ancestors => type || [type_list]</tt>
66
+ # * <tt>:for_types_and_ancestors => type || [type_list]</tt>
67
+ # * <tt>:for_type_and_ancestors => type || [type_list]</tt>
68
+ # * <tt>:on_types_and_descendents => type || [type_list]</tt>
69
+ # * <tt>:on_type_and_descendents => type || [type_list]</tt>
70
+ # * <tt>:on_types_and_ancestors => type || [type_list]</tt>
71
+ # * <tt>:on_type_and_ancestors => type || [type_list]</tt>
72
+ # * <tt>:within_types_and_descendents => type || [type_list]</tt>
73
+ # * <tt>:within_type_and_descendents => type || [type_list]</tt>
74
+ # * <tt>:within_types_and_ancestors => type || [type_list]</tt>
75
+ # * <tt>:within_type_and_ancestors => type || [type_list]</tt>
71
76
  #
72
- # <tt>:objects => object || [object_list]</tt>::
73
- # <tt>:object => object || [object_list]</tt>::
74
- # <tt>:for_objects => object || [object_list]</tt>::
75
- # <tt>:for_object => object || [object_list]</tt>::
76
- # <tt>:on_objects => object || [object_list]</tt>::
77
- # <tt>:on_object => object || [object_list]</tt>::
78
- # <tt>:within_objects => object || [object_list]</tt>::
79
- # <tt>:within_object => object || [object_list]</tt>::
80
- # Objects to match.
81
- #
82
- # <tt>:default_objects => object || [object_list]</tt>::
83
- # <tt>:default_object => object || [object_list]</tt>::
84
- # An "internal" flag used by AspectDSL#pointcut when no object or type is specified,
85
- # the value of :default_objects will be used, if defined. AspectDSL#pointcut sets the
86
- # value to self, so that the user doesn't have to in the appropriate contexts.
87
- # This flag is subject to change, so don't use it explicitly!
88
- #
89
- # <tt>:methods => method || [method_list]</tt>::
90
- # <tt>:method => method || [method_list]</tt>::
91
- # <tt>:within_methods => method || [method_list]</tt>::
92
- # <tt>:within_method => method || [method_list]</tt>::
93
- # <tt>:calling => method || [method_list]</tt>::
94
- # <tt>:calls_to => method || [method_list]</tt>::
95
- # <tt>:invoking => method || [method_list]</tt>::
96
- # <tt>:invocations_of => method || [method_list]</tt>::
97
- # <tt>:sending_message_to => method || [method_list]</tt>::
98
- # One or an array of methods, method names and/or method regular expessions to match.
99
- # By default, unless :attributes are specified, searches for public instance methods
100
- # with the method option :exclude_ancestor_methods implied, unless explicit method
101
- # options are given.
77
+ # ===== Types and Nested Types
78
+ # Specify a type, type name, type name regular expression or an array of the same. (Mixed is allowed.)
79
+ # The nested (enclosed) types will also be found.
80
+ # * <tt>:types_and_nested_types => type || [type_list]</tt>
81
+ # * <tt>:type_and_nested_types => type || [type_list]</tt>
82
+ # * <tt>:types_and_nested => type || [type_list]</tt>
83
+ # * <tt>:type_and_nested => type || [type_list]</tt>
84
+ # * <tt>:for_types_and_nested_types => type || [type_list]</tt>
85
+ # * <tt>:for_type_and_nested_types => type || [type_list]</tt>
86
+ # * <tt>:for_types_and_nested => type || [type_list]</tt>
87
+ # * <tt>:for_type_and_nested => type || [type_list]</tt>
88
+ # * <tt>:on_types_and_nested_types => type || [type_list]</tt>
89
+ # * <tt>:on_type_and_nested_types => type || [type_list]</tt>
90
+ # * <tt>:on_types_and_nested => type || [type_list]</tt>
91
+ # * <tt>:on_type_and_nested => type || [type_list]</tt>
92
+ # * <tt>:within_types_and_nested_types => type || [type_list]</tt>
93
+ # * <tt>:within_type_and_nested_types => type || [type_list]</tt>
94
+ # * <tt>:within_types_and_nested => type || [type_list]</tt>
95
+ # * <tt>:within_type_and_nested => type || [type_list]</tt>
102
96
  #
103
- # <tt>:method_options => [options]</tt>::
104
- # One or more options supported by Aquarium::Finders::MethodFinder. The :exclude_ancestor_methods
105
- # option is most useful.
97
+ # ===== Objects
98
+ # * <tt>:objects => object || [object_list]</tt>
99
+ # * <tt>:object => object || [object_list]</tt>
100
+ # * <tt>:for_objects => object || [object_list]</tt>
101
+ # * <tt>:for_object => object || [object_list]</tt>
102
+ # * <tt>:on_objects => object || [object_list]</tt>
103
+ # * <tt>:on_object => object || [object_list]</tt>
104
+ # * <tt>:within_objects => object || [object_list]</tt>
105
+ # * <tt>:within_object => object || [object_list]</tt>
106
+ #
107
+ # ===== "Default" Objects
108
+ # An "internal" flag used by Aspect::DSL#pointcut. When no object or type is specified
109
+ # explicitly, the value of :default_objects will be used, if defined. Aspect::DSL#pointcut
110
+ # sets the value to +self+, so the user doesn't have to specify a type or object in the
111
+ # contexts where that would be useful, <i>e.g.,</i> pointcuts defined within a type for join points
112
+ # within itself. *WARNING*: This flag is subject to change, so don't use it explicitly!
113
+ # * <tt>:default_objects => object || [object_list]</tt>
114
+ # * <tt>:default_object => object || [object_list]</tt>
106
115
  #
107
- # <tt>:reading => attribute || [attribute_list]</tt>::
108
- # <tt>:writing => attribute || [attribute_list]</tt>::
109
- # <tt>:changing => attribute || [attribute_list]</tt>::
110
- # <tt>:accessing => attribute || [attribute_list]</tt>::
111
- # One or an array of attribute names and/or regular expessions to match.
112
- # This is syntactic sugar for the corresponding attribute readers and/or writers
113
- # methods.
114
- # If <tt>:reading</tt> is specified, just attribute readers are matched.
115
- # If <tt>:writing</tt> is specified, just attribute writers are matched.
116
- # If <tt>:accessing</tt> is specified, both readers and writers are matched.
117
- # Any matches will be joined with the matched <tt>:methods.</tt>.
116
+ # ===== Methods
117
+ # A method name, name regular expession or an array of the same.
118
+ # By default, if neither <tt>:methods</tt> nor <tt>:attributes</tt> are specified, all public instance methods
119
+ # will be found, with the method option <tt>:exclude_ancestor_methods</tt> implied, unless explicit method
120
+ # options are given.
121
+ # * <tt>:methods => method || [method_list]</tt>
122
+ # * <tt>:method => method || [method_list]</tt>
123
+ # * <tt>:within_methods => method || [method_list]</tt>
124
+ # * <tt>:within_method => method || [method_list]</tt>
125
+ # * <tt>:calling => method || [method_list]</tt>
126
+ # * <tt>:calls_to => method || [method_list]</tt>
127
+ # * <tt>:invoking => method || [method_list]</tt>
128
+ # * <tt>:invocations_of => method || [method_list]</tt>
129
+ # * <tt>:sending_message_to => method || [method_list]</tt>
118
130
  #
119
- # <tt>:attributes => attribute || [attribute_list]</tt>::
120
- # <tt>:attribute => attribute || [attribute_list]</tt>::
121
- # One or an array of attribute names and/or regular expessions to match.
122
- # This is syntactic sugar for the corresponding attribute readers and/or writers
123
- # methods, as specified using the <tt>:attrbute_options</tt>. Any matches will be
124
- # joined with the matched <tt>:methods.</tt>.
131
+ # ===== Method Options
132
+ # One or more options supported by Aquarium::Finders::MethodFinder. The <tt>:exclude_ancestor_methods</tt>
133
+ # option is most useful.
134
+ # * <tt>:method_options => [options]</tt>
125
135
  #
126
- # <tt>:attribute_options => [options]</tt>::
127
- # One or more of <tt>:readers</tt>, <tt>:reader</tt> (synonymous),
128
- # <tt>:writers</tt>, and/or <tt>:writer</tt> (synonymous). By default, both
129
- # readers and writers are matched.
130
- # <tt>:reading => ...</tt> is synonymous with <tt>:attributes => ...,
131
- # :attribute_options => [:readers]</tt>.
132
- # <tt>:writing => ...</tt> and <tt>:changing => ...</tt> are synonymous with <tt>:attributes => ...,
133
- # :attribute_options => [:writers]</tt>.
134
- # <tt>:accessing => ...</tt> is synonymous with <tt>:attributes => ...</tt>.
136
+ # ===== Attributes
137
+ # An attribute name, regular expession or array of the same.
138
+ # *WARNING* This is syntactic sugar for the corresponding attribute readers and/or writers
139
+ # methods. The actual attribute accesses are not advised, which can lead to unexpected
140
+ # behavior. A goal before V1.0 is to support actual attribute accesses, if possible.
141
+ # * <tt>:attributes => attribute || [attribute_list]</tt>
142
+ # * <tt>:attribute => attribute || [attribute_list]</tt>
143
+ # * <tt>:reading => attribute || [attribute_list]</tt>
144
+ # * <tt>:writing => attribute || [attribute_list]</tt>
145
+ # * <tt>:changing => attribute || [attribute_list]</tt>
146
+ # * <tt>:accessing => attribute || [attribute_list]</tt>
147
+ # If <tt>:reading</tt> is specified, just attribute readers are matched.
148
+ # If <tt>:writing</tt> is specified, just attribute writers are matched.
149
+ # If <tt>:accessing</tt> is specified, both readers and writers are matched.
150
+ # Any matches will be joined with the matched <tt>:methods.</tt>.
135
151
  #
136
- # <tt>:exclude_pointcuts => pc || [pc_list]</tt>::
137
- # <tt>:exclude_pointcut => pc || [pc_list]</tt>::
138
- # <tt>:exclude_join_points => jp || [jp_list]</tt>::
139
- # <tt>:exclude_join_point => jp || [jp_list]</tt>::
140
- # <tt>:exclude_types => type || [type_list]</tt>::
141
- # <tt>:exclude_types => type || [type_list]</tt>::
142
- # <tt>:exclude_type => type || [type_list]</tt>::
143
- # <tt>:exclude_objects => object || [object_list]</tt>::
144
- # <tt>:exclude_object => object || [object_list]</tt>::
145
- # <tt>:exclude_methods => method || [method_list]</tt>::
146
- # <tt>:exclude_method => method || [method_list]</tt>::
147
- # <tt>:exclude_attributes => attribute || [attribute_list]</tt>::
148
- # <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
149
- # Also <tt>exclude_{synonyms}</tt> of the same options...
150
- # Exclude the specified "things" from the matched join points. If pointcuts are
151
- # excluded, they should be subsets of the matched pointcuts. Otherwise, the
152
- # resulting pointcut will be empty!
152
+ # ===== Attribute Options
153
+ # One or more of <tt>:readers</tt>, <tt>:reader</tt> (synonymous),
154
+ # <tt>:writers</tt>, and/or <tt>:writer</tt> (synonymous). By default, both
155
+ # readers and writers are matched.
156
+ # <tt>:reading => ...</tt> is synonymous with <tt>:attributes => ...,
157
+ # :attribute_options => [:readers]</tt>.
158
+ # <tt>:writing => ...</tt> and <tt>:changing => ...</tt> are synonymous with <tt>:attributes => ...,
159
+ # :attribute_options => [:writers]</tt>.
160
+ # <tt>:accessing => ...</tt> is synonymous with <tt>:attributes => ...</tt>.
161
+ # * <tt>:attribute_options => [options]</tt>
153
162
  #
154
- # <tt>:exclude_types_and_descendents => type || [type_list]</tt>::
155
- # <tt>:exclude_type_and_descendents => type || [type_list]</tt>::
156
- # <tt>:exclude_types_and_ancestors => type || [type_list]</tt>::
157
- # <tt>:exclude_type_and_ancestors => type || [type_list]</tt>::
158
- # Exclude the specified types and their descendents, ancestors.
159
- # If you want to exclude both the descendents _and_ ancestors, use both options.
163
+ # ==== Exclusion Options
164
+ # Exclude the specified "things" from the matched join points. If pointcuts are
165
+ # excluded, they should be subsets of the matched pointcuts. Otherwise, the
166
+ # resulting pointcut will be empty!
167
+ # * <tt>:exclude_pointcuts => pc || [pc_list]</tt>
168
+ # * <tt>:exclude_pointcut => pc || [pc_list]</tt>
169
+ # * <tt>:exclude_join_points => jp || [jp_list]</tt>
170
+ # * <tt>:exclude_join_point => jp || [jp_list]</tt>
171
+ # * <tt>:exclude_types => type || [type_list]</tt>
172
+ # * <tt>:exclude_types => type || [type_list]</tt>
173
+ # * <tt>:exclude_type => type || [type_list]</tt>
174
+ # * <tt>:exclude_types_and_descendents => type || [type_list]</tt>
175
+ # * <tt>:exclude_type_and_descendents => type || [type_list]</tt>
176
+ # * <tt>:exclude_types_and_ancestors => type || [type_list]</tt>
177
+ # * <tt>:exclude_type_and_ancestors => type || [type_list]</tt>
178
+ # * <tt>:exclude_types_and_nested_types => type || [type_list]</tt>
179
+ # * <tt>:exclude_type_and_nested_types => type || [type_list]</tt>
180
+ # * <tt>:exclude_types_and_nested => type || [type_list]</tt>
181
+ # * <tt>:exclude_type_and_nested => type || [type_list]</tt>
182
+ # * <tt>:exclude_objects => object || [object_list]</tt>
183
+ # * <tt>:exclude_object => object || [object_list]</tt>
184
+ # * <tt>:exclude_methods => method || [method_list]</tt>
185
+ # * <tt>:exclude_method => method || [method_list]</tt>
186
+ # * <tt>:exclude_attributes => attribute || [attribute_list]</tt>
187
+ # * <tt>:exclude_attribute => attribute || [attribute_list]</tt>
188
+ # The <tt>exclude_</tt> prefix works with the synonyms of the options shown.
160
189
  #
161
- # Pointcut.new also accepts all the "universal" options documented in OptionsUtils.
190
+ # Pointcut.new also accepts all the "universal" options documented in Aquarium::Utils::OptionsUtils.
162
191
  def initialize options = {}
163
192
  init_specification options, CANONICAL_OPTIONS, (ATTRIBUTE_OPTIONS_VALUES + Advice::KINDS_IN_PRIORITY_ORDER) do
164
193
  finish_specification_initialization
@@ -231,7 +260,6 @@ module Aquarium
231
260
  result
232
261
  end
233
262
 
234
- # TODO remove duplication w/ aspect.rb
235
263
  def finish_specification_initialization
236
264
  @specification.merge! Pointcut.make_attribute_reading_writing_options(@original_options)
237
265
  # Map the method options to their canonical values:
@@ -246,7 +274,7 @@ module Aquarium
246
274
  end
247
275
 
248
276
  def any_type_related_options_given?
249
- objects_given? or join_points_given? or types_given? or types_and_descendents_given? or types_and_ancestors_given?
277
+ objects_given? or join_points_given? or types_given? or types_and_descendents_given? or types_and_ancestors_given? or types_and_nested_types_given?
250
278
  end
251
279
 
252
280
  def self.validate_attribute_options spec_hash, options_hash
@@ -285,9 +313,10 @@ module Aquarium
285
313
  finder_options = {}
286
314
  exclude_finder_options = {}
287
315
  ['', 'exclude_'].each do |prefix|
288
- ['', '_and_ancestors', '_and_descendents'].each do |suffix|
289
- # Because the user might be asking for descendents and/or ancestors, we convert explicitly-specified
290
- # types into names, then "refind" them. While less efficient, it makes the code more uniform.
316
+ ['', '_and_ancestors', '_and_descendents', '_and_nested_types'].each do |suffix|
317
+ # Because the user might be asking for descendents, ancestors and/or nested types, we convert
318
+ # explicitly-specified types into names, then "refind" them. While less efficient, it makes
319
+ # the code more uniform.
291
320
  eval <<-EOF
292
321
  #{prefix}type_regexps_or_names#{suffix} = @specification[:#{prefix}types#{suffix}].map do |t|
293
322
  Aquarium::Utils::TypeUtils.is_type?(t) ? t.name : t