aquarium 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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