aquarium 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +26 -5
- data/README +8 -8
- data/RELEASE-PLAN +20 -2
- data/TODO.rb +26 -0
- data/UPGRADE +5 -5
- data/examples/aspect_design_example.rb +1 -1
- data/examples/aspect_design_example_spec.rb +1 -1
- data/examples/design_by_contract_example.rb +4 -9
- data/examples/design_by_contract_example_spec.rb +7 -9
- data/examples/exception_wrapping_example.rb +48 -0
- data/examples/exception_wrapping_example_spec.rb +49 -0
- data/examples/reusable_aspect_hack_example.rb +56 -0
- data/examples/reusable_aspect_hack_example_spec.rb +80 -0
- data/lib/aquarium.rb +1 -0
- data/lib/aquarium/aspects.rb +1 -1
- data/lib/aquarium/aspects/advice.rb +16 -13
- data/lib/aquarium/aspects/aspect.rb +81 -56
- data/lib/aquarium/aspects/join_point.rb +4 -4
- data/lib/aquarium/aspects/pointcut.rb +49 -73
- data/lib/aquarium/dsl.rb +2 -0
- data/lib/aquarium/dsl/aspect_dsl.rb +77 -0
- data/lib/aquarium/{aspects/dsl → dsl}/object_dsl.rb +2 -2
- data/lib/aquarium/extras/design_by_contract.rb +1 -1
- data/lib/aquarium/finders.rb +1 -1
- data/lib/aquarium/finders/method_finder.rb +26 -26
- data/lib/aquarium/finders/type_finder.rb +45 -39
- data/lib/aquarium/utils/array_utils.rb +6 -5
- data/lib/aquarium/utils/default_logger.rb +2 -1
- data/lib/aquarium/utils/options_utils.rb +178 -67
- data/lib/aquarium/utils/set_utils.rb +8 -3
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -14
- data/spec/aquarium/aspects/aspect_spec.rb +91 -7
- data/spec/aquarium/aspects/pointcut_spec.rb +61 -0
- data/spec/aquarium/{aspects/dsl → dsl}/aspect_dsl_spec.rb +76 -32
- data/spec/aquarium/finders/method_finder_spec.rb +80 -80
- data/spec/aquarium/finders/type_finder_spec.rb +57 -52
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -12
- data/spec/aquarium/spec_example_types.rb +4 -3
- data/spec/aquarium/utils/array_utils_spec.rb +9 -7
- data/spec/aquarium/utils/options_utils_spec.rb +106 -5
- data/spec/aquarium/utils/set_utils_spec.rb +14 -0
- metadata +12 -7
- data/lib/aquarium/aspects/dsl.rb +0 -2
- 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
|
data/lib/aquarium.rb
CHANGED
data/lib/aquarium/aspects.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
35
|
+
ASPECT_CANONICAL_OPTIONS = {
|
36
36
|
"advice" => %w[action do_action use_advice advise_with invoke call],
|
37
|
-
"pointcuts" => %w[pointcut
|
38
|
-
"
|
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
|
-
|
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,
|
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
|
161
|
+
def finish_specification_initialization &block
|
173
162
|
Advice.kinds.each do |kind|
|
174
|
-
|
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::
|
212
|
-
|
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 =>
|
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
|
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
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
bad_options(":after_returning can't be used with :after_raising.") if
|
486
|
-
|
487
|
-
|
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 <<
|
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
|
-
|
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
|
523
|
-
sym =
|
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}"
|
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
|
-
:
|
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
|
-
(
|
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
|
-
|
199
|
-
"
|
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
|
-
"
|
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
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
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
|
223
|
-
|
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
|
-
|
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
|
241
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
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
|
-
|
267
|
-
|
268
|
-
|
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
|
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
|
-
|
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
|
-
:
|
366
|
+
:method_options => method_options
|
391
367
|
end
|
392
368
|
|
393
369
|
def add_join_points search_results, type_or_object_sym
|