aquarium 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/CHANGES +26 -5
  2. data/README +8 -8
  3. data/RELEASE-PLAN +20 -2
  4. data/TODO.rb +26 -0
  5. data/UPGRADE +5 -5
  6. data/examples/aspect_design_example.rb +1 -1
  7. data/examples/aspect_design_example_spec.rb +1 -1
  8. data/examples/design_by_contract_example.rb +4 -9
  9. data/examples/design_by_contract_example_spec.rb +7 -9
  10. data/examples/exception_wrapping_example.rb +48 -0
  11. data/examples/exception_wrapping_example_spec.rb +49 -0
  12. data/examples/reusable_aspect_hack_example.rb +56 -0
  13. data/examples/reusable_aspect_hack_example_spec.rb +80 -0
  14. data/lib/aquarium.rb +1 -0
  15. data/lib/aquarium/aspects.rb +1 -1
  16. data/lib/aquarium/aspects/advice.rb +16 -13
  17. data/lib/aquarium/aspects/aspect.rb +81 -56
  18. data/lib/aquarium/aspects/join_point.rb +4 -4
  19. data/lib/aquarium/aspects/pointcut.rb +49 -73
  20. data/lib/aquarium/dsl.rb +2 -0
  21. data/lib/aquarium/dsl/aspect_dsl.rb +77 -0
  22. data/lib/aquarium/{aspects/dsl → dsl}/object_dsl.rb +2 -2
  23. data/lib/aquarium/extras/design_by_contract.rb +1 -1
  24. data/lib/aquarium/finders.rb +1 -1
  25. data/lib/aquarium/finders/method_finder.rb +26 -26
  26. data/lib/aquarium/finders/type_finder.rb +45 -39
  27. data/lib/aquarium/utils/array_utils.rb +6 -5
  28. data/lib/aquarium/utils/default_logger.rb +2 -1
  29. data/lib/aquarium/utils/options_utils.rb +178 -67
  30. data/lib/aquarium/utils/set_utils.rb +8 -3
  31. data/lib/aquarium/version.rb +1 -1
  32. data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -14
  33. data/spec/aquarium/aspects/aspect_spec.rb +91 -7
  34. data/spec/aquarium/aspects/pointcut_spec.rb +61 -0
  35. data/spec/aquarium/{aspects/dsl → dsl}/aspect_dsl_spec.rb +76 -32
  36. data/spec/aquarium/finders/method_finder_spec.rb +80 -80
  37. data/spec/aquarium/finders/type_finder_spec.rb +57 -52
  38. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -12
  39. data/spec/aquarium/spec_example_types.rb +4 -3
  40. data/spec/aquarium/utils/array_utils_spec.rb +9 -7
  41. data/spec/aquarium/utils/options_utils_spec.rb +106 -5
  42. data/spec/aquarium/utils/set_utils_spec.rb +14 -0
  43. metadata +12 -7
  44. data/lib/aquarium/aspects/dsl.rb +0 -2
  45. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +0 -64
@@ -0,0 +1,80 @@
1
+ require File.dirname(__FILE__) + '/../spec/aquarium/spec_helper'
2
+ require 'aquarium'
3
+
4
+ # Example demonstrating a hack for defining a reusable aspect in a module
5
+ # so that the aspect only gets created when the module is included by another
6
+ # module or class.
7
+ # Hacking like this defies the spirit of Aquarium's goal of being "intuitive",
8
+ # so I created a feature request #19122 to address this problem.
9
+ #
10
+ # WARNING: put the "include ..." statement at the END of the class declaration,
11
+ # as shown below. If you put the include statement at the beginning, as you
12
+ # normally wouuld for including a module, it won't advice any join points,
13
+ # because no methods will have been defined at that point!!
14
+
15
+ module Aquarium
16
+ module Reusables
17
+ module TraceMethods
18
+ def self.advice_invoked?
19
+ @@advice_invoked
20
+ end
21
+ def self.reset_advice_invoked
22
+ @@advice_invoked = false
23
+ end
24
+
25
+ def self.append_features mod
26
+ Aquarium::Aspects::Aspect.new :around, :ignore_no_matching_join_points => true,
27
+ :type => mod, :methods => :all, :method_options => [:exclude_ancestor_methods] do |jp, object, *args|
28
+ @@advice_invoked = true
29
+ jp.proceed
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ class NotTraced1
37
+ def doit; end
38
+ end
39
+ class NotTraced2
40
+ include Aquarium::Reusables::TraceMethods
41
+ def doit; end
42
+ end
43
+ class Traced1
44
+ def doit; end
45
+ include Aquarium::Reusables::TraceMethods
46
+ end
47
+ class Traced2
48
+ def doit; end
49
+ end
50
+
51
+ describe "Reusable aspect defined in a module can be evaluated at 'include' time if append_features is used" do
52
+ before :each do
53
+ Aquarium::Reusables::TraceMethods.reset_advice_invoked
54
+ end
55
+
56
+ it "should not advise types that don't include the module with the aspect" do
57
+ NotTraced1.new.doit
58
+ Aquarium::Reusables::TraceMethods.advice_invoked?.should be_false
59
+ end
60
+
61
+ it "should not advise any methods if the module with the aspect is included before any methods are defined!" do
62
+ NotTraced2.new.doit
63
+ Aquarium::Reusables::TraceMethods.advice_invoked?.should be_false
64
+ end
65
+
66
+ it "should advise methods if the module with the aspect is included after the methods are defined" do
67
+ Traced1.new.doit
68
+ Aquarium::Reusables::TraceMethods.advice_invoked?.should be_true
69
+ end
70
+
71
+ it "should advise methods after the module with the aspect is included" do
72
+ Traced2.new.doit
73
+ Aquarium::Reusables::TraceMethods.advice_invoked?.should be_false
74
+ class Traced2
75
+ include Aquarium::Reusables::TraceMethods
76
+ end
77
+ Traced2.new.doit
78
+ Aquarium::Reusables::TraceMethods.advice_invoked?.should be_true
79
+ end
80
+ end
@@ -4,4 +4,5 @@ require 'aquarium/extensions'
4
4
  require 'aquarium/finders'
5
5
  require 'aquarium/aspects'
6
6
  require 'aquarium/version'
7
+ require 'aquarium/dsl'
7
8
 
@@ -3,4 +3,4 @@ require 'aquarium/aspects/aspect'
3
3
  require 'aquarium/aspects/join_point'
4
4
  require 'aquarium/aspects/pointcut'
5
5
  require 'aquarium/aspects/pointcut_composition'
6
- require 'aquarium/aspects/dsl'
6
+ require 'aquarium/dsl'
@@ -37,6 +37,15 @@ module Aquarium
37
37
  attr_accessor(:#{key})
38
38
  EOF
39
39
  end
40
+
41
+ def call_advice jp, obj, *args
42
+ case advice.arity
43
+ when 0 then advice.call
44
+ when 1 then advice.call jp
45
+ when 2 then advice.call jp, obj
46
+ else advice.call jp, obj, *args
47
+ end
48
+ end
40
49
  end
41
50
 
42
51
  def call jp, obj, *args
@@ -106,15 +115,9 @@ module Aquarium
106
115
  def initialize options = {}
107
116
  super(options) { |jp, obj, *args|
108
117
  block_for_method = jp.context.block_for_method
109
- method = invoking_object(jp).method(@alias_method_name)
110
118
  block_for_method.nil? ?
111
119
  invoking_object(jp).send(@alias_method_name, *args) :
112
120
  invoking_object(jp).send(@alias_method_name, *args, &block_for_method)
113
- # Buggy!!
114
- # method = invoking_object.method(@alias_method_name)
115
- # block_for_method.nil? ?
116
- # method.call(*args) :
117
- # method.call(*args, &block_for_method)
118
121
  }
119
122
  end
120
123
  end
@@ -123,7 +126,7 @@ module Aquarium
123
126
  def initialize options = {}
124
127
  super(options) { |jp, obj, *args|
125
128
  before_jp = jp.make_current_context_join_point :advice_kind => :before, :current_advice_node => self
126
- advice.call(before_jp, obj, *args)
129
+ call_advice(before_jp, obj, *args)
127
130
  next_node.call(jp, obj, *args)
128
131
  }
129
132
  end
@@ -134,7 +137,7 @@ module Aquarium
134
137
  super(options) { |jp, obj, *args|
135
138
  returned_value = next_node.call(jp, obj, *args)
136
139
  next_jp = jp.make_current_context_join_point :advice_kind => :after_returning, :returned_value => returned_value, :current_advice_node => self
137
- advice.call(next_jp, obj, *args)
140
+ call_advice(next_jp, obj, *args)
138
141
  next_jp.context.returned_value # allow advice to modify the returned value
139
142
  }
140
143
  end
@@ -151,7 +154,7 @@ module Aquarium
151
154
  rescue Object => raised_exception
152
155
  if after_raising_exceptions_list_includes raised_exception
153
156
  next_jp = jp.make_current_context_join_point :advice_kind => :after_raising, :raised_exception => raised_exception, :current_advice_node => self
154
- advice.call(next_jp, obj, *args)
157
+ call_advice(next_jp, obj, *args)
155
158
  raised_exception = next_jp.context.raised_exception # allow advice to modify raised exception
156
159
  end
157
160
  raise raised_exception
@@ -173,16 +176,16 @@ module Aquarium
173
176
  class AfterAdviceChainNode < AdviceChainNode
174
177
  def initialize options = {}
175
178
  super(options) { |jp, obj, *args|
176
- # advice.call is invoked in each bloc, rather than once in an "ensure" clause, so the invocation in the rescue class
179
+ # call_advice is invoked in each bloc, rather than once in an "ensure" clause, so the invocation in the rescue class
177
180
  # can allow the advice to change the exception that will be raised.
178
181
  begin
179
182
  returned_value = next_node.call(jp, obj, *args)
180
183
  next_jp = jp.make_current_context_join_point :advice_kind => :after, :returned_value => returned_value, :current_advice_node => self
181
- advice.call(next_jp, obj, *args)
184
+ call_advice(next_jp, obj, *args)
182
185
  next_jp.context.returned_value # allow advice to modify the returned value
183
186
  rescue Object => raised_exception
184
187
  next_jp = jp.make_current_context_join_point :advice_kind => :after, :raised_exception => raised_exception, :current_advice_node => self
185
- advice.call(next_jp, obj, *args)
188
+ call_advice(next_jp, obj, *args)
186
189
  raise next_jp.context.raised_exception
187
190
  end
188
191
  }
@@ -193,7 +196,7 @@ module Aquarium
193
196
  def initialize options = {}
194
197
  super(options) { |jp, obj, *args|
195
198
  around_jp = jp.make_current_context_join_point :advice_kind => :around, :proceed_proc => next_node, :current_advice_node => self
196
- advice.call(around_jp, obj, *args)
199
+ call_advice(around_jp, obj, *args)
197
200
  }
198
201
  end
199
202
  end
@@ -32,29 +32,18 @@ module Aquarium
32
32
 
33
33
  attr_reader :specification, :pointcuts, :advice
34
34
 
35
- CANONICAL_OPTIONS = Pointcut::CANONICAL_OPTIONS.merge({
35
+ ASPECT_CANONICAL_OPTIONS = {
36
36
  "advice" => %w[action do_action use_advice advise_with invoke call],
37
- "pointcuts" => %w[pointcut within_pointcuts within_pointcut on_pointcuts on_pointcut],
38
- "exclude_pointcuts" => %w[exclude_pointcut exclude_on_pointcut exclude_on_pointcuts exclude_within_pointcut exclude_within_pointcuts],
37
+ "pointcuts" => %w[pointcut],
38
+ "exceptions" => %w[exception],
39
39
  "ignore_no_matching_join_points" => %[ignore_no_jps]
40
- })
40
+ }
41
+ add_prepositional_option_variants_for "pointcuts", ASPECT_CANONICAL_OPTIONS
42
+ add_exclude_options_for "pointcuts", ASPECT_CANONICAL_OPTIONS
43
+ CANONICAL_OPTIONS = Pointcut::CANONICAL_OPTIONS.merge ASPECT_CANONICAL_OPTIONS
41
44
 
42
- ALL_ALLOWED_OPTIONS = CANONICAL_OPTIONS.keys.inject([]) {|ary,i| ary << i << CANONICAL_OPTIONS[i]}.flatten +
43
- Pointcut::ATTRIBUTE_OPTIONS
45
+ canonical_options_given_methods CANONICAL_OPTIONS
44
46
 
45
- ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern} + Advice::kinds
46
-
47
- CANONICAL_OPTIONS.keys.each do |name|
48
- module_eval(<<-EOF, __FILE__, __LINE__)
49
- def #{name}_given
50
- @specification[:#{name}]
51
- end
52
-
53
- def #{name}_given?
54
- not (#{name}_given.nil? or #{name}_given.empty?)
55
- end
56
- EOF
57
- end
58
47
 
59
48
  # Aspect.new (:around | :before | :after | :after_returning | :after_raising ) \
60
49
  # (:pointcuts => [...]), | \
@@ -84,6 +73,8 @@ module Aquarium
84
73
  # Invoke the specified advice after the join point returns successfully.
85
74
  #
86
75
  # <tt>:after_raising [=> exception || [exception_list]]</tt>::
76
+ # <tt>:after_raising, :exceptions => (exception || [exception_list])</tt>::
77
+ # <tt>:after_raising, :exception => (exception || [exception_list])</tt>::
87
78
  # Invoke the specified advice after the join point raises one of the specified exceptions.
88
79
  # If no exceptions are specified, the advice is invoked after any exception is raised.
89
80
  #
@@ -115,7 +106,9 @@ module Aquarium
115
106
  def initialize *options, &block
116
107
  @first_option_that_was_method = []
117
108
  opts = rationalize options
118
- init_specification opts, CANONICAL_OPTIONS, &block
109
+ init_specification opts, CANONICAL_OPTIONS, (Pointcut::ATTRIBUTE_OPTIONS_VALUES + KINDS_IN_PRIORITY_ORDER) do
110
+ finish_specification_initialization &block
111
+ end
119
112
  init_pointcuts
120
113
  validate_specification
121
114
  return if noop
@@ -130,10 +123,6 @@ module Aquarium
130
123
  get_jps :join_points_not_matched
131
124
  end
132
125
 
133
- def all_allowed_option_symbols
134
- ALL_ALLOWED_OPTION_SYMBOLS + @first_option_that_was_method
135
- end
136
-
137
126
  def unadvise
138
127
  return if noop
139
128
  @pointcuts.each do |pointcut|
@@ -169,15 +158,38 @@ module Aquarium
169
158
  return (options.first.kind_of?(Hash) or options.first.kind_of?(Array)) ? options.first : options
170
159
  end
171
160
 
172
- def init_type_specific_specification original_options, options_hash, &block
161
+ def finish_specification_initialization &block
173
162
  Advice.kinds.each do |kind|
174
- @specification[kind] = Set.new(make_array(options_hash[kind])) if options_hash[kind]
163
+ found, value_array = contains_advice_kind kind
164
+ @specification[kind] = Set.new(value_array) if found
165
+ end
166
+ init_pointcut_specific_specification
167
+ options_to_ignore_when_validating = []
168
+ unless methods_given?
169
+ options_to_ignore_when_validating = use_first_nonadvice_symbol_as_method
175
170
  end
176
- @specification.merge! Pointcut.make_attribute_reading_writing_options(options_hash)
177
- use_default_objects_if_defined unless some_type_or_pc_option_given?
178
- use_first_nonadvice_symbol_as_method(original_options) unless methods_given?
179
171
  calculate_excluded_types
180
172
  @advice = determine_advice block
173
+ # Be careful to only add the exceptions if :after_raising was actually specified!
174
+ if (exceptions_given? and specified_advice_kinds.include?(:after_raising))
175
+ @specification[:after_raising] += exceptions_given
176
+ end
177
+ options_to_ignore_when_validating
178
+ end
179
+
180
+ def contains_advice_kind kind
181
+ keys = @original_options
182
+ hash = {}
183
+ if Array === @original_options
184
+ if Hash === @original_options.last
185
+ hash = @original_options.last
186
+ keys = @original_options[0...-1] + hash.keys
187
+ end
188
+ else Hash === @original_options
189
+ hash = @original_options
190
+ keys = @original_options.keys
191
+ end
192
+ keys.include?(kind) ? [true, make_array(hash[kind])] : [false, []]
181
193
  end
182
194
 
183
195
  def calculate_excluded_types
@@ -194,6 +206,19 @@ module Aquarium
194
206
  block || (@specification[:advice].to_a.first)
195
207
  end
196
208
 
209
+ def init_pointcut_specific_specification
210
+ options_hash = hash_in_original_options
211
+ @specification.merge! Pointcut.make_attribute_reading_writing_options(options_hash)
212
+ # Map the method options to their canonical values:
213
+ @specification[:method_options] = Aquarium::Finders::MethodFinder.init_method_options(@specification[:method_options])
214
+
215
+ Pointcut::validate_attribute_options @specification, options_hash
216
+ end
217
+
218
+ def hash_in_original_options
219
+ @original_options.kind_of?(Array) ? @original_options.last : @original_options
220
+ end
221
+
197
222
  def init_pointcuts
198
223
  pointcuts = []
199
224
  if pointcuts_given?
@@ -208,8 +233,9 @@ module Aquarium
208
233
  end
209
234
  else
210
235
  pc_options = {}
211
- Pointcut::ALL_ALLOWED_OPTION_SYMBOLS.each do |pc_option|
212
- pc_options[pc_option] = @specification[pc_option] unless @specification[pc_option].nil?
236
+ Pointcut::CANONICAL_OPTIONS.keys.each do |pc_option|
237
+ pco_sym = pc_option.intern
238
+ pc_options[pco_sym] = @specification[pco_sym] unless @specification[pco_sym].nil?
213
239
  end
214
240
  pointcuts << Pointcut.new(pc_options)
215
241
  end
@@ -351,7 +377,7 @@ module Aquarium
351
377
  advice_chain = #{type_being_advised_text}.send :class_variable_get, "#{advice_chain_attr_sym}"
352
378
  static_join_point = advice_chain.static_join_point
353
379
  advice_join_point = static_join_point.make_current_context_join_point(
354
- :advice_kind => :before,
380
+ :advice_kind => #{advice_kinds_given.inspect},
355
381
  :advised_object => #{target_self},
356
382
  :parameters => args,
357
383
  :block_for_method => block_for_method)
@@ -454,8 +480,8 @@ module Aquarium
454
480
  "_aspect_"
455
481
  end
456
482
 
457
- def some_type_or_pc_option_given?
458
- pointcuts_given? or some_type_option_given? or objects_given?
483
+ def some_type_object_join_point_or_pc_option_given?
484
+ pointcuts_given? or join_points_given? or some_type_option_given? or objects_given?
459
485
  end
460
486
 
461
487
  def some_type_option_given?
@@ -474,17 +500,22 @@ module Aquarium
474
500
  Advice.kinds & @specification.keys
475
501
  end
476
502
 
503
+ def options_given? option1, option2
504
+ @specification[option1] and @specification[option2]
505
+ end
506
+
477
507
  def validate_specification
478
508
  bad_options("One of #{Advice.kinds.inspect} is required.") unless advice_kinds_given?
479
- bad_options(":around can't be used with :before.") if around_given_with? :before
480
- bad_options(":around can't be used with :after.") if around_given_with? :after
481
- bad_options(":around can't be used with :after_returning.") if around_given_with? :after_returning
482
- bad_options(":around can't be used with :after_raising.") if around_given_with? :after_raising
483
- bad_options(":after can't be used with :after_returning.") if after_given_with? :after_returning
484
- bad_options(":after can't be used with :after_raising.") if after_given_with? :after_raising
485
- bad_options(":after_returning can't be used with :after_raising.") if after_returning_given_with? :after_raising
486
- unless some_type_or_pc_option_given?
487
- bad_options("At least one of :pointcut(s), :type(s), :type(s)_and_ancestors, :type(s)_and_descendents, :object(s) is required.")
509
+ %w[before after after_returning after_raising].each do |advice_kind|
510
+ bad_options(":around can't be used with :#{advice_kind}.") if options_given? :around, advice_kind.intern
511
+ end
512
+ %w[after_returning after_raising].each do |advice_kind|
513
+ bad_options(":after can't be used with :#{advice_kind}.") if options_given? :after, advice_kind.intern
514
+ end
515
+ bad_options(":after_returning can't be used with :after_raising.") if options_given? :after_returning, :after_raising
516
+ bad_options(":exceptions can't be specified except with :after_raising.") if exceptions_given? and not specified_advice_kinds.include?(:after_raising)
517
+ unless some_type_object_join_point_or_pc_option_given? or default_objects_given?
518
+ bad_options("At least one of :pointcut(s), :join_point(s), :type(s), :type(s)_and_ancestors, :type(s)_and_descendents, or :object(s) is required.")
488
519
  end
489
520
  if pointcuts_given? and (some_type_option_given? or objects_given?)
490
521
  bad_options("Can't specify both :pointcut(s) and one or more of :type(s), and/or :object(s).")
@@ -502,32 +533,26 @@ module Aquarium
502
533
  end
503
534
 
504
535
  def advice_kinds_given
505
- Advice.kinds.inject([]) {|ary, kind| ary << @specification[kind] if @specification[kind]; ary}
536
+ Advice.kinds.inject([]) {|ary, kind| ary << kind if @specification[kind]; ary}
506
537
  end
507
538
 
508
539
  def advice_kinds_given?
509
540
  not advice_kinds_given.empty?
510
541
  end
511
542
 
512
- %w[around after after_returning].each do |advice_kind|
513
- class_eval(<<-EOF, __FILE__, __LINE__)
514
- def #{advice_kind}_given_with? other_advice_kind_sym
515
- @specification[:#{advice_kind}] and @specification[other_advice_kind_sym]
516
- end
517
- EOF
518
- end
519
-
520
- def use_first_nonadvice_symbol_as_method options
543
+ def use_first_nonadvice_symbol_as_method
521
544
  2.times do |i|
522
- if options.size >= i+1
523
- sym = options[i]
545
+ if @original_options.size >= i+1
546
+ sym = @original_options[i]
524
547
  if sym.kind_of?(Symbol) && !Advice::kinds.include?(sym)
525
548
  @specification[:methods] = Set.new([sym])
549
+ @specification.delete sym
526
550
  @first_option_that_was_method << sym
527
- return
551
+ return [sym]
528
552
  end
529
553
  end
530
554
  end
555
+ []
531
556
  end
532
557
 
533
558
  def bad_options message
@@ -38,7 +38,7 @@ module Aquarium
38
38
 
39
39
  def update options
40
40
  options.each do |key, value|
41
- instance_variable_set "@#{key}".intern, value
41
+ instance_variable_set "@#{key}", value
42
42
  end
43
43
  end
44
44
 
@@ -125,7 +125,7 @@ module Aquarium
125
125
  type_or_object_sym = @target_type ? :type : :object
126
126
  results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object,
127
127
  :method => method_name,
128
- :options => [visibility, instance_or_class_method]
128
+ :method_options => [visibility, instance_or_class_method]
129
129
  raise Aquarium::Utils::LogicError("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
130
130
  return results.matched.size == 1 ? true : false
131
131
  end
@@ -217,9 +217,9 @@ module Aquarium
217
217
  return type if type.kind_of? Module
218
218
  found = Aquarium::Finders::TypeFinder.new.find :type => type
219
219
  if found.matched.empty?
220
- bad_attributes("No type matched the string or regular expression: #{type}", options)
220
+ bad_attributes("No type matched the string or regular expression: #{type.inspect}", options)
221
221
  elsif found.matched.size > 1
222
- bad_attributes("More than one type matched the string or regular expression: #{type}", options)
222
+ bad_attributes("More than one type matched the string or regular expression: #{type.inspect}", options)
223
223
  end
224
224
  found.matched.keys.first
225
225
  end
@@ -160,7 +160,9 @@ module Aquarium
160
160
  #
161
161
  # Pointcut.new also accepts all the "universal" options documented in OptionsUtils.
162
162
  def initialize options = {}
163
- init_specification options, CANONICAL_OPTIONS
163
+ init_specification options, CANONICAL_OPTIONS, (ATTRIBUTE_OPTIONS_VALUES + Advice::KINDS_IN_PRIORITY_ORDER) do
164
+ finish_specification_initialization
165
+ end
164
166
  return if noop
165
167
  init_candidate_types
166
168
  init_candidate_objects
@@ -174,8 +176,9 @@ module Aquarium
174
176
  # the specifications are equal, and the candidate types and candidate objects are equal.
175
177
  # if you care only about the matched join points, then just compare #join_points_matched
176
178
  def eql? other
177
- object_id == other.object_id ||
178
- (specification == other.specification &&
179
+ object_id == other.object_id ||
180
+ (self.class === other &&
181
+ specification == other.specification &&
179
182
  candidate_types == other.candidate_types &&
180
183
  candidate_types_excluded == other.candidate_types_excluded &&
181
184
  candidate_objects == other.candidate_objects &&
@@ -195,111 +198,84 @@ module Aquarium
195
198
 
196
199
  alias to_s inspect
197
200
 
198
- CANONICAL_OPTIONS = {
199
- "types" => %w[type class classes module modules],
200
- "types_and_descendents" => %w[type_and_descendents class_and_descendents classes_and_descendents module_and_descendents modules_and_descendents],
201
- "types_and_ancestors" => %w[type_and_ancestors class_and_ancestors classes_and_ancestors module_and_ancestors modules_and_ancestors],
202
- "objects" => %w[object],
201
+ POINTCUT_CANONICAL_OPTIONS = {
202
+ "default_objects" => %w[default_object],
203
203
  "join_points" => %w[join_point],
204
- "methods" => %w[method within_method within_methods calling invoking calls_to invocations_of sending_message_to sending_messages_to],
204
+ "exclude_pointcuts" => %w[exclude_pointcut],
205
205
  "attributes" => %w[attribute accessing],
206
- "method_options" => %w[method_option restricting_methods_to],
207
206
  "attribute_options" => %w[attribute_option],
208
- "default_objects" => %w[default_object]
209
207
  }
210
- %w[types types_and_descendents types_and_ancestors objects join_points ].each do |thing|
211
- roots = CANONICAL_OPTIONS[thing].dup + [thing]
212
- CANONICAL_OPTIONS["exclude_#{thing}"] = roots.map {|x| "exclude_#{x}"}
213
- %w[for on in within].each do |prefix|
214
- roots.each do |root|
215
- CANONICAL_OPTIONS[thing] << "#{prefix}_#{root}"
216
- end
217
- end
218
- end
219
- CANONICAL_OPTIONS["methods"].dup.each do |synonym|
220
- CANONICAL_OPTIONS["methods"] << "#{synonym}_methods_matching"
208
+ add_prepositional_option_variants_for "join_points", POINTCUT_CANONICAL_OPTIONS
209
+ add_exclude_options_for "join_points", POINTCUT_CANONICAL_OPTIONS
210
+ Aquarium::Utils::OptionsUtils.universal_prepositions.each do |prefix|
211
+ POINTCUT_CANONICAL_OPTIONS["exclude_pointcuts"] += ["exclude_#{prefix}_pointcuts", "exclude_#{prefix}_pointcut"]
221
212
  end
222
- CANONICAL_OPTIONS["exclude_methods"] = []
223
- CANONICAL_OPTIONS["methods"].each do |synonym|
224
- CANONICAL_OPTIONS["exclude_methods"] << "exclude_#{synonym}"
225
- end
226
- CANONICAL_OPTIONS["exclude_pointcuts"] = ["exclude_pointcut"] +
227
- %w[for on in within].map {|prefix| ["exclude_#{prefix}_pointcuts", "exclude_#{prefix}_pointcut"]}.flatten
228
-
229
- ATTRIBUTE_OPTIONS = %w[reading writing changing]
230
-
231
- ALL_ALLOWED_OPTIONS = ATTRIBUTE_OPTIONS +
232
- CANONICAL_OPTIONS.keys.inject([]) {|ary,i| ary << i << CANONICAL_OPTIONS[i]}.flatten
213
+ CANONICAL_OPTIONS = Aquarium::Finders::TypeFinder::CANONICAL_OPTIONS.merge(
214
+ Aquarium::Finders::MethodFinder::METHOD_FINDER_CANONICAL_OPTIONS.merge(POINTCUT_CANONICAL_OPTIONS))
233
215
 
234
- ALL_ALLOWED_OPTION_SYMBOLS = ALL_ALLOWED_OPTIONS.map {|o| o.intern}
235
-
236
- def all_allowed_option_symbols
237
- ALL_ALLOWED_OPTION_SYMBOLS
238
- end
216
+ ATTRIBUTE_OPTIONS_VALUES = %w[reading writing changing]
239
217
 
240
- CANONICAL_OPTIONS.keys.each do |name|
241
- module_eval(<<-EOF, __FILE__, __LINE__)
242
- def #{name}_given
243
- @specification[:#{name}]
244
- end
245
-
246
- def #{name}_given?
247
- not (#{name}_given.nil? or #{name}_given.empty?)
248
- end
249
- EOF
250
- end
218
+ canonical_options_given_methods CANONICAL_OPTIONS
219
+ canonical_option_accessor CANONICAL_OPTIONS
251
220
 
252
221
  def self.make_attribute_reading_writing_options options_hash
253
222
  result = {}
254
223
  [:writing, :changing, :reading].each do |attr_key|
255
- unless options_hash[attr_key].nil? or options_hash[attr_key].empty?
256
- result[:attributes] ||= Set.new([])
257
- result[:attribute_options] ||= Set.new([])
258
- result[:attributes].merge(Aquarium::Utils::ArrayUtils.make_array(options_hash[attr_key]))
259
- attr_opt = attr_key == :reading ? :readers : :writers
260
- result[:attribute_options] << attr_opt
261
- end
224
+ next if options_hash[attr_key].nil? or options_hash[attr_key].empty?
225
+ result[:attributes] ||= Set.new([])
226
+ result[:attribute_options] ||= Set.new([])
227
+ result[:attributes].merge(Aquarium::Utils::ArrayUtils.make_array(options_hash[attr_key]))
228
+ attr_opt = attr_key == :reading ? :readers : :writers
229
+ result[:attribute_options] << attr_opt
262
230
  end
263
231
  result
264
232
  end
265
233
 
266
- protected
267
-
268
- attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
269
-
270
- def init_type_specific_specification original_options, options_hash
271
- @specification.merge! Pointcut.make_attribute_reading_writing_options(options_hash)
234
+ # TODO remove duplication w/ aspect.rb
235
+ def finish_specification_initialization
236
+ @specification.merge! Pointcut.make_attribute_reading_writing_options(@original_options)
272
237
  # Map the method options to their canonical values:
273
238
  @specification[:method_options] = Aquarium::Finders::MethodFinder.init_method_options(@specification[:method_options])
274
- use_default_objects_if_defined unless (types_given? || objects_given?)
239
+ use_default_objects_if_defined unless any_type_related_options_given?
240
+ Pointcut::validate_attribute_options @specification, @original_options
241
+ init_methods_specification
242
+ end
243
+
244
+ def init_methods_specification
245
+ match_all_methods if ((no_methods_specified? and no_attributes_specified?) or all_methods_specified?)
246
+ end
275
247
 
276
- raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if @specification[:attributes] == Set.new([:all])
248
+ 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?
250
+ end
251
+
252
+ def self.validate_attribute_options spec_hash, options_hash
253
+ raise Aquarium::Utils::InvalidOptions.new(":all is not yet supported for :attributes.") if spec_hash[:attributes] == Set.new([:all])
277
254
  if options_hash[:reading] and (options_hash[:writing] or options_hash[:changing])
278
255
  unless options_hash[:reading].eql?(options_hash[:writing]) or options_hash[:reading].eql?(options_hash[:changing])
279
256
  raise Aquarium::Utils::InvalidOptions.new(":reading and :writing/:changing can only be used together if they refer to the same set of attributes.")
280
257
  end
281
258
  end
282
- init_methods_specification options_hash
283
- end
284
-
285
- def init_methods_specification options
286
- match_all_methods if ((no_methods_specified and no_attributes_specified) or all_methods_specified)
287
259
  end
260
+
261
+ protected
262
+
263
+ attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_types_excluded, :candidate_objects, :candidate_join_points
288
264
 
289
265
  def match_all_methods
290
266
  @specification[:methods] = Set.new([:all])
291
267
  end
292
268
 
293
- def no_methods_specified
269
+ def no_methods_specified?
294
270
  @specification[:methods].nil? or @specification[:methods].empty?
295
271
  end
296
272
 
297
- def all_methods_specified
273
+ def all_methods_specified?
298
274
  methods_spec = @specification[:methods].to_a
299
275
  methods_spec.include?(:all) or methods_spec.include?(:all_methods)
300
276
  end
301
277
 
302
- def no_attributes_specified
278
+ def no_attributes_specified?
303
279
  @specification[:attributes].nil? or @specification[:attributes].empty?
304
280
  end
305
281
 
@@ -387,7 +363,7 @@ module Aquarium
387
363
  Aquarium::Finders::MethodFinder.new.find type_or_object_sym => candidates.matched_keys,
388
364
  :methods => which_methods.to_a,
389
365
  :exclude_methods => @specification[:exclude_methods],
390
- :options => method_options
366
+ :method_options => method_options
391
367
  end
392
368
 
393
369
  def add_join_points search_results, type_or_object_sym