aquarium 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/CHANGES
CHANGED
@@ -1,3 +1,67 @@
|
|
1
|
+
== Version 0.1.6
|
2
|
+
|
3
|
+
Bug fixes:
|
4
|
+
14353 Advising subclass method that calls super raises exception when method executed
|
5
|
+
14356 Regexps for types must cover the whole name, which is inconsistent with method/attribute regexps
|
6
|
+
14384 Design by Contract "extra" does not return correct value "invar" handling
|
7
|
+
13410 Fix funky navigation bar on website
|
8
|
+
|
9
|
+
14353 was kind of bad, but it's actually a Ruby bug with a good workaround. If you
|
10
|
+
advised a method that called "super", Ruby would use the wrong method name to lookup the
|
11
|
+
class in the parent. See the bug description for the details.
|
12
|
+
|
13
|
+
For 14356, type regular expressions now match on parts of names; they don't have to match
|
14
|
+
the whole name. The exception is regular expressions with module separators "::". In this
|
15
|
+
case, it seems to make more sense for the regular expression to be interpreted as follows:
|
16
|
+
If the expression is /A::B::C::D/, then for the the outermost types, the expression behaves
|
17
|
+
as /^.*A/, for the types between two "::", the expressions behave as /^B$/ and /^C$/, and
|
18
|
+
the trailing expression behaves as /D.*$/.
|
19
|
+
|
20
|
+
14384 was an easy mistake to make with "around" advice; you have to remember to return the
|
21
|
+
result of the "join_point.proceed" call, unless you specifically want to change the returned
|
22
|
+
value! Here are two ways to do it:
|
23
|
+
|
24
|
+
do_something_before(...)
|
25
|
+
result = join_point.proceed
|
26
|
+
do_something_after(...)
|
27
|
+
return result
|
28
|
+
|
29
|
+
or
|
30
|
+
|
31
|
+
begin
|
32
|
+
do_something_before(...)
|
33
|
+
join_point.proceed
|
34
|
+
ensure
|
35
|
+
do_something_after(...)
|
36
|
+
end
|
37
|
+
|
38
|
+
The latter approach looks "asymmetrical" and it will behave differently if "proceed" raises!
|
39
|
+
However, it eliminates the temporary, if you find that desirable.
|
40
|
+
|
41
|
+
Enhancements:
|
42
|
+
13407 Pick a better method name for JoinPoint#type, which hides the Module#type
|
43
|
+
14385 Pointcut.new should accept a :join_point => jp argument
|
44
|
+
14386 Aspect.new ..., :pointcut => should accept a join point object
|
45
|
+
14440 Add good warning message when "proceed" used for non-around advice
|
46
|
+
|
47
|
+
For 13407, new attribute methods have been added
|
48
|
+
* JoinPoint#target_type return the type that the join_point matches.
|
49
|
+
* JoinPoint#target_type= set the type that the join_point matches.
|
50
|
+
* JoinPoint#target_object return the object that the join_point matches.
|
51
|
+
* JoinPoint#target_object= set the object that the join_point matches.
|
52
|
+
|
53
|
+
The following, older methods are now deprecated and will be removed in the 0.2.0 release (#14053):
|
54
|
+
* JoinPoint#type
|
55
|
+
* JoinPoint#type=
|
56
|
+
* JoinPoint#object
|
57
|
+
* JoinPoint#object=
|
58
|
+
|
59
|
+
JoinPoint#type method is deprecated because it hides Module#type, which returns the type of
|
60
|
+
the corresponding object. For "symmetry", the other three methods are also now deprecated and
|
61
|
+
they will be removed in a future release. Until then, all will print a warning message to
|
62
|
+
STDOUT. (If you really want the type of what could be a JoinPoint object, you should use #class
|
63
|
+
anyway, as Module#type is also deprecated!)
|
64
|
+
|
1
65
|
== Version 0.1.5
|
2
66
|
|
3
67
|
Bug fixes:
|
data/README
CHANGED
@@ -29,6 +29,7 @@ Only around advice can prevent execution of the join point, except for the speci
|
|
29
29
|
|
30
30
|
=== Known Limitations
|
31
31
|
|
32
|
+
* You cannot advice "String", "Symbol" or instances there of, because trying to specify either one will be confused with naming a type.
|
32
33
|
* Concurrent type- and object-based advice can't be removed cleanly.
|
33
34
|
* See also the comparison with AspectJ behavior next.
|
34
35
|
* The API and wrapper DSL will probably evolve until the 1.0.0 release. Backwards compatibility will be maintained between releases as much as possible and translation tools will be provided, when necessary.
|
@@ -59,18 +60,20 @@ Here is an example that traces invocations of all public instance methods of the
|
|
59
60
|
|
60
61
|
require 'aquarium'
|
61
62
|
Aspect.new :around, :types => [Foo, Bar], :methods => :all do |execution_point, *args|
|
62
|
-
p "Entering: #{execution_point.
|
63
|
-
execution_point.proceed
|
64
|
-
p "Leaving: #{execution_point.
|
63
|
+
p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}"
|
64
|
+
result = execution_point.proceed
|
65
|
+
p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}"
|
66
|
+
result # block needs to return the result of the "proceed"!
|
65
67
|
end
|
66
68
|
|
67
69
|
The advice to execute at each join point is the block. The pointcut is the set of all public instance methods in Foo and Bar. (There are additional options available for specifying class methods, protected methods, etc.) Here is the same example using the convenience DSL that adds aspect methods to Object (available only if you require aquarium/aspects/dsl/object_dsl', since other toolkits, like Rails, define similar methods on Object!).
|
68
70
|
|
69
71
|
require 'aquarium/aspects/dsl/object_dsl'
|
70
72
|
around :types => [Foo, Bar], :methods => :all do |execution_point, *args|
|
71
|
-
p "Entering: #{execution_point.
|
72
|
-
execution_point.proceed
|
73
|
-
p "Leaving: #{execution_point.
|
73
|
+
p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}"
|
74
|
+
result = execution_point.proceed
|
75
|
+
p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}"
|
76
|
+
result # block needs to return the result of the "proceed"!
|
74
77
|
end
|
75
78
|
|
76
79
|
See "examples/method_tracing_example.rb" for a more detailed version of this example.
|
@@ -103,8 +106,9 @@ If you use the DSL inside a class and omit the :type(s) and :object(s) options,
|
|
103
106
|
class Foo
|
104
107
|
around :critical_operation do |execution_point, *args|
|
105
108
|
p "Entering: Foo#critical_operation"
|
106
|
-
execution_point.proceed
|
109
|
+
result = execution_point.proceed
|
107
110
|
p "Leaving: Foo#critical_operation"
|
111
|
+
result
|
108
112
|
end
|
109
113
|
end
|
110
114
|
|
@@ -156,6 +160,12 @@ very much like the :method options. Note that :all is NOT supported in this case
|
|
156
160
|
around :attributes = /^foo/, ...
|
157
161
|
around :attributes = [/^foo/, /bar$/], ...
|
158
162
|
|
163
|
+
You can specify a "Pointcut" that encapsulates one or more pre-defined Pointcuts or JoinPoints.
|
164
|
+
|
165
|
+
around :pointcut = pc, ... # for pre-defined pointcut "pc"
|
166
|
+
around :pointcuts = [pc, ...], ... # for pre-defined pointcut list
|
167
|
+
around :pointcut = {:type => T, :method => :m}, ... # same as around :type => T, :method => :m, ..
|
168
|
+
|
159
169
|
You can advice methods before execution:
|
160
170
|
|
161
171
|
before :types => ...
|
@@ -193,8 +203,9 @@ You can pass a block as the advice. Notice that all advice blocks and Procs (see
|
|
193
203
|
|
194
204
|
around :type => [...], :methods => :all do |join_point, *args|
|
195
205
|
advice_to_execute_before_the_jp
|
196
|
-
join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
|
206
|
+
result = join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
|
197
207
|
advice_to_execute_after_the_jp
|
208
|
+
result # return the result of the "proceed", unless you override the value.
|
198
209
|
end
|
199
210
|
around(:type => [...], :methods => :all) {|join_point, *args| ...} # (...) necessary for precedence...
|
200
211
|
|
data/UPGRADE
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
== Upgrading
|
1
|
+
== Upgrading to Aquarium-0.1.6
|
2
|
+
|
3
|
+
As described in the CHANGES, the JoinPoint#type, JoinPoint#type=, JoinPoint#object, and JoinPoint#object=
|
4
|
+
are now deprecated. Client code that uses these methods will still work, but warning messages will be
|
5
|
+
written to STDOUT. See CHANGES for more details.
|
6
|
+
|
7
|
+
== Upgrading to Aquarium-0.1.5
|
2
8
|
|
3
9
|
This is mostly a bug-fix release, but it did have to introduce one API change, as described in the
|
4
10
|
CHANGES. In particular, the aspect "DSL" methods are no longer automatically to Object, as some of
|
@@ -38,10 +38,14 @@ bar1.do_something_else :b3, :b4
|
|
38
38
|
|
39
39
|
include Aquarium::Aspects
|
40
40
|
|
41
|
-
Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :all,
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :all,
|
42
|
+
:method_options => :suppress_ancestor_methods do |execution_point, *args|
|
43
|
+
begin
|
44
|
+
p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
45
|
+
execution_point.proceed
|
46
|
+
ensure
|
47
|
+
p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
p "After advising the methods. Notice that #intialize isn't advised:"
|
@@ -51,10 +55,16 @@ foo2.do_it :b5, :b6
|
|
51
55
|
bar1 = Aquarium::Bar.new :a7, :a8
|
52
56
|
bar1.do_something_else :b7, :b8
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
+
# The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
|
59
|
+
# of the "proceed", rather than the value returned by "p"!
|
60
|
+
Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize,
|
61
|
+
:method_options => :private do |execution_point, *args|
|
62
|
+
begin
|
63
|
+
p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
64
|
+
execution_point.proceed
|
65
|
+
ensure
|
66
|
+
p "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
67
|
+
end
|
58
68
|
end
|
59
69
|
|
60
70
|
p "After advising the private methods. Notice that #intialize is advised:"
|
@@ -54,13 +54,18 @@ end
|
|
54
54
|
|
55
55
|
describe "An example with advice on the public instance methods (excluding ancestor methods) of Foo" do
|
56
56
|
it "should trace all calls to the public methods defined by Foo" do
|
57
|
+
# The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
|
58
|
+
# of the "proceed", rather than the value returned by "p"!
|
57
59
|
aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Foo, :methods => :all, :method_options => :suppress_ancestor_methods do |execution_point, *args|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
begin
|
61
|
+
o = execution_point.context.advised_object
|
62
|
+
o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
63
|
+
execution_point.proceed
|
64
|
+
ensure
|
65
|
+
o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
66
|
+
end
|
62
67
|
end
|
63
|
-
|
68
|
+
|
64
69
|
foo = Aquarium::Foo.new :a5, :a6
|
65
70
|
foo.do_it :b5, :b6
|
66
71
|
foo.logged_messages.size.should == 4
|
@@ -75,12 +80,15 @@ end
|
|
75
80
|
describe "An example with advice on the public instance methods (excluding ancestor methods) of Bar" do
|
76
81
|
it "should not trace any calls to the public methods defined by the included BarModule" do
|
77
82
|
aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => :all, :method_options => :suppress_ancestor_methods do |execution_point, *args|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
83
|
+
begin
|
84
|
+
o = execution_point.context.advised_object
|
85
|
+
o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
86
|
+
execution_point.proceed
|
87
|
+
ensure
|
88
|
+
o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
89
|
+
end
|
82
90
|
end
|
83
|
-
|
91
|
+
|
84
92
|
bar = Aquarium::Bar.new :a7, :a8
|
85
93
|
bar.do_something_else :b7, :b8
|
86
94
|
bar.logged_messages.size.should == 2
|
@@ -91,12 +99,15 @@ end
|
|
91
99
|
describe "An example with advice on the public instance methods (including ancestor methods) of Bar" do
|
92
100
|
it "should trace all calls to the public methods defined by the included BarModule" do
|
93
101
|
aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => /^do_/ do |execution_point, *args|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
102
|
+
begin
|
103
|
+
o = execution_point.context.advised_object
|
104
|
+
o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
105
|
+
execution_point.proceed
|
106
|
+
ensure
|
107
|
+
o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
108
|
+
end
|
98
109
|
end
|
99
|
-
|
110
|
+
|
100
111
|
bar = Aquarium::Bar.new :a9, :a10
|
101
112
|
bar.do_something_else :b9, :b10
|
102
113
|
bar.logged_messages.size.should == 4
|
@@ -113,12 +124,15 @@ describe "An example with advice on the private initialize method of Foo and Bar
|
|
113
124
|
it "should trace all calls to initialize" do
|
114
125
|
before_methods = Aquarium::Foo.private_instance_methods.sort #- Object.private_methods.sort
|
115
126
|
aspect = Aquarium::Aspects::Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize, :method_options => :private do |execution_point, *args|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
127
|
+
begin
|
128
|
+
o = execution_point.context.advised_object
|
129
|
+
o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
130
|
+
execution_point.proceed
|
131
|
+
ensure
|
132
|
+
o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
|
133
|
+
end
|
120
134
|
end
|
121
|
-
|
135
|
+
|
122
136
|
foo = Aquarium::Foo.new :a11, :a12
|
123
137
|
foo.do_it :b11, :b12
|
124
138
|
foo.logged_messages.size.should == 4
|
@@ -74,17 +74,25 @@ module Aquarium
|
|
74
74
|
NIL_OBJECT = Aquarium::Utils::NilObject.new
|
75
75
|
end
|
76
76
|
|
77
|
+
# When invoking the original method, we use object.send(original_method_name, *args)
|
78
|
+
# rather than object.method(...).call(*args). The latter fails when the original method
|
79
|
+
# calls super. This is a Ruby bug: http://www.ruby-forum.com/topic/124276
|
77
80
|
class NoAdviceChainNode < AdviceChainNode
|
78
81
|
# Note that we extract the block passed to the original method call, if any,
|
79
82
|
# from the context and pass it to method invocation.
|
80
83
|
def initialize options = {}
|
81
84
|
super(options) { |jp, *args|
|
82
85
|
block_for_method = jp.context.block_for_method
|
83
|
-
invoking_object = jp.instance_method? ? jp.context.advised_object : jp.
|
86
|
+
invoking_object = jp.instance_method? ? jp.context.advised_object : jp.target_type
|
84
87
|
method = invoking_object.method(@alias_method_name)
|
85
88
|
block_for_method.nil? ?
|
86
|
-
|
87
|
-
|
89
|
+
invoking_object.send(@alias_method_name, *args) :
|
90
|
+
invoking_object.send(@alias_method_name, *args, &block_for_method)
|
91
|
+
# Buggy!!
|
92
|
+
# method = invoking_object.method(@alias_method_name)
|
93
|
+
# block_for_method.nil? ?
|
94
|
+
# method.call(*args) :
|
95
|
+
# method.call(*args, &block_for_method)
|
88
96
|
}
|
89
97
|
end
|
90
98
|
end
|
@@ -60,7 +60,7 @@ module Aquarium
|
|
60
60
|
# <tt>:pointcut => pointcut || [pointcut_list]</tt>::
|
61
61
|
# <tt>:within_pointcut => pointcut || [pointcut_list]</tt>::
|
62
62
|
# <tt>:within_pointcuts => pointcut || [pointcut_list]</tt>::
|
63
|
-
# One or an array of Pointcut objects. Mutually-exclusive with the :types, :objects,
|
63
|
+
# One or an array of Pointcut or JoinPoint objects. Mutually-exclusive with the :types, :objects,
|
64
64
|
# :methods, :attributes, :method_options, and :attribute_options parameters.
|
65
65
|
#
|
66
66
|
# <tt>:types => type || [type_list]</tt>::
|
@@ -177,6 +177,8 @@ module Aquarium
|
|
177
177
|
pointcuts_given.each do |pointcut|
|
178
178
|
if pointcut.kind_of?(Aquarium::Aspects::Pointcut)
|
179
179
|
pointcuts << pointcut
|
180
|
+
elsif pointcut.kind_of?(Aquarium::Aspects::JoinPoint)
|
181
|
+
pointcuts << Aquarium::Aspects::Pointcut.new(:join_point => pointcut)
|
180
182
|
else
|
181
183
|
pointcuts << Aquarium::Aspects::Pointcut.new(pointcut)
|
182
184
|
end
|
@@ -236,7 +238,7 @@ module Aquarium
|
|
236
238
|
end
|
237
239
|
|
238
240
|
def add_advice_framework join_point
|
239
|
-
type_to_advise = join_point.
|
241
|
+
type_to_advise = join_point.target_type || (class << join_point.target_object; self; end)
|
240
242
|
alias_method_name = (saved_method_name join_point).intern
|
241
243
|
return if private_method_defined? join_point.type_or_object, alias_method_name
|
242
244
|
type_to_advise.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
|
@@ -270,8 +272,8 @@ module Aquarium
|
|
270
272
|
# of the advised classes. This also means that we have to use the Class.method(name).call()
|
271
273
|
# idiom when invoking it.
|
272
274
|
def alias_original_method_text alias_method_name, join_point
|
273
|
-
self_name = join_point.
|
274
|
-
target_self = join_point.instance_method? ? "self" : join_point.
|
275
|
+
self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
|
276
|
+
target_self = join_point.instance_method? ? "self" : join_point.target_type.name
|
275
277
|
<<-EOF
|
276
278
|
alias_method :#{alias_method_name}, :#{join_point.method_name}
|
277
279
|
def #{join_point.method_name} *args, &block_for_method
|
@@ -286,7 +288,7 @@ module Aquarium
|
|
286
288
|
end
|
287
289
|
|
288
290
|
def unalias_original_method_text alias_method_name, join_point
|
289
|
-
self_name = join_point.
|
291
|
+
self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
|
290
292
|
<<-EOF
|
291
293
|
alias_method :#{join_point.method_name}, :#{alias_method_name}
|
292
294
|
#{join_point.visibility.to_s} :#{join_point.method_name}
|
@@ -295,7 +297,7 @@ module Aquarium
|
|
295
297
|
end
|
296
298
|
|
297
299
|
def remove_advice_chain_class_variable_text alias_method_name, join_point
|
298
|
-
self_name = join_point.
|
300
|
+
self_name = join_point.target_type.nil? ? "self" : join_point.target_type.name
|
299
301
|
<<-EOF
|
300
302
|
advice_chain_name = :@@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}
|
301
303
|
remove_class_variable advice_chain_name
|
@@ -345,7 +347,7 @@ module Aquarium
|
|
345
347
|
|
346
348
|
def restore_type_method join_point
|
347
349
|
alias_method_name = (saved_method_name join_point).intern
|
348
|
-
join_point.
|
350
|
+
join_point.target_type.class_eval(<<-EVAL_WRAPPER, __FILE__, __LINE__)
|
349
351
|
#{static_method_prefix join_point.instance_method?}
|
350
352
|
#{unalias_original_method_text alias_method_name, join_point}
|
351
353
|
#{static_method_suffix join_point.instance_method?}
|
@@ -358,14 +360,14 @@ module Aquarium
|
|
358
360
|
|
359
361
|
def restore_object_method join_point
|
360
362
|
saved = saved_method_name join_point
|
361
|
-
singleton = class << join_point.
|
363
|
+
singleton = class << join_point.target_object; self; end
|
362
364
|
singleton.class_eval do
|
363
365
|
alias_method join_point.method_name, saved
|
364
366
|
public join_point.method_name
|
365
367
|
undef_method saved.intern
|
366
368
|
end
|
367
369
|
advice_chain_name = "@#{Aspect.advice_chain_attr_name join_point.type_or_object, join_point.method_name}".intern
|
368
|
-
join_point.
|
370
|
+
join_point.target_object.method(:remove_instance_variable).call advice_chain_name
|
369
371
|
end
|
370
372
|
|
371
373
|
def self.set_advice_chain type_or_object, method_name, advice_chain
|
@@ -26,7 +26,7 @@ module Aquarium
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def proceed enclosing_join_point, *args, &block
|
29
|
-
raise "JoinPoint#proceed can
|
29
|
+
raise "It looks like you tried to call \"JoinPoint#proceed\" (or \"JoinPoint::Context#proceed\") from within advice that isn't \"around\" advice. Only around advice can call proceed. (Specific error: JoinPoint#proceed cannot be called because no \"@proceed_proc\" attribute was set on the corresponding JoinPoint::Context object.)" unless @proceed_proc
|
30
30
|
args = parameters if (args.nil? or args.size == 0)
|
31
31
|
enclosing_join_point.context.block_for_method = block if block
|
32
32
|
proceed_proc.call enclosing_join_point, *args
|
@@ -67,30 +67,65 @@ module Aquarium
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
|
71
|
-
|
70
|
+
%w[type object].each do |attr|
|
71
|
+
class_eval <<-EOF
|
72
|
+
# Deprecated, as JoinPoint#type overrides Module#type in a non-substitutable way!
|
73
|
+
# JoinPoint#target_#{attr} will be removed in the next release.
|
74
|
+
# Use JoinPoint#target_#{attr} instead
|
75
|
+
def #{attr}
|
76
|
+
p "WARNING: JoinPoint##{attr} is deprecated. It will be removed in the next release."
|
77
|
+
target_#{attr}
|
78
|
+
end
|
79
|
+
# Deprecated
|
80
|
+
def #{attr}= new_#{attr}
|
81
|
+
p "WARNING: JoinPoint##{attr}= is deprecated. It will be removed in the next release."
|
82
|
+
target_#{attr}= new_#{attr}
|
83
|
+
end
|
84
|
+
EOF
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_accessor :target_type, :target_object, :method_name, :visibility, :context
|
88
|
+
attr_reader :instance_or_class_method
|
89
|
+
|
90
|
+
# For "symmetry" with JoinPoint#target_type
|
91
|
+
alias_method :object, :target_object
|
92
|
+
|
93
|
+
# For "symmetry" with JoinPoint#target_type=
|
94
|
+
alias_method :object=, :target_object=
|
95
|
+
|
72
96
|
def instance_method?
|
73
97
|
@instance_method
|
74
98
|
end
|
75
99
|
|
100
|
+
def class_method?
|
101
|
+
!@instance_method
|
102
|
+
end
|
103
|
+
|
76
104
|
def initialize options = {}
|
77
|
-
@
|
78
|
-
@
|
79
|
-
@method_name
|
105
|
+
@target_type = options[:type]
|
106
|
+
@target_object = options[:object]
|
107
|
+
@method_name = options[:method_name] || options[:method]
|
80
108
|
@instance_method = options[:instance_method]
|
81
|
-
class_method
|
109
|
+
class_method = options[:class_method].nil? ? false : options[:class_method]
|
82
110
|
@instance_method = (!class_method) if @instance_method.nil?
|
111
|
+
@instance_or_class_method = @instance_method ? :instance : :class
|
83
112
|
@visibility = Aquarium::Utils::MethodUtils.visibility(type_or_object, @method_name, class_or_instance_method_flag)
|
84
113
|
assert_valid options
|
85
114
|
end
|
86
115
|
|
87
|
-
# deal with warnings for Object#type being obsolete:
|
88
|
-
def get_type
|
89
|
-
@type
|
90
|
-
end
|
91
|
-
|
92
116
|
def type_or_object
|
93
|
-
@
|
117
|
+
@target_type || @target_object
|
118
|
+
end
|
119
|
+
|
120
|
+
alias_method :target_type_or_object, :type_or_object
|
121
|
+
|
122
|
+
def exists?
|
123
|
+
type_or_object_sym = @target_type ? :type : :object
|
124
|
+
results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object,
|
125
|
+
:method => method_name,
|
126
|
+
:options => [visibility, instance_or_class_method]
|
127
|
+
raise Exception("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
|
128
|
+
return results.matched.size == 1 ? true : false
|
94
129
|
end
|
95
130
|
|
96
131
|
# TODO while convenient, it couples advice-type information where it doesn't belong!
|
@@ -114,10 +149,10 @@ module Aquarium
|
|
114
149
|
return 0 if object_id == other.object_id
|
115
150
|
result = self.class <=> other.class
|
116
151
|
return result unless result == 0
|
117
|
-
result = (self.
|
152
|
+
result = (self.target_type.nil? && other.target_type.nil?) ? 0 : self.target_type.to_s <=> other.target_type.to_s
|
118
153
|
return result unless result == 0
|
119
|
-
result = (self.
|
120
|
-
result = self.
|
154
|
+
result = (self.target_object.object_id.nil? && other.target_object.object_id.nil?) ? 0 : self.target_object.object_id <=> other.target_object.object_id
|
155
|
+
result = self.target_object.object_id <=> other.target_object.object_id
|
121
156
|
return result unless result == 0
|
122
157
|
result = (self.method_name.nil? && other.method_name.nil?) ? 0 : self.method_name.to_s <=> other.method_name.to_s
|
123
158
|
return result unless result == 0
|
@@ -135,7 +170,7 @@ module Aquarium
|
|
135
170
|
alias :=== :eql?
|
136
171
|
|
137
172
|
def inspect
|
138
|
-
"JoinPoint: {
|
173
|
+
"JoinPoint: {target_type = #{target_type.inspect}, target_object = #{target_object.inspect}, method_name = #{method_name}, instance_method? #{instance_method?}, context = #{context.inspect}}"
|
139
174
|
end
|
140
175
|
|
141
176
|
alias :to_s :inspect
|
@@ -147,13 +182,13 @@ module Aquarium
|
|
147
182
|
def assert_valid options
|
148
183
|
error_message = ""
|
149
184
|
error_message << "Must specify a :method_name. " unless method_name
|
150
|
-
error_message << "Must specify either a :type or :object. " unless (
|
151
|
-
error_message << "Can't specify both a :type or :object. " if (
|
185
|
+
error_message << "Must specify either a :type or :object. " unless (target_type or target_object)
|
186
|
+
error_message << "Can't specify both a :type or :object. " if (target_type and target_object)
|
152
187
|
bad_attributes(error_message, options) if error_message.length > 0
|
153
188
|
end
|
154
189
|
|
155
190
|
def class_or_instance_method_flag
|
156
|
-
|
191
|
+
"#{instance_or_class_method.to_s}_method_only".intern
|
157
192
|
end
|
158
193
|
|
159
194
|
public
|