aquarium 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|