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