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
@@ -2,7 +2,7 @@ require 'aquarium/aspects/pointcut'
|
|
2
2
|
require 'aquarium/utils/array_utils'
|
3
3
|
|
4
4
|
# == Pointcut (composition)
|
5
|
-
# Since Pointcuts are queries, they can be composed,
|
5
|
+
# Since Pointcuts are queries, they can be composed, <i>i.e.,</i> unions and intersections of
|
6
6
|
# them can be computed, yielding new Pointcuts.
|
7
7
|
class Aquarium::Aspects::Pointcut
|
8
8
|
|
@@ -1,10 +1,6 @@
|
|
1
1
|
require 'aquarium/aspects/aspect'
|
2
2
|
require 'aquarium/utils/type_utils'
|
3
3
|
|
4
|
-
# Convenience methods added to the current type to provide a low-level AOP DSL.
|
5
|
-
# If you don't want these methods added to a type, then only require aspect.rb
|
6
|
-
# and create instances of Aspect.
|
7
|
-
|
8
4
|
module Aquarium
|
9
5
|
module DSLMethods
|
10
6
|
|
@@ -54,6 +50,18 @@ module Aquarium
|
|
54
50
|
end
|
55
51
|
end
|
56
52
|
|
53
|
+
# Include this module to add convenience class-level methods to a type, which provide
|
54
|
+
# a low-level AOP DSL. For example, instead of writing
|
55
|
+
#
|
56
|
+
# Aspect.new :around, :calls_to => ...
|
57
|
+
#
|
58
|
+
# You can write the following instead.
|
59
|
+
#
|
60
|
+
# include Aspect::DSL
|
61
|
+
# ...
|
62
|
+
# around :calls_to => ...
|
63
|
+
# If you don't want these methods added to a type, then only require aspect.rb
|
64
|
+
# and create instances of Aspect, as in the first example.
|
57
65
|
module DSL
|
58
66
|
include Aquarium::DSLMethods
|
59
67
|
# Add the methods as class, not instance, methods.
|
@@ -62,7 +70,7 @@ module Aquarium
|
|
62
70
|
end
|
63
71
|
end
|
64
72
|
|
65
|
-
# Backwards compatibility with old name.
|
73
|
+
# Backwards compatibility with old name. (Deprecated)
|
66
74
|
module Aspects
|
67
75
|
module DSL
|
68
76
|
module AspectDSL
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'aquarium/dsl/aspect_dsl'
|
2
2
|
|
3
|
-
# Add
|
4
|
-
# file if you really want these methods on all objects in your runtime!
|
3
|
+
# Add the Aquarium::DSL convenience methods to Object. Only require this
|
4
|
+
# file if you <i>really</i> want these methods on all objects in your runtime!
|
5
|
+
# For example, Rails already adds <tt>before</tt> and <tt>after</tt> methods
|
6
|
+
# to object, so including this file is not compatible!
|
5
7
|
class Object
|
6
8
|
include Aquarium::DSL
|
7
9
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
-
# A simple Design by Contract module. Adds advice to test that the contract, which is specified with
|
2
|
-
# a block passes. Note that it doesn't attempt to handle the correct behavior under contract
|
3
|
-
# inheritance.
|
4
|
-
# Warning: This module automatically includes Aquarium::DSL into the class with
|
5
|
-
# the contract and it adds the :precondition, :postcondition, and the :invariant methods to Object!
|
6
1
|
require 'aquarium'
|
7
2
|
|
8
3
|
module Aquarium
|
4
|
+
# == Extras
|
5
|
+
# These modules are <i>not</i> included automatically when you <tt>require 'aquarium'</tt>. You have to
|
6
|
+
# include them explicitly.
|
9
7
|
module Extras
|
8
|
+
# A simple Design by Contract module. Adds advice to test that the contract, which is specified with
|
9
|
+
# a block passes. Note that it doesn't attempt to handle the correct behavior under contract
|
10
|
+
# inheritance. A usage example is included in the Examples as part of the distribution and it is also
|
11
|
+
# shown on the web site.
|
12
|
+
# *Warning*: This module automatically includes Aquarium::DSL into the class with
|
13
|
+
# the contract and it adds the :precondition, :postcondition, and the :invariant methods to Object!
|
10
14
|
module DesignByContract
|
11
15
|
include Aquarium::Aspects
|
12
16
|
|
data/lib/aquarium/finders.rb
CHANGED
@@ -3,6 +3,9 @@ require 'aquarium/extensions'
|
|
3
3
|
|
4
4
|
module Aquarium
|
5
5
|
module Finders
|
6
|
+
# == FinderResult
|
7
|
+
# Wraps hashes that hold the results of various *Finder utilities. The #not_matched method returns specified
|
8
|
+
# inputs that did not result in a successful find.
|
6
9
|
class FinderResult
|
7
10
|
include Aquarium::Utils::HashUtils
|
8
11
|
include Aquarium::Utils::SetUtils
|
@@ -32,12 +35,12 @@ module Aquarium
|
|
32
35
|
|
33
36
|
NIL_OBJECT = FinderResult.new unless const_defined?(:NIL_OBJECT)
|
34
37
|
|
35
|
-
# Convenience method to get the keys for the
|
38
|
+
# Convenience method to get the keys for the matches.
|
36
39
|
def matched_keys
|
37
40
|
@matched.keys
|
38
41
|
end
|
39
42
|
|
40
|
-
# Convenience method to get the keys for the items that did not
|
43
|
+
# Convenience method to get the keys for the items that did not result in matches.
|
41
44
|
def not_matched_keys
|
42
45
|
@not_matched.keys
|
43
46
|
end
|
@@ -49,8 +52,12 @@ module Aquarium
|
|
49
52
|
end
|
50
53
|
|
51
54
|
# "Or" two results together
|
55
|
+
#--
|
56
|
+
# We use dup here and in other methods, rather than FinderResult.new, so that new subclass
|
57
|
+
# objects are returned correctly!
|
58
|
+
#++
|
52
59
|
def or other_result
|
53
|
-
result =
|
60
|
+
result = dup
|
54
61
|
result.matched = hash_union(matched, other_result.matched)
|
55
62
|
result.not_matched = hash_union(not_matched, other_result.not_matched)
|
56
63
|
result
|
@@ -61,7 +68,7 @@ module Aquarium
|
|
61
68
|
|
62
69
|
# "And" two results together
|
63
70
|
def and other_result
|
64
|
-
result =
|
71
|
+
result = dup
|
65
72
|
result.matched = hash_intersection(matched, other_result.matched)
|
66
73
|
result.not_matched = hash_intersection(not_matched, other_result.not_matched)
|
67
74
|
result
|
@@ -71,7 +78,7 @@ module Aquarium
|
|
71
78
|
alias :& :and
|
72
79
|
|
73
80
|
def minus other_result
|
74
|
-
result =
|
81
|
+
result = dup
|
75
82
|
result.matched = matched - other_result.matched
|
76
83
|
result.not_matched = not_matched - other_result.not_matched
|
77
84
|
result
|
@@ -101,6 +108,7 @@ module Aquarium
|
|
101
108
|
|
102
109
|
alias :to_s :inspect
|
103
110
|
|
111
|
+
# Were there no matches?
|
104
112
|
def empty?
|
105
113
|
matched.empty?
|
106
114
|
end
|
@@ -9,91 +9,92 @@ require File.dirname(__FILE__) + '/finder_result'
|
|
9
9
|
# Find methods and types and objects.
|
10
10
|
module Aquarium
|
11
11
|
module Finders
|
12
|
+
# == MethodFinder
|
13
|
+
# Locate methods in specified types or objects.
|
12
14
|
class MethodFinder
|
13
15
|
include Aquarium::Utils::ArrayUtils
|
14
16
|
include Aquarium::Utils::OptionsUtils
|
15
17
|
|
16
|
-
# Returns a Aquarium::Finders::FinderResult
|
17
|
-
# and
|
18
|
+
# Returns a Aquarium::Finders::FinderResult, where the "matched" keys are the input
|
19
|
+
# types, type names, and/or regular expressions, and objects for which matches were found and the
|
20
|
+
# corresponding values are the method name <i>symbols</i> that were found.
|
18
21
|
# Method names, not method objects, are always returned, because we can only get
|
19
22
|
# method objects for instance methods if we have an instance!
|
23
|
+
# The keys in the "not_matched" part of the FinderResult are the specified types and objects
|
24
|
+
# for which no matches were found.
|
20
25
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
26
|
+
# The options are as follows:
|
27
|
+
# ==== Types
|
28
|
+
# All the options supported by TypeFinder#find.
|
24
29
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# <tt>:
|
30
|
-
# <tt>:
|
31
|
-
# <tt>:
|
32
|
-
# <tt>:
|
33
|
-
# <tt>:
|
34
|
-
# <tt>:
|
35
|
-
#
|
36
|
-
#
|
30
|
+
# ==== Objects
|
31
|
+
# One or more objects to match.
|
32
|
+
# Specify one or an array of values.
|
33
|
+
# *Note*: String or symbol objects will be treated as type names!
|
34
|
+
# * <tt>:objects => objects</tt>
|
35
|
+
# * <tt>:object => objects</tt>
|
36
|
+
# * <tt>:for_objects => objects</tt>
|
37
|
+
# * <tt>:for_object => objects</tt>
|
38
|
+
# * <tt>:on_objects => objects</tt>
|
39
|
+
# * <tt>:on_object => objects</tt>
|
40
|
+
# * <tt>:in_objects => objects</tt>
|
41
|
+
# * <tt>:in_object => objects</tt>
|
42
|
+
# * <tt>:within_objects => objects</tt>
|
43
|
+
# * <tt>:within_object => objects</tt>
|
37
44
|
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# <tt>:
|
41
|
-
#
|
42
|
-
# <tt>:
|
43
|
-
#
|
44
|
-
# <tt>:
|
45
|
-
# <tt>:
|
46
|
-
# <tt>:
|
47
|
-
# <tt>:
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
45
|
+
# ==== Methods
|
46
|
+
# One or more method names and/or regular expressions to match.
|
47
|
+
# Specify one or an array of values. If <tt>:all</tt> or <tt>:all_methods</tt>
|
48
|
+
# is specified, all methods in the types or objects will be matched, subject
|
49
|
+
# to the search options described below. That is, <tt>:all</tt> is equivalent
|
50
|
+
# to the regular expression /.+/.
|
51
|
+
# * <tt>:methods => method_names_and_regexps</tt>
|
52
|
+
# * <tt>:method => method_names_and_regexps</tt>
|
53
|
+
# * <tt>:within_methods => method_names_and_regexps</tt>
|
54
|
+
# * <tt>:within_method => method_names_and_regexps</tt>
|
55
|
+
# * <tt>:calling => method_names_and_regexps</tt>
|
56
|
+
# * <tt>:invoking => method_names_and_regexps</tt>
|
57
|
+
# * <tt>:calls_to => method_names_and_regexps</tt>
|
58
|
+
# * <tt>:sending_message_to => method_names_and_regexps</tt>
|
59
|
+
# * <tt>:sending_messages_to => method_names_and_regexps</tt>
|
51
60
|
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# <tt>:
|
55
|
-
# <tt>:
|
56
|
-
# <tt>:
|
57
|
-
# <tt>:
|
58
|
-
# <tt>:calls_to => method_names_and_regexps</tt>::
|
59
|
-
# <tt>:sending_message_to => method_names_and_regexps</tt>::
|
60
|
-
# <tt>:sending_messages_to => method_names_and_regexps</tt>::
|
61
|
-
# One or more method names and regular expressions to match.
|
62
|
-
# Specify one or an array of values. If :all or :all_methods is specified, all
|
63
|
-
# methods will be matched. That is, these special values are equivalent to the
|
64
|
-
# the regular expression /.+/.
|
65
|
-
#
|
66
|
-
# <tt>:exclude_methods => method_names_and_regexps</tt>::
|
67
|
-
# <tt>:exclude_method => method_names_and_regexps</tt>::
|
68
|
-
# <tt>:exclude_<other_method_synonyms> => method_names_and_regexps</tt>::
|
69
|
-
# One or more method names and regular expressions to exclude from the match.
|
70
|
-
# Specify one or an array of values.
|
71
|
-
#
|
72
|
-
# <tt>:method_options => options</tt>::
|
73
|
-
# <tt>:method_option => options</tt>::
|
74
|
-
# <tt>:options => options</tt>::
|
75
|
-
# <tt>:restricting_methods_to => options</tt>::
|
76
|
-
# By default, searches for public instance methods. Specify one or more
|
77
|
-
# of the following options for alternatives. You can combine any of the
|
78
|
-
# <tt>:public</tt>, <tt>:protected</tt>, and <tt>:private</tt>, as well as
|
79
|
-
# <tt>:instance</tt> and <tt>:class</tt>.
|
61
|
+
# ==== Methods Options
|
62
|
+
# By default, the searches are restricted to public instance methods.
|
63
|
+
# * <tt>:method_options => options</tt>
|
64
|
+
# * <tt>:method_option => options</tt>
|
65
|
+
# * <tt>:options => options</tt>
|
66
|
+
# * <tt>:restricting_methods_to => options</tt>
|
80
67
|
#
|
81
|
-
#
|
82
|
-
#
|
68
|
+
# Note, the <tt>:options</tt> synonym is deprecated.
|
69
|
+
#
|
70
|
+
# Here are the allowed "options". Specify one or more of them. Any combination is allowed.
|
71
|
+
# <tt>:public</tt> or <tt>:public_methods</tt>:: Search for public methods (default).
|
72
|
+
# <tt>:private</tt> or <tt>:private_methods</tt>:: Search for private methods.
|
83
73
|
# <tt>:protected</tt> or <tt>:protected_methods</tt>:: Search for protected methods.
|
84
|
-
# <tt>:instance</tt> or <tt>:instance_methods</tt>::
|
85
|
-
# <tt>:class</tt> or <tt>:class_methods</tt>::
|
86
|
-
# <tt>:singleton</tt> or <tt>:singleton_methods</tt>:: Search for singleton methods.
|
87
|
-
#
|
88
|
-
#
|
89
|
-
# <tt>:
|
90
|
-
#
|
91
|
-
#
|
74
|
+
# <tt>:instance</tt> or <tt>:instance_methods</tt>:: Search for instance methods.
|
75
|
+
# <tt>:class</tt> or <tt>:class_methods</tt>:: Search for class methods.
|
76
|
+
# <tt>:singleton</tt> or <tt>:singleton_methods</tt>:: Search for singleton methods.
|
77
|
+
#
|
78
|
+
# Note: specifying <tt>:class</tt> when objects are specified won't work.
|
79
|
+
# Also, <tt>:class</tt>, <tt>:public</tt>, <tt>:protected</tt>, and <tt>:private</tt>
|
80
|
+
# are ignored when looking for singleton methods.
|
81
|
+
#
|
82
|
+
# ==== Excluded Types, Objects, or Methods
|
83
|
+
# Exclude one or more types, objects, or methods from the match.
|
84
|
+
# Specify one or an array of values.
|
85
|
+
# * <tt>:exclude_methods => method_names_and_regexps</tt>
|
86
|
+
# * <tt>:exclude_method => method_names_and_regexps</tt>
|
87
|
+
# * <tt>:exclude_ancestor_methods</tt> - Suppress "ancestor" methods.
|
88
|
+
# The <tt>exclude_</tt> prefix can be used with any of the synonyms for the other options.
|
89
|
+
# *WARNING*: the <tt>:exclude_ancestor_methods</tt> option means that
|
90
|
+
# if you search for a override method +foo+ in a derived class and +foo+ is
|
91
|
+
# defined in the base class, you won't find it!
|
92
92
|
#
|
93
93
|
def find options = {}
|
94
94
|
init_specification options, CANONICAL_OPTIONS do
|
95
95
|
finish_specification_initialization
|
96
96
|
end
|
97
|
+
warn_if_deprecated_options_used options
|
97
98
|
return Aquarium::Finders::FinderResult.new if nothing_to_find?
|
98
99
|
types_and_objects = input_types + input_objects
|
99
100
|
method_names_or_regexps = input_methods
|
@@ -111,7 +112,6 @@ module Aquarium
|
|
111
112
|
|
112
113
|
NIL_OBJECT = MethodFinder.new unless const_defined?(:NIL_OBJECT)
|
113
114
|
|
114
|
-
# TODO remove (or deprecate) the "options" option!
|
115
115
|
METHOD_FINDER_CANONICAL_OPTIONS = {
|
116
116
|
"objects" => %w[object for_object for_objects on_object on_objects in_object in_objects within_object within_objects],
|
117
117
|
"methods" => %w[method within_method within_methods calling invoking invocations_of calls_to sending_message_to sending_messages_to],
|
@@ -171,7 +171,12 @@ module Aquarium
|
|
171
171
|
all_recognized_method_option_symbols.include? sym
|
172
172
|
end
|
173
173
|
|
174
|
-
|
174
|
+
private
|
175
|
+
|
176
|
+
def warn_if_deprecated_options_used options
|
177
|
+
return unless options[:options]
|
178
|
+
logger.warn "The :options => ... option is now deprecated. Please use :method_options instead."
|
179
|
+
end
|
175
180
|
|
176
181
|
def do_find_all_by types_and_objects, method_names_or_regexps
|
177
182
|
types_and_objects = make_array types_and_objects
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'aquarium/utils'
|
2
|
+
require 'aquarium/finders/finder_result'
|
3
|
+
|
4
|
+
# Finds pointcuts by name, either class variables, class constants or both.
|
5
|
+
module Aquarium
|
6
|
+
module Finders
|
7
|
+
# == PointcutFinder
|
8
|
+
# Locate named pointcuts in specified types or objects.
|
9
|
+
class PointcutFinder
|
10
|
+
include Aquarium::Utils::OptionsUtils
|
11
|
+
|
12
|
+
POINTCUT_FINDER_CANONICAL_OPTIONS = {}
|
13
|
+
['', 'constants_', 'class_variables_'].each do |prefix|
|
14
|
+
POINTCUT_FINDER_CANONICAL_OPTIONS["#{prefix}matching"] = ["#{prefix}with_names_matching", "#{prefix}named"]
|
15
|
+
end
|
16
|
+
|
17
|
+
CANONICAL_OPTIONS = Aquarium::Finders::TypeFinder::CANONICAL_OPTIONS.merge(POINTCUT_FINDER_CANONICAL_OPTIONS)
|
18
|
+
canonical_options_given_methods CANONICAL_OPTIONS
|
19
|
+
canonical_option_accessor CANONICAL_OPTIONS
|
20
|
+
|
21
|
+
# Returns a Aquarium::Finders::FinderResult, where the "matched" keys are the input
|
22
|
+
# types, type names, and/or regular expressions, and objects for which matches were found and the
|
23
|
+
# corresponding values are the class constant or variable pointcuts that were found.
|
24
|
+
# The keys in the "not_matched" part of the FinderResult are the specified types and objects
|
25
|
+
# for which no matches were found.
|
26
|
+
#
|
27
|
+
# The options are as follows:
|
28
|
+
# ==== Types
|
29
|
+
# All the options supported by TypeFinder#find.
|
30
|
+
#
|
31
|
+
# ==== Pointcut Names
|
32
|
+
# A name, regular expression or an array of the same. (Mixed allowed.) In the types searched, the
|
33
|
+
# names will be matched against class constants <i>and</i> class variable pointcut definitions.
|
34
|
+
# * <tt>:matching => a variable/constant name, regular expression, or array of the same</tt>
|
35
|
+
# * <tt>:with_names_matching => same</tt>
|
36
|
+
# * <tt>:named => same</tt>
|
37
|
+
#
|
38
|
+
# A name, regular expression or an array of the same that will be matched against pointcuts that
|
39
|
+
# are class constants <i>only</i>.
|
40
|
+
# * <tt>:constants_matching => a variable/constant name, regular expression, or array of the same</tt>
|
41
|
+
# * <tt>:constants_with_names_matching => same</tt>
|
42
|
+
# * <tt>:constants_named => same</tt>
|
43
|
+
#
|
44
|
+
# A name, regular expression or an array of the same that will be matched against pointcuts that
|
45
|
+
# are class variables <i>only</i>.
|
46
|
+
# * <tt>:class_variables_matching => a variable/constant name, regular expression, or array of the same</tt>
|
47
|
+
# * <tt>:class_variables_with_names_matching => same</tt>
|
48
|
+
# * <tt>:class_variables_named => same</tt>
|
49
|
+
#
|
50
|
+
def find options = {}
|
51
|
+
init_specification options, CANONICAL_OPTIONS do
|
52
|
+
finish_specification_initialization
|
53
|
+
end
|
54
|
+
result = do_find_pointcuts unless noop
|
55
|
+
unset_specification
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
class PoincutFinderResult < Aquarium::Finders::FinderResult
|
60
|
+
# Return an array with the found pointcuts.
|
61
|
+
def found_pointcuts
|
62
|
+
matched.values.inject([]) {|pcs, set| pcs += set.to_a; pcs}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
POINTCUT_FINDER_CANONICAL_OPTIONS_KEYS_AS_SYMBOLS = POINTCUT_FINDER_CANONICAL_OPTIONS.keys.map {|k| k.intern}
|
69
|
+
|
70
|
+
def finish_specification_initialization
|
71
|
+
raise Aquarium::Utils::InvalidOptions.new("No options specified") unless any_types_given?
|
72
|
+
end
|
73
|
+
|
74
|
+
# Hack. Since the finder could be reused, unset the specification created by #find.
|
75
|
+
def unset_specification
|
76
|
+
@specification = {}
|
77
|
+
end
|
78
|
+
|
79
|
+
def do_find_pointcuts
|
80
|
+
types_search_result = Aquarium::Finders::TypeFinder.new.find specification_without_pointcut_names
|
81
|
+
return types_search_result if types_search_result.matched.empty?
|
82
|
+
types = types_search_result.matched.keys
|
83
|
+
pointcuts = PoincutFinderResult.new
|
84
|
+
unless any_names_given?
|
85
|
+
pointcuts << find_constant_pointcuts(types)
|
86
|
+
pointcuts << find_class_variable_pointcuts(types)
|
87
|
+
return pointcuts
|
88
|
+
end
|
89
|
+
names = matching_given
|
90
|
+
unless names.empty?
|
91
|
+
pointcuts << find_constant_pointcuts(types, names)
|
92
|
+
pointcuts << find_class_variable_pointcuts(types, names)
|
93
|
+
end
|
94
|
+
names = constants_matching_given
|
95
|
+
unless names.empty?
|
96
|
+
pointcuts << find_constant_pointcuts(types, names)
|
97
|
+
end
|
98
|
+
names = class_variables_matching_given
|
99
|
+
unless names.empty?
|
100
|
+
pointcuts << find_class_variable_pointcuts(types, names)
|
101
|
+
end
|
102
|
+
pointcuts
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_constant_pointcuts types, names = :all
|
106
|
+
pointcuts = PoincutFinderResult.new
|
107
|
+
types.each do |t|
|
108
|
+
candidates = t.constants.select {|c| matches_name(c, names)}
|
109
|
+
candidates.each do |c|
|
110
|
+
if t.const_defined? c
|
111
|
+
con = t.const_get c
|
112
|
+
pointcuts.append_matched({t => con}) if con.kind_of?(Aquarium::Aspects::Pointcut)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
pointcuts
|
117
|
+
end
|
118
|
+
|
119
|
+
def find_class_variable_pointcuts types, names = :all
|
120
|
+
pointcuts = PoincutFinderResult.new
|
121
|
+
types.each do |t|
|
122
|
+
candidates = t.class_variables.select {|c| matches_name(c, to_class_variable_name(names))}
|
123
|
+
candidates.each do |c|
|
124
|
+
con = t.send :class_variable_get, c
|
125
|
+
pointcuts.append_matched({t => con}) if con.kind_of?(Aquarium::Aspects::Pointcut)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
pointcuts
|
129
|
+
end
|
130
|
+
|
131
|
+
def matches_name candidate, names
|
132
|
+
return true if names == :all
|
133
|
+
if names.kind_of? Regexp
|
134
|
+
names.match candidate
|
135
|
+
elsif names.kind_of?(String) or names.kind_of?(Symbol)
|
136
|
+
names.to_s.eql? candidate
|
137
|
+
else
|
138
|
+
names.inject(false) {|matches, name| matches = true if matches_name(candidate, name); matches}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_class_variable_name names
|
143
|
+
return names if names == :all
|
144
|
+
if names.kind_of? Regexp
|
145
|
+
names # no change
|
146
|
+
elsif names.kind_of?(String) or names.kind_of?(Symbol)
|
147
|
+
names.to_s =~ /^@@/ ? names.to_s : "@@#{names}"
|
148
|
+
else
|
149
|
+
names.inject([]) {|result, name| result << to_class_variable_name(name); result}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def any_types_given?
|
154
|
+
types_given? or types_and_descendents_given? or types_and_ancestors_given?
|
155
|
+
end
|
156
|
+
|
157
|
+
def any_names_given?
|
158
|
+
matching_given? or constants_matching_given? or class_variables_matching_given?
|
159
|
+
end
|
160
|
+
|
161
|
+
def specification_without_pointcut_names
|
162
|
+
@specification.reject {|key,v| POINTCUT_FINDER_CANONICAL_OPTIONS_KEYS_AS_SYMBOLS.include?(key)}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|