aquarium 0.1.5 → 0.1.6
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 +64 -0
- data/README +19 -8
- data/UPGRADE +7 -1
- data/examples/method_tracing_example.rb +18 -8
- data/examples/method_tracing_example_spec.rb +34 -20
- data/lib/aquarium/aspects/advice.rb +11 -3
- data/lib/aquarium/aspects/aspect.rb +11 -9
- data/lib/aquarium/aspects/join_point.rb +55 -20
- data/lib/aquarium/aspects/pointcut.rb +49 -20
- data/lib/aquarium/extras/design_by_contract.rb +2 -1
- data/lib/aquarium/finders/type_finder.rb +55 -16
- data/lib/aquarium/utils/method_utils.rb +5 -1
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +12 -0
- data/spec/aquarium/aspects/aspect_spec.rb +85 -8
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +49 -0
- data/spec/aquarium/aspects/join_point_spec.rb +94 -23
- data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +5 -3
- data/spec/aquarium/aspects/pointcut_spec.rb +104 -22
- data/spec/aquarium/extras/design_by_contract_spec.rb +6 -0
- data/spec/aquarium/finders/type_finder_spec.rb +60 -14
- metadata +4 -3
@@ -70,10 +70,11 @@ module Aquarium
|
|
70
70
|
init_specification options
|
71
71
|
init_candidate_types
|
72
72
|
init_candidate_objects
|
73
|
+
init_candidate_join_points
|
73
74
|
init_join_points
|
74
75
|
end
|
75
76
|
|
76
|
-
attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects
|
77
|
+
attr_reader :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
|
77
78
|
|
78
79
|
# Two Considered equivalent only if the same join points matched and not_matched sets are equal,
|
79
80
|
# the specifications are equal, and the candidate types and candidate objects are equal.
|
@@ -111,15 +112,17 @@ module Aquarium
|
|
111
112
|
|
112
113
|
protected
|
113
114
|
|
114
|
-
attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects
|
115
|
+
attr_writer :join_points_matched, :join_points_not_matched, :specification, :candidate_types, :candidate_objects, :candidate_join_points
|
115
116
|
|
116
117
|
def init_specification options
|
117
118
|
@specification = {}
|
118
119
|
options ||= {}
|
120
|
+
validate_options options
|
119
121
|
@specification[:method_options] = Set.new(make_array(options[:method_options]))
|
120
122
|
@specification[:attribute_options] = Set.new(make_array(options[:attribute_options]) )
|
121
123
|
@specification[:types] = Set.new(make_array(options[:types], options[:type]))
|
122
124
|
@specification[:objects] = Set.new(make_array(options[:objects], options[:object]))
|
125
|
+
@specification[:join_points] = Set.new(make_array(options[:join_points], options[:join_point]))
|
123
126
|
@specification[:default_object] = Set.new(make_array(options[:default_object]))
|
124
127
|
use_default_object_if_defined unless (types_given? || objects_given?)
|
125
128
|
@specification[:attributes] = Set.new(make_array(options[:attributes], options[:attribute]))
|
@@ -131,6 +134,12 @@ module Aquarium
|
|
131
134
|
@specification[:methods] = Set.new(make_array(options[:methods], options[:method]))
|
132
135
|
@specification[:methods].add(:all) if @specification[:methods].empty? and @specification[:attributes].empty?
|
133
136
|
end
|
137
|
+
|
138
|
+
def validate_options options
|
139
|
+
knowns = %w[object objects type types join_point join_points method methods attribute attributes method_options attribute_options default_object].map {|x| x.intern}
|
140
|
+
unknowns = options.keys - knowns
|
141
|
+
raise Aquarium::Utils::InvalidOptions.new("Unknown options specified: #{unknowns.inspect}") if unknowns.size > 0
|
142
|
+
end
|
134
143
|
|
135
144
|
def self.read_only attribute_options
|
136
145
|
read_option(attribute_options) && !write_option(attribute_options)
|
@@ -148,7 +157,7 @@ module Aquarium
|
|
148
157
|
attribute_options.include?(:writers) || attribute_options.include?(:writer)
|
149
158
|
end
|
150
159
|
|
151
|
-
%w[types objects methods attributes method_options attribute_options].each do |name|
|
160
|
+
%w[types objects join_points methods attributes method_options attribute_options].each do |name|
|
152
161
|
class_eval(<<-EOF, __FILE__, __LINE__)
|
153
162
|
def #{name}_given
|
154
163
|
@specification[:#{name}]
|
@@ -164,7 +173,7 @@ module Aquarium
|
|
164
173
|
|
165
174
|
def init_candidate_types
|
166
175
|
explicit_types, type_regexps_or_names = @specification[:types].partition do |type|
|
167
|
-
|
176
|
+
is_type? type
|
168
177
|
end
|
169
178
|
@candidate_types = Aquarium::Finders::TypeFinder.new.find :types => type_regexps_or_names
|
170
179
|
@candidate_types.append_matched(make_hash(explicit_types) {|x| Set.new([])}) # Append already-known types
|
@@ -175,33 +184,53 @@ module Aquarium
|
|
175
184
|
@specification[:objects].each {|o| object_hash[o] = Set.new([])}
|
176
185
|
@candidate_objects = Aquarium::Finders::FinderResult.new object_hash
|
177
186
|
end
|
187
|
+
|
188
|
+
def init_candidate_join_points
|
189
|
+
@candidate_join_points = Aquarium::Finders::FinderResult.new
|
190
|
+
@specification[:join_points].each do |jp|
|
191
|
+
if jp.exists?
|
192
|
+
@candidate_join_points.matched[jp] = Set.new([])
|
193
|
+
else
|
194
|
+
@candidate_join_points.not_matched[jp] = Set.new([])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
178
198
|
|
179
199
|
def init_join_points
|
180
200
|
@join_points_matched = Set.new
|
181
201
|
@join_points_not_matched = Set.new
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
results = find_methods_for_objects make_all_method_names
|
186
|
-
add_join_points @join_points_matched, results.matched, :object
|
187
|
-
add_join_points @join_points_not_matched, results.not_matched, :object
|
202
|
+
find_join_points_for :type, candidate_types, make_all_method_names
|
203
|
+
find_join_points_for :object, candidate_objects, make_all_method_names
|
204
|
+
add_join_points_for_candidate_join_points
|
188
205
|
end
|
189
206
|
|
190
|
-
def
|
191
|
-
|
192
|
-
Aquarium::Finders::MethodFinder.new.find :types => candidate_types.matched_keys,
|
193
|
-
:methods => which_methods,
|
194
|
-
:options => @specification[:method_options].to_a
|
207
|
+
def is_type? candidate_type
|
208
|
+
candidate_type.kind_of?(Module) || candidate_type.kind_of?(Class)
|
195
209
|
end
|
196
|
-
|
197
|
-
def
|
198
|
-
|
199
|
-
|
210
|
+
|
211
|
+
def add_join_points_for_candidate_join_points
|
212
|
+
@join_points_matched += @candidate_join_points.matched.keys
|
213
|
+
@join_points_not_matched += @candidate_join_points.not_matched.keys
|
214
|
+
end
|
215
|
+
|
216
|
+
def find_join_points_for type_or_object_sym, candidates, method_names
|
217
|
+
results = find_methods_for type_or_object_sym, candidates, method_names
|
218
|
+
add_join_points results, type_or_object_sym
|
219
|
+
end
|
220
|
+
|
221
|
+
def find_methods_for type_or_object_sym, candidates, which_methods
|
222
|
+
return Aquarium::Finders::FinderResult::NIL_OBJECT if candidates.matched.size == 0
|
223
|
+
Aquarium::Finders::MethodFinder.new.find type_or_object_sym => candidates.matched_keys,
|
200
224
|
:methods => which_methods,
|
201
225
|
:options => @specification[:method_options].to_a
|
202
226
|
end
|
203
227
|
|
204
|
-
def add_join_points
|
228
|
+
def add_join_points search_results, type_or_object_sym
|
229
|
+
add_join_points_to @join_points_matched, search_results.matched, type_or_object_sym
|
230
|
+
add_join_points_to @join_points_not_matched, search_results.not_matched, type_or_object_sym
|
231
|
+
end
|
232
|
+
|
233
|
+
def add_join_points_to which_join_points_list, results_hash, type_or_object_sym
|
205
234
|
instance_method = @specification[:method_options].include?(:class) ? false : true
|
206
235
|
results_hash.each_pair do |type_or_object, method_name_list|
|
207
236
|
method_name_list.each do |method_name|
|
@@ -30,8 +30,9 @@ module Aquarium
|
|
30
30
|
message = handle_message_arg args
|
31
31
|
Aspect.new make_args(:around, *args) do |jp, *params|
|
32
32
|
DesignByContract.test_condition "invariant failure (before invocation): #{message}", jp, *params, &contract_block
|
33
|
-
jp.proceed
|
33
|
+
result = jp.proceed
|
34
34
|
DesignByContract.test_condition "invariant failure (after invocation): #{message}", jp, *params, &contract_block
|
35
|
+
result
|
35
36
|
end
|
36
37
|
end
|
37
38
|
|
@@ -29,6 +29,26 @@ module Aquarium
|
|
29
29
|
#
|
30
30
|
# Actually, there is actually no difference between <tt>:types</tt>,
|
31
31
|
# <tt>:type</tt>, <tt>:names</tt>, and <tt>:name</tt>. The extra forms are "sugar"...
|
32
|
+
#
|
33
|
+
# Because of the special sigificance of the module ("namespace") separator "::", the rules
|
34
|
+
# for the regular expressions are as follows. Assume that "subexp" is a "sub regular
|
35
|
+
# expression" that results if you split on the separator "::".
|
36
|
+
#
|
37
|
+
# A full regexp with no "::"::
|
38
|
+
# Allow partial matches, <i>i.e.</i>, as if you wrote <tt>/^.*#{regexp}.*$/.</tt>
|
39
|
+
#
|
40
|
+
# A subexp before the first "::"::
|
41
|
+
# It behaves as <tt>/^.*#{subexp}::.../</tt>, meaning that the end of "subexp"
|
42
|
+
# must be followed by "::".
|
43
|
+
#
|
44
|
+
# A subexp after the last "::"::
|
45
|
+
# It behaves as <tt>/...::#{subexp}$/</tt>, meaning that the beginning of "subexp"
|
46
|
+
# must immediately follow a "::".
|
47
|
+
#
|
48
|
+
# For a subexp between two "::"::
|
49
|
+
# It behaves as <tt>/...::#{subexp}::.../</tt>, meaning that the subexp must match
|
50
|
+
# the whole name between the "::" exactly.
|
51
|
+
#
|
32
52
|
def find options = {}
|
33
53
|
result = Aquarium::Finders::FinderResult.new
|
34
54
|
unknown_options = []
|
@@ -41,7 +61,7 @@ module Aquarium
|
|
41
61
|
end
|
42
62
|
end
|
43
63
|
raise Aquarium::Utils::InvalidOptions.new("Unknown options: #{unknown_options.inspect}.") if unknown_options.size > 0
|
44
|
-
|
64
|
+
result
|
45
65
|
end
|
46
66
|
|
47
67
|
# For a name (not a regular expression), return the corresponding type.
|
@@ -68,11 +88,10 @@ module Aquarium
|
|
68
88
|
expressions.each do |expression|
|
69
89
|
expr = strip expression
|
70
90
|
next if empty expr
|
71
|
-
|
72
|
-
|
73
|
-
result.append_matched result_for_expression
|
91
|
+
if expr.kind_of? Regexp
|
92
|
+
result << find_namespace_matched(expr)
|
74
93
|
else
|
75
|
-
result
|
94
|
+
result << find_by_name(expr)
|
76
95
|
end
|
77
96
|
end
|
78
97
|
result
|
@@ -94,24 +113,33 @@ module Aquarium
|
|
94
113
|
end
|
95
114
|
|
96
115
|
def find_namespace_matched expression
|
97
|
-
|
116
|
+
expr = expression.kind_of?(Regexp) ? expression.source : expression.to_s
|
117
|
+
return nil if expr.empty?
|
98
118
|
found_types = [Module]
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
found_types = find_next_types found_types, subexp
|
119
|
+
split_expr = expr.split("::")
|
120
|
+
split_expr.each_with_index do |subexp, index|
|
121
|
+
next if subexp.size == 0
|
122
|
+
found_types = find_next_types found_types, subexp, (index == 0), (index == (split_expr.size - 1))
|
103
123
|
break if found_types.size == 0
|
104
124
|
end
|
105
|
-
|
125
|
+
if found_types.size > 0
|
126
|
+
Aquarium::Finders::FinderResult.new make_return_hash(found_types, [])
|
127
|
+
else
|
128
|
+
Aquarium::Finders::FinderResult.new :not_matched => {expression => Set.new([])}
|
129
|
+
end
|
106
130
|
end
|
107
131
|
|
108
|
-
def find_next_types
|
109
|
-
# grep <parent>.constants because "
|
132
|
+
def find_next_types enclosing_types, subexp, suppress_lh_ctrl_a, suppress_rh_ctrl_z
|
133
|
+
# grep <parent>.constants because "subexp" may be a regexp string!.
|
110
134
|
# Then use const_get to get the type itself.
|
111
135
|
found_types = []
|
112
|
-
|
113
|
-
|
114
|
-
|
136
|
+
lhs = suppress_lh_ctrl_a ? "" : "\\A"
|
137
|
+
rhs = suppress_rh_ctrl_z ? "" : "\\Z"
|
138
|
+
regexp = /#{lhs}#{subexp}#{rhs}/
|
139
|
+
enclosing_types.each do |parent|
|
140
|
+
parent.constants.grep(regexp).each do |m|
|
141
|
+
found_types << get_type_from_parent(parent, m, regexp)
|
142
|
+
end
|
115
143
|
end
|
116
144
|
found_types
|
117
145
|
end
|
@@ -122,6 +150,17 @@ module Aquarium
|
|
122
150
|
found.each {|x| h[x] = Set.new([])}
|
123
151
|
h
|
124
152
|
end
|
153
|
+
|
154
|
+
protected
|
155
|
+
def get_type_from_parent parent, name, regexp
|
156
|
+
begin
|
157
|
+
parent.const_get(name)
|
158
|
+
rescue => e
|
159
|
+
msg = "ERROR: for enclosing type '#{parent.inspect}', #{parent.inspect}.constants.grep(/#{regexp.source}/) returned a list including #{name.inspect}."
|
160
|
+
msg += "However, #{parent.inspect}.const_get('#{name}') raised exception #{e}. Please report this bug to the Aquarium team. Thanks."
|
161
|
+
raise e.exception(msg)
|
162
|
+
end
|
163
|
+
end
|
125
164
|
end
|
126
165
|
end
|
127
166
|
end
|
@@ -37,7 +37,7 @@ module Aquarium
|
|
37
37
|
limits = class_or_instance_only.nil? ? [:instance_method_only, :class_method_only] : [class_or_instance_only]
|
38
38
|
meta_method_suffixes = []
|
39
39
|
limits.each do |limit|
|
40
|
-
if (
|
40
|
+
if (is_type? type_or_instance)
|
41
41
|
meta_method_suffixes << "instance_methods" if limit == :instance_method_only
|
42
42
|
meta_method_suffixes << "methods" if limit == :class_method_only
|
43
43
|
else
|
@@ -47,6 +47,10 @@ module Aquarium
|
|
47
47
|
meta_method_suffixes
|
48
48
|
end
|
49
49
|
|
50
|
+
def self.is_type? type_or_instance
|
51
|
+
type_or_instance.kind_of?(Class) || type_or_instance.kind_of?(Module)
|
52
|
+
end
|
53
|
+
|
50
54
|
def self.find_method2 type_or_instance, method_sym, meta_method
|
51
55
|
type_or_instance.send(meta_method, method_sym.to_s)
|
52
56
|
end
|
data/lib/aquarium/version.rb
CHANGED
@@ -72,6 +72,18 @@ describe Aspect, "#new arguments for specifying the types and methods" do
|
|
72
72
|
aspect1.advice.should eql(aspect2.advice)
|
73
73
|
aspect1.unadvise
|
74
74
|
end
|
75
|
+
|
76
|
+
it "should advise an equivalent join point when :type => T and :method => m is used or :pointcut => join_point is used, where join_point matches :type => T and :method => m." do
|
77
|
+
# pending "working on Pointcut.new first."
|
78
|
+
advice = proc {|jp,*args| "advice"}
|
79
|
+
aspect1 = Aspect.new :after, :type => Watchful, :method => :public_watchful_method, &advice
|
80
|
+
join_point = Aquarium::Aspects::JoinPoint.new :type => Watchful, :method => :public_watchful_method
|
81
|
+
aspect2 = Aspect.new :after, :pointcut => join_point, &advice
|
82
|
+
aspect1.join_points_matched.should eql(aspect2.join_points_matched)
|
83
|
+
aspect1.advice.should eql(aspect2.advice)
|
84
|
+
aspect1.unadvise
|
85
|
+
end
|
86
|
+
|
75
87
|
end
|
76
88
|
|
77
89
|
describe Aspect, "#new arguments for specifying the objects and methods" do
|
@@ -805,17 +805,75 @@ describe Aspect, "#new with :around advice" do
|
|
805
805
|
end
|
806
806
|
|
807
807
|
it "should advise subclass invocations of methods advised in the superclass." do
|
808
|
+
module AdvisingSuperClass
|
809
|
+
class SuperClass
|
810
|
+
def public_method *args
|
811
|
+
yield *args if block_given?
|
812
|
+
end
|
813
|
+
protected
|
814
|
+
def protected_method *args
|
815
|
+
yield *args if block_given?
|
816
|
+
end
|
817
|
+
private
|
818
|
+
def private_method *args
|
819
|
+
yield *args if block_given?
|
820
|
+
end
|
821
|
+
end
|
822
|
+
class SubClass < SuperClass
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
808
826
|
context = nil
|
809
|
-
@aspect = Aspect.new :around, :pointcut => {:type =>
|
827
|
+
@aspect = Aspect.new :around, :pointcut => {:type => AdvisingSuperClass::SuperClass, :methods => [:public_method]} do |jp, *args|
|
810
828
|
context = jp.context
|
811
829
|
end
|
812
|
-
child =
|
830
|
+
child = AdvisingSuperClass::SubClass.new
|
813
831
|
public_block_called = false
|
814
832
|
protected_block_called = false
|
815
833
|
private_block_called = false
|
816
|
-
child.
|
817
|
-
child.send(:
|
818
|
-
child.send(:
|
834
|
+
child.public_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| fail }
|
835
|
+
child.send(:protected_method, :b1, :b2, :b3) {|*args| protected_block_called = true}
|
836
|
+
child.send(:private_method, :c1, :c2, :c3) {|*args| private_block_called = true}
|
837
|
+
public_block_called.should be_false # proceed is never called!
|
838
|
+
protected_block_called.should be_true
|
839
|
+
private_block_called.should be_true
|
840
|
+
context.advised_object.should == child
|
841
|
+
context.advice_kind.should == :around
|
842
|
+
context.returned_value.should == nil
|
843
|
+
context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
|
844
|
+
context.raised_exception.should == nil
|
845
|
+
end
|
846
|
+
|
847
|
+
it "should advise subclass invocations of methods advised in the subclass that are defined in the superclass." do
|
848
|
+
module AdvisingSubClass
|
849
|
+
class SuperClass
|
850
|
+
def public_method *args
|
851
|
+
yield *args if block_given?
|
852
|
+
end
|
853
|
+
protected
|
854
|
+
def protected_method *args
|
855
|
+
yield *args if block_given?
|
856
|
+
end
|
857
|
+
private
|
858
|
+
def private_method *args
|
859
|
+
yield *args if block_given?
|
860
|
+
end
|
861
|
+
end
|
862
|
+
class SubClass < SuperClass
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
context = nil
|
867
|
+
@aspect = Aspect.new :around, :pointcut => {:type => AdvisingSubClass::SuperClass, :methods => [:public_method]} do |jp, *args|
|
868
|
+
context = jp.context
|
869
|
+
end
|
870
|
+
child = AdvisingSubClass::SubClass.new
|
871
|
+
public_block_called = false
|
872
|
+
protected_block_called = false
|
873
|
+
private_block_called = false
|
874
|
+
child.public_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| fail }
|
875
|
+
child.send(:protected_method, :b1, :b2, :b3) {|*args| protected_block_called = true}
|
876
|
+
child.send(:private_method, :c1, :c2, :c3) {|*args| private_block_called = true}
|
819
877
|
public_block_called.should be_false # proceed is never called!
|
820
878
|
protected_block_called.should be_true
|
821
879
|
private_block_called.should be_true
|
@@ -874,7 +932,7 @@ describe Aspect, "#new with :around advice" do
|
|
874
932
|
watchful.public_watchful_method_args.should == [:a4, :a5, :a6]
|
875
933
|
end
|
876
934
|
|
877
|
-
it "should return the value returned by the advice" do
|
935
|
+
it "should return the value returned by the advice, NOT the value returned by the advised join point!" do
|
878
936
|
class ReturningValue
|
879
937
|
def doit args
|
880
938
|
args + ["d"]
|
@@ -883,9 +941,28 @@ describe Aspect, "#new with :around advice" do
|
|
883
941
|
ary = %w[a b c]
|
884
942
|
ReturningValue.new.doit(ary).should == %w[a b c d]
|
885
943
|
@aspect = Aspect.new :around, :type => ReturningValue, :method => :doit do |jp, *args|
|
886
|
-
|
944
|
+
jp.proceed
|
945
|
+
%w[aa bb cc]
|
887
946
|
end
|
888
|
-
ReturningValue.new.doit(ary).should == %w[aa
|
947
|
+
ReturningValue.new.doit(ary).should == %w[aa bb cc]
|
948
|
+
end
|
949
|
+
|
950
|
+
it "should return the value returned by the advised join point only if the advice returns the value" do
|
951
|
+
class ReturningValue
|
952
|
+
def doit args
|
953
|
+
args + ["d"]
|
954
|
+
end
|
955
|
+
end
|
956
|
+
ary = %w[a b c]
|
957
|
+
ReturningValue.new.doit(ary).should == %w[a b c d]
|
958
|
+
@aspect = Aspect.new :around, :type => ReturningValue, :method => :doit do |jp, *args|
|
959
|
+
begin
|
960
|
+
jp.proceed
|
961
|
+
ensure
|
962
|
+
%w[aa bb cc]
|
963
|
+
end
|
964
|
+
end
|
965
|
+
ReturningValue.new.doit(ary).should == %w[a b c d]
|
889
966
|
end
|
890
967
|
|
891
968
|
def do_around_spec *args_passed_to_proceed
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
3
|
+
require File.dirname(__FILE__) + '/../spec_example_classes'
|
4
|
+
require 'aquarium/aspects'
|
5
|
+
|
6
|
+
include Aquarium::Aspects
|
7
|
+
|
8
|
+
# Explicitly check that advising subtypes works correctly.
|
9
|
+
|
10
|
+
module SubTypeAspects
|
11
|
+
class Base
|
12
|
+
attr_reader :base_state
|
13
|
+
def doit *args
|
14
|
+
@base_state = args
|
15
|
+
yield args
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Derived < Base
|
20
|
+
attr_reader :derived_state
|
21
|
+
def doit *args
|
22
|
+
@derived_state = args
|
23
|
+
super
|
24
|
+
yield args
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Aspect, " when advising overridden methods that call super" do
|
30
|
+
after(:each) do
|
31
|
+
@aspect.unadvise if @aspect
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should correctly invoke and advise subclass and superclass methods." do
|
35
|
+
advised_types = []
|
36
|
+
@aspect = Aspect.new :before, :pointcut => {:types => /SubTypeAspects::.*/, :methods => :doit} do |jp, *args|
|
37
|
+
advised_types << jp.target_type
|
38
|
+
end
|
39
|
+
derived = SubTypeAspects::Derived.new
|
40
|
+
block_called = 0
|
41
|
+
derived.doit(:a1, :a2, :a3) { |*args| block_called += 1 }
|
42
|
+
block_called.should == 2
|
43
|
+
advised_types.should == [SubTypeAspects::Derived, SubTypeAspects::Base]
|
44
|
+
derived.base_state.should == [:a1, :a2, :a3]
|
45
|
+
derived.derived_state.should == [:a1, :a2, :a3]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|