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.
- data/Aquarium-IDEA.ipr +252 -0
- data/Aquarium-IDEA.iws +493 -0
- data/Aquarium.ipr +1 -1
- data/Aquarium.iws +133 -138
- data/CHANGES +63 -0
- data/ParseTreePlay.rb +25 -0
- data/README +55 -3
- data/RELEASE-PLAN +9 -1
- data/TODO.rb +175 -15
- data/examples/aspect_design_example.rb +13 -1
- data/examples/aspect_design_example_spec.rb +20 -2
- data/examples/introductions_example.rb +35 -0
- data/examples/introductions_example_spec.rb +37 -0
- data/examples/method_missing_example.rb +2 -1
- data/lib/aquarium/aspects/advice.rb +127 -74
- data/lib/aquarium/aspects/aspect.rb +139 -72
- data/lib/aquarium/aspects/default_objects_handler.rb +6 -4
- data/lib/aquarium/aspects/exclusion_handler.rb +15 -3
- data/lib/aquarium/aspects/join_point.rb +60 -55
- data/lib/aquarium/aspects/pointcut.rb +153 -124
- data/lib/aquarium/aspects/pointcut_composition.rb +1 -1
- data/lib/aquarium/dsl/aspect_dsl.rb +13 -5
- data/lib/aquarium/dsl/object_dsl.rb +4 -2
- data/lib/aquarium/extras/design_by_contract.rb +9 -5
- data/lib/aquarium/finders.rb +1 -0
- data/lib/aquarium/finders/finder_result.rb +13 -5
- data/lib/aquarium/finders/method_finder.rb +75 -70
- data/lib/aquarium/finders/pointcut_finder.rb +166 -0
- data/lib/aquarium/finders/type_finder.rb +104 -62
- data/lib/aquarium/utils/array_utils.rb +1 -1
- data/lib/aquarium/utils/invalid_options.rb +2 -0
- data/lib/aquarium/utils/name_utils.rb +3 -2
- data/lib/aquarium/utils/nil_object.rb +7 -3
- data/lib/aquarium/utils/options_utils.rb +38 -27
- data/lib/aquarium/utils/set_utils.rb +2 -2
- data/lib/aquarium/utils/type_utils.rb +11 -0
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/advice_spec.rb +147 -32
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +252 -43
- data/spec/aquarium/aspects/aspect_spec.rb +148 -88
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +40 -34
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +39 -3
- data/spec/aquarium/aspects/join_point_spec.rb +190 -227
- data/spec/aquarium/aspects/pointcut_spec.rb +24 -1
- data/spec/aquarium/dsl/aspect_dsl_spec.rb +17 -17
- data/spec/aquarium/finders/method_finder_spec.rb +8 -2
- data/spec/aquarium/finders/pointcut_finder_spec.rb +193 -0
- data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +90 -0
- data/spec/aquarium/finders/type_finder_spec.rb +17 -0
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +4 -4
- data/spec/aquarium/finders/type_finder_with_nested_types.rb +47 -0
- data/spec/aquarium/utils/nil_object_spec.rb +21 -0
- data/spec/aquarium/utils/type_utils_sample_nested_types.rb +51 -0
- data/spec/aquarium/utils/type_utils_spec.rb +18 -1
- metadata +13 -3
@@ -3,10 +3,12 @@ require 'set'
|
|
3
3
|
|
4
4
|
module Aquarium
|
5
5
|
module Aspects
|
6
|
-
# Some classes
|
7
|
-
# object is specified.
|
8
|
-
#
|
9
|
-
#
|
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
|
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
|
14
|
-
|
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
|
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, :
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
47
|
-
|
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
|
-
|
52
|
-
|
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
|
-
|
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 =
|
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 :
|
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
|
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
|
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
|
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
|
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
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# <tt>:
|
39
|
-
# <tt>:
|
40
|
-
# <tt>:
|
41
|
-
# <tt>:
|
42
|
-
# <tt>:
|
43
|
-
#
|
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
|
-
#
|
46
|
-
#
|
47
|
-
# <tt>:
|
48
|
-
# <tt>:
|
49
|
-
# <tt>:
|
50
|
-
# <tt>:
|
51
|
-
# <tt>:
|
52
|
-
# <tt>:
|
53
|
-
#
|
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
|
-
#
|
56
|
-
#
|
57
|
-
# <
|
58
|
-
#
|
59
|
-
# <tt>:
|
60
|
-
# <tt>:
|
61
|
-
# <tt>:
|
62
|
-
# <tt>:
|
63
|
-
# <tt>:
|
64
|
-
# <tt>:
|
65
|
-
# <tt>:
|
66
|
-
# <tt>:
|
67
|
-
# <tt>:
|
68
|
-
# <tt>:
|
69
|
-
#
|
70
|
-
#
|
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
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
# <tt>:
|
76
|
-
# <tt>:
|
77
|
-
# <tt>:
|
78
|
-
# <tt>:
|
79
|
-
# <tt>:
|
80
|
-
#
|
81
|
-
#
|
82
|
-
# <tt>:
|
83
|
-
# <tt>:
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
# <tt>:
|
90
|
-
# <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
|
-
#
|
104
|
-
#
|
105
|
-
#
|
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
|
-
#
|
108
|
-
#
|
109
|
-
# <tt>:
|
110
|
-
# <tt>:
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
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
|
-
#
|
120
|
-
# <tt>:
|
121
|
-
#
|
122
|
-
#
|
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
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
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
|
-
#
|
137
|
-
# <tt>:
|
138
|
-
# <tt>:
|
139
|
-
#
|
140
|
-
# <tt>:
|
141
|
-
#
|
142
|
-
# <tt>:
|
143
|
-
#
|
144
|
-
# <tt>:
|
145
|
-
# <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
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
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
|
290
|
-
# types into names, then "refind" them. While less efficient, it makes
|
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
|