aquarium 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/Aquarium-IDEA.ipr +252 -0
  2. data/Aquarium-IDEA.iws +493 -0
  3. data/Aquarium.ipr +1 -1
  4. data/Aquarium.iws +133 -138
  5. data/CHANGES +63 -0
  6. data/ParseTreePlay.rb +25 -0
  7. data/README +55 -3
  8. data/RELEASE-PLAN +9 -1
  9. data/TODO.rb +175 -15
  10. data/examples/aspect_design_example.rb +13 -1
  11. data/examples/aspect_design_example_spec.rb +20 -2
  12. data/examples/introductions_example.rb +35 -0
  13. data/examples/introductions_example_spec.rb +37 -0
  14. data/examples/method_missing_example.rb +2 -1
  15. data/lib/aquarium/aspects/advice.rb +127 -74
  16. data/lib/aquarium/aspects/aspect.rb +139 -72
  17. data/lib/aquarium/aspects/default_objects_handler.rb +6 -4
  18. data/lib/aquarium/aspects/exclusion_handler.rb +15 -3
  19. data/lib/aquarium/aspects/join_point.rb +60 -55
  20. data/lib/aquarium/aspects/pointcut.rb +153 -124
  21. data/lib/aquarium/aspects/pointcut_composition.rb +1 -1
  22. data/lib/aquarium/dsl/aspect_dsl.rb +13 -5
  23. data/lib/aquarium/dsl/object_dsl.rb +4 -2
  24. data/lib/aquarium/extras/design_by_contract.rb +9 -5
  25. data/lib/aquarium/finders.rb +1 -0
  26. data/lib/aquarium/finders/finder_result.rb +13 -5
  27. data/lib/aquarium/finders/method_finder.rb +75 -70
  28. data/lib/aquarium/finders/pointcut_finder.rb +166 -0
  29. data/lib/aquarium/finders/type_finder.rb +104 -62
  30. data/lib/aquarium/utils/array_utils.rb +1 -1
  31. data/lib/aquarium/utils/invalid_options.rb +2 -0
  32. data/lib/aquarium/utils/name_utils.rb +3 -2
  33. data/lib/aquarium/utils/nil_object.rb +7 -3
  34. data/lib/aquarium/utils/options_utils.rb +38 -27
  35. data/lib/aquarium/utils/set_utils.rb +2 -2
  36. data/lib/aquarium/utils/type_utils.rb +11 -0
  37. data/lib/aquarium/version.rb +1 -1
  38. data/spec/aquarium/aspects/advice_spec.rb +147 -32
  39. data/spec/aquarium/aspects/aspect_invocation_spec.rb +252 -43
  40. data/spec/aquarium/aspects/aspect_spec.rb +148 -88
  41. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +40 -34
  42. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +39 -3
  43. data/spec/aquarium/aspects/join_point_spec.rb +190 -227
  44. data/spec/aquarium/aspects/pointcut_spec.rb +24 -1
  45. data/spec/aquarium/dsl/aspect_dsl_spec.rb +17 -17
  46. data/spec/aquarium/finders/method_finder_spec.rb +8 -2
  47. data/spec/aquarium/finders/pointcut_finder_spec.rb +193 -0
  48. data/spec/aquarium/finders/pointcut_finder_spec_test_classes.rb +90 -0
  49. data/spec/aquarium/finders/type_finder_spec.rb +17 -0
  50. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +4 -4
  51. data/spec/aquarium/finders/type_finder_with_nested_types.rb +47 -0
  52. data/spec/aquarium/utils/nil_object_spec.rb +21 -0
  53. data/spec/aquarium/utils/type_utils_sample_nested_types.rb +51 -0
  54. data/spec/aquarium/utils/type_utils_spec.rb +18 -1
  55. metadata +13 -3
@@ -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, _i.e.,_ unions and intersections of
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 aspect convenience methods to Object. Only require this
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
 
@@ -1,3 +1,4 @@
1
1
  require 'aquarium/finders/finder_result'
2
2
  require 'aquarium/finders/type_finder'
3
3
  require 'aquarium/finders/method_finder'
4
+ require 'aquarium/finders/pointcut_finder'
@@ -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 matched.
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 match.
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 = FinderResult.new
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 = FinderResult.new
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 = FinderResult.new
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 for the hash of types, type names, and/or regular expressions
17
- # and the corresponding method name <b>symbols</b> found.
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
- # finder_result = MethodFinder.new.find :types => ... {, :methods => ..., :method_options => [...]}
22
- # where
23
- # "{}" indicate optional arguments
26
+ # The options are as follows:
27
+ # ==== Types
28
+ # All the options supported by TypeFinder#find.
24
29
  #
25
- # <tt>:types => types_and_type_names_and_regexps</tt>::
26
- # <tt>:type => types_and_type_names_and_regexps</tt>::
27
- # <tt>:for_types => types_and_type_names_and_regexps</tt>::
28
- # <tt>:for_type => types_and_type_names_and_regexps</tt>::
29
- # <tt>:on_types => types_and_type_names_and_regexps</tt>::
30
- # <tt>:on_type => types_and_type_names_and_regexps</tt>::
31
- # <tt>:in_types => types_and_type_names_and_regexps</tt>::
32
- # <tt>:in_type => types_and_type_names_and_regexps</tt>::
33
- # <tt>:within_types => types_and_type_names_and_regexps</tt>::
34
- # <tt>:within_type => types_and_type_names_and_regexps</tt>::
35
- # One or more types, type names and/or regular expessions to match.
36
- # Specify one or an array of values.
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
- # <tt>:objects => objects</tt>::
39
- # <tt>:object => objects</tt>::
40
- # <tt>:for_objects => objects</tt>::
41
- # <tt>:for_object => objects</tt>::
42
- # <tt>:on_objects => objects</tt>::
43
- # <tt>:on_object => objects</tt>::
44
- # <tt>:in_objects => objects</tt>::
45
- # <tt>:in_object => objects</tt>::
46
- # <tt>:within_objects => objects</tt>::
47
- # <tt>:within_object => objects</tt>::
48
- # One or more objects to match.
49
- # Specify one or an array of values.
50
- # Note: Currently, string or symbol objects will be misinterpreted as type names!
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
- # <tt>:methods => method_names_and_regexps</tt>::
53
- # <tt>:method => method_names_and_regexps</tt>::
54
- # <tt>:within_methods => method_names_and_regexps</tt>::
55
- # <tt>:within_method => method_names_and_regexps</tt>::
56
- # <tt>:calling => method_names_and_regexps</tt>::
57
- # <tt>:invoking => method_names_and_regexps</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_&lt;other_method_synonyms&gt; => 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
- # <tt>:public</tt> or <tt>:public_methods</tt>:: Search for public methods (default).
82
- # <tt>:private</tt> or <tt>:private_methods</tt>:: Search for private methods.
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>:: Search for instance methods.
85
- # <tt>:class</tt> or <tt>:class_methods</tt>:: Search for class methods.
86
- # <tt>:singleton</tt> or <tt>:singleton_methods</tt>:: Search for singleton methods. (Using :class for objects
87
- # won't work and :class, :public, :protected, and :private are ignored when
88
- # looking for singleton methods.)
89
- # <tt>:exclude_ancestor_methods</tt>:: Suppress "ancestor" methods. This
90
- # means that if you search for a override method +foo+ in a
91
- # +Derived+ class that is defined in the +Base+ class, you won't find it!
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
- protected
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