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
@@ -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
|