aquarium 0.1.8 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +59 -2
- data/README +33 -16
- data/RELEASE-PLAN +28 -5
- data/UPGRADE +11 -0
- data/examples/aspect_design_example.rb +2 -2
- data/examples/aspect_design_example_spec.rb +2 -2
- data/examples/design_by_contract_example.rb +4 -4
- data/examples/design_by_contract_example_spec.rb +4 -4
- data/examples/method_missing_example.rb +4 -1
- data/examples/method_missing_example_spec.rb +4 -1
- data/examples/method_tracing_example.rb +2 -2
- data/examples/method_tracing_example_spec.rb +16 -16
- data/lib/aquarium/aspects/advice.rb +47 -25
- data/lib/aquarium/aspects/aspect.rb +81 -39
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +1 -1
- data/lib/aquarium/aspects/exclusion_handler.rb +2 -2
- data/lib/aquarium/aspects/join_point.rb +28 -28
- data/lib/aquarium/aspects/pointcut.rb +61 -15
- data/lib/aquarium/extras/design_by_contract.rb +7 -7
- data/lib/aquarium/finders.rb +0 -1
- data/lib/aquarium/finders/method_finder.rb +10 -20
- data/lib/aquarium/finders/type_finder.rb +141 -75
- data/lib/aquarium/utils.rb +1 -0
- data/lib/aquarium/utils/logic_error.rb +9 -0
- data/lib/aquarium/utils/method_utils.rb +4 -3
- data/lib/aquarium/utils/nil_object.rb +1 -0
- data/lib/aquarium/utils/type_utils.rb +19 -0
- data/lib/aquarium/version.rb +2 -2
- data/spec/aquarium/aspects/advice_chain_node_spec.rb +2 -2
- data/spec/aquarium/aspects/advice_spec.rb +28 -5
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +522 -289
- data/spec/aquarium/aspects/aspect_spec.rb +59 -41
- data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +7 -7
- data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +2 -2
- data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -2
- data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
- data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +34 -34
- data/spec/aquarium/aspects/join_point_spec.rb +79 -0
- data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +13 -3
- data/spec/aquarium/aspects/pointcut_spec.rb +310 -63
- data/spec/aquarium/extras/design_by_contract_spec.rb +4 -4
- data/spec/aquarium/finders/method_finder_spec.rb +208 -54
- data/spec/aquarium/finders/type_finder_spec.rb +24 -88
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +206 -0
- data/spec/aquarium/spec_example_classes.rb +75 -12
- data/spec/aquarium/utils/logic_error_spec.rb +10 -0
- data/spec/aquarium/utils/type_utils_sample_classes.rb +203 -0
- data/spec/aquarium/utils/type_utils_spec.rb +47 -1
- metadata +48 -39
- data/lib/aquarium/finders/object_finder.rb +0 -75
- data/spec/aquarium/finders/object_finder_spec.rb +0 -231
@@ -26,6 +26,7 @@ module Aquarium
|
|
26
26
|
class AdviceChainNode
|
27
27
|
include Enumerable
|
28
28
|
def initialize options = {}, &proc_block
|
29
|
+
raise Aquarium::Utils::InvalidOptions.new("You must specify an advice block or Proc") if proc_block.nil?
|
29
30
|
@proc = Proc.new &proc_block
|
30
31
|
# assign :next_node and :static_join_point so the attributes are always created
|
31
32
|
options[:next_node] ||= nil
|
@@ -38,19 +39,28 @@ module Aquarium
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
|
-
def call jp, *args
|
42
|
+
def call jp, obj, *args
|
43
|
+
do_call @proc, "", jp, obj, *args
|
44
|
+
end
|
45
|
+
|
46
|
+
def invoke_original_join_point current_jp, obj, *args
|
47
|
+
do_call last, "While executing the original join_point: ", current_jp, obj, *args
|
48
|
+
end
|
49
|
+
|
50
|
+
def do_call proc_to, error_message_prefix, jp, obj, *args
|
42
51
|
begin
|
43
|
-
|
52
|
+
proc_to.call jp, obj, *args
|
44
53
|
rescue => e
|
45
54
|
class_or_instance_method_separater = jp.instance_method? ? "#" : "."
|
46
|
-
context_message = "Exception raised while executing \"#{jp.context.advice_kind}\" advice for \"#{jp.type_or_object.inspect}#{class_or_instance_method_separater}#{jp.method_name}\": "
|
55
|
+
context_message = error_message_prefix + "Exception raised while executing \"#{jp.context.advice_kind}\" advice for \"#{jp.type_or_object.inspect}#{class_or_instance_method_separater}#{jp.method_name}\": "
|
47
56
|
backtrace = e.backtrace
|
48
57
|
e2 = e.exception(context_message + e.message + " (join_point = #{jp.inspect})")
|
49
58
|
e2.set_backtrace backtrace
|
50
59
|
raise e2
|
51
60
|
end
|
52
61
|
end
|
53
|
-
|
62
|
+
protected :do_call
|
63
|
+
|
54
64
|
# Supports Enumerable
|
55
65
|
def each
|
56
66
|
node = self
|
@@ -59,7 +69,14 @@ module Aquarium
|
|
59
69
|
node = node.next_node
|
60
70
|
end
|
61
71
|
end
|
62
|
-
|
72
|
+
|
73
|
+
def last
|
74
|
+
last_node = nil
|
75
|
+
each { |node| last_node = node unless node.nil? } #; p "<br/>last_node: #{last_node.inspect .gsub(/\</,"<").gsub(/\>/,">")}"}
|
76
|
+
# p "<br/><br/>returning last_node: #{last_node.inspect .gsub(/\</,"<").gsub(/\>/,">")}"
|
77
|
+
last_node
|
78
|
+
end
|
79
|
+
|
63
80
|
def size
|
64
81
|
inject(0) {|memo, node| memo += 1}
|
65
82
|
end
|
@@ -73,6 +90,12 @@ module Aquarium
|
|
73
90
|
end
|
74
91
|
|
75
92
|
NIL_OBJECT = Aquarium::Utils::NilObject.new
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def invoking_object join_point
|
97
|
+
join_point.instance_method? ? join_point.context.advised_object : join_point.target_type
|
98
|
+
end
|
76
99
|
end
|
77
100
|
|
78
101
|
# When invoking the original method, we use object.send(original_method_name, *args)
|
@@ -82,13 +105,12 @@ module Aquarium
|
|
82
105
|
# Note that we extract the block passed to the original method call, if any,
|
83
106
|
# from the context and pass it to method invocation.
|
84
107
|
def initialize options = {}
|
85
|
-
super(options) { |jp, *args|
|
108
|
+
super(options) { |jp, obj, *args|
|
86
109
|
block_for_method = jp.context.block_for_method
|
87
|
-
|
88
|
-
method = invoking_object.method(@alias_method_name)
|
110
|
+
method = invoking_object(jp).method(@alias_method_name)
|
89
111
|
block_for_method.nil? ?
|
90
|
-
invoking_object.send(@alias_method_name, *args) :
|
91
|
-
invoking_object.send(@alias_method_name, *args, &block_for_method)
|
112
|
+
invoking_object(jp).send(@alias_method_name, *args) :
|
113
|
+
invoking_object(jp).send(@alias_method_name, *args, &block_for_method)
|
92
114
|
# Buggy!!
|
93
115
|
# method = invoking_object.method(@alias_method_name)
|
94
116
|
# block_for_method.nil? ?
|
@@ -100,20 +122,20 @@ module Aquarium
|
|
100
122
|
|
101
123
|
class BeforeAdviceChainNode < AdviceChainNode
|
102
124
|
def initialize options = {}
|
103
|
-
super(options) { |jp, *args|
|
125
|
+
super(options) { |jp, obj, *args|
|
104
126
|
before_jp = jp.make_current_context_join_point :advice_kind => :before
|
105
|
-
advice.call(before_jp, *args)
|
106
|
-
next_node.call(jp, *args)
|
127
|
+
advice.call(before_jp, obj, *args)
|
128
|
+
next_node.call(jp, obj, *args)
|
107
129
|
}
|
108
130
|
end
|
109
131
|
end
|
110
132
|
|
111
133
|
class AfterReturningAdviceChainNode < AdviceChainNode
|
112
134
|
def initialize options = {}
|
113
|
-
super(options) { |jp, *args|
|
114
|
-
returned_value = next_node.call(jp, *args)
|
135
|
+
super(options) { |jp, obj, *args|
|
136
|
+
returned_value = next_node.call(jp, obj, *args)
|
115
137
|
next_jp = jp.make_current_context_join_point :advice_kind => :after_returning, :returned_value => returned_value
|
116
|
-
advice.call(next_jp, *args)
|
138
|
+
advice.call(next_jp, obj, *args)
|
117
139
|
next_jp.context.returned_value # allow advice to modify the returned value
|
118
140
|
}
|
119
141
|
end
|
@@ -124,13 +146,13 @@ module Aquarium
|
|
124
146
|
class AfterRaisingAdviceChainNode < AdviceChainNode
|
125
147
|
include Aquarium::Utils::ArrayUtils
|
126
148
|
def initialize options = {}
|
127
|
-
super(options) { |jp, *args|
|
149
|
+
super(options) { |jp, obj, *args|
|
128
150
|
begin
|
129
|
-
next_node.call(jp, *args)
|
151
|
+
next_node.call(jp, obj, *args)
|
130
152
|
rescue Object => raised_exception
|
131
153
|
if after_raising_exceptions_list_includes raised_exception
|
132
154
|
next_jp = jp.make_current_context_join_point :advice_kind => :after_raising, :raised_exception => raised_exception
|
133
|
-
advice.call(next_jp, *args)
|
155
|
+
advice.call(next_jp, obj, *args)
|
134
156
|
raised_exception = next_jp.context.raised_exception # allow advice to modify raised exception
|
135
157
|
end
|
136
158
|
raise raised_exception
|
@@ -151,17 +173,17 @@ module Aquarium
|
|
151
173
|
|
152
174
|
class AfterAdviceChainNode < AdviceChainNode
|
153
175
|
def initialize options = {}
|
154
|
-
super(options) { |jp, *args|
|
176
|
+
super(options) { |jp, obj, *args|
|
155
177
|
# advice.call is invoked in each bloc, rather than once in an "ensure" clause, so the invocation in the rescue class
|
156
178
|
# can allow the advice to change the exception that will be raised.
|
157
179
|
begin
|
158
|
-
returned_value = next_node.call(jp, *args)
|
180
|
+
returned_value = next_node.call(jp, obj, *args)
|
159
181
|
next_jp = jp.make_current_context_join_point :advice_kind => :after, :returned_value => returned_value
|
160
|
-
advice.call(next_jp, *args)
|
182
|
+
advice.call(next_jp, obj, *args)
|
161
183
|
next_jp.context.returned_value # allow advice to modify the returned value
|
162
184
|
rescue Object => raised_exception
|
163
185
|
next_jp = jp.make_current_context_join_point :advice_kind => :after, :raised_exception => raised_exception
|
164
|
-
advice.call(next_jp, *args)
|
186
|
+
advice.call(next_jp, obj, *args)
|
165
187
|
raise next_jp.context.raised_exception
|
166
188
|
end
|
167
189
|
}
|
@@ -170,9 +192,9 @@ module Aquarium
|
|
170
192
|
|
171
193
|
class AroundAdviceChainNode < AdviceChainNode
|
172
194
|
def initialize options = {}
|
173
|
-
super(options) { |jp, *args|
|
195
|
+
super(options) { |jp, obj, *args|
|
174
196
|
around_jp = jp.make_current_context_join_point :advice_kind => :around, :proceed_proc => next_node
|
175
|
-
advice.call(around_jp, *args)
|
197
|
+
advice.call(around_jp, obj, *args)
|
176
198
|
}
|
177
199
|
end
|
178
200
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'aquarium/extensions'
|
2
|
+
require 'aquarium/finders/type_finder'
|
2
3
|
require 'aquarium/utils'
|
3
4
|
require 'aquarium/aspects/advice'
|
4
5
|
require 'aquarium/aspects/exclusion_handler'
|
@@ -30,12 +31,15 @@ module Aquarium
|
|
30
31
|
attr_accessor :verbose, :log
|
31
32
|
attr_reader :specification, :pointcuts, :advice
|
32
33
|
|
34
|
+
OTHER_ALLOWED_OPTIONS_SINGULAR = %w[type_and_descendents type_and_ancestors exclude_type_and_descendents exclude_type_and_ancestors].map {|o| o.intern}
|
35
|
+
OTHER_ALLOWED_OPTIONS_PLURAL = %w[types_and_descendents types_and_ancestors exclude_types_and_descendents exclude_types_and_ancestors].map {|o| o.intern}
|
36
|
+
|
33
37
|
ALLOWED_OPTIONS_SINGULAR = %w[advice type object method attribute method_option attribute_option pointcut default_object
|
34
38
|
exclude_type exclude_object exclude_pointcut exclude_join_point exclude_method exclude_attribute noop].map {|o| o.intern}
|
35
39
|
|
36
|
-
ALLOWED_OPTIONS_PLURAL = ALLOWED_OPTIONS_SINGULAR.map {|o| "#{o}s".intern}
|
40
|
+
ALLOWED_OPTIONS_PLURAL = OTHER_ALLOWED_OPTIONS_PLURAL + ALLOWED_OPTIONS_SINGULAR.map {|o| "#{o.to_s}s".intern}
|
37
41
|
|
38
|
-
ALLOWED_OPTIONS = ALLOWED_OPTIONS_SINGULAR +
|
42
|
+
ALLOWED_OPTIONS = ALLOWED_OPTIONS_PLURAL + ALLOWED_OPTIONS_SINGULAR + OTHER_ALLOWED_OPTIONS_SINGULAR
|
39
43
|
|
40
44
|
ADVICE_OPTIONS_SYNONYMS_MAP = {
|
41
45
|
:call => :advice,
|
@@ -44,33 +48,38 @@ module Aquarium
|
|
44
48
|
}
|
45
49
|
|
46
50
|
ALLOWED_OPTIONS_SYNONYMS_MAP = {
|
47
|
-
:type
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
62
|
-
:
|
63
|
-
:
|
64
|
-
:
|
65
|
-
:
|
51
|
+
:type => :types,
|
52
|
+
:type_and_descendents => :types_and_descendents,
|
53
|
+
:type_and_ancestors => :types_and_ancestors,
|
54
|
+
:within_type => :types,
|
55
|
+
:within_types => :types,
|
56
|
+
:object => :objects,
|
57
|
+
:within_object => :objects,
|
58
|
+
:within_objects => :objects,
|
59
|
+
:method => :methods,
|
60
|
+
:within_method => :methods,
|
61
|
+
:within_methods => :methods,
|
62
|
+
:attribute => :attributes,
|
63
|
+
:pointcut => :pointcuts,
|
64
|
+
:within_pointcut => :pointcuts,
|
65
|
+
:within_pointcuts => :pointcuts,
|
66
|
+
:exclude_type => :exclude_types,
|
67
|
+
:exclude_type_and_descendents => :exclude_types_and_descendents,
|
68
|
+
:exclude_type_and_ancestors => :exclude_types_and_ancestors,
|
69
|
+
:exclude_object => :exclude_objects,
|
70
|
+
:exclude_pointcut => :exclude_pointcuts,
|
71
|
+
:exclude_join_point => :exclude_join_points,
|
72
|
+
:exclude_method => :exclude_methods,
|
73
|
+
:exclude_attribute => :exclude_attributes,
|
66
74
|
}
|
67
75
|
|
68
76
|
# Aspect.new (:around | :before | :after | :after_returning | :after_raising ) \
|
69
77
|
# (:pointcuts => [...]), | \
|
70
|
-
# ((:types => [...] | :
|
78
|
+
# ((:types => [...] | :types_and_ancestors => [...] | :types_and_descendents => [...] \
|
79
|
+
# :objects => [...]),
|
71
80
|
# :methods => [], :method_options => [...], \
|
72
81
|
# :attributes => [...], :attribute_options[...]), \
|
73
|
-
# (:advice = advice | do |join_point, *args| ...; end)
|
82
|
+
# (:advice = advice | do |join_point, obj, *args| ...; end)
|
74
83
|
#
|
75
84
|
# where the parameters often have many synonyms (mostly to support a "humane
|
76
85
|
# interface") and they are interpreted as followed:
|
@@ -109,6 +118,13 @@ module Aquarium
|
|
109
118
|
# All the :types, :objects, :methods, :attributes, :method_options, and :attribute_options
|
110
119
|
# are used to construct Pointcuts internally.
|
111
120
|
#
|
121
|
+
# <tt>:types_and_descendents => type || [type_list]</tt>::
|
122
|
+
# <tt>:type_and_descendents => type || [type_list]</tt>::
|
123
|
+
# <tt>:types_and_ancestors => type || [type_list]</tt>::
|
124
|
+
# <tt>:type_and_ancestors => type || [type_list]</tt>::
|
125
|
+
# One or an array of types and either their descendents or ancestors.
|
126
|
+
# If you want both the descendents _and_ ancestors, use both options.
|
127
|
+
#
|
112
128
|
# <tt>:objects => object || [object_list]</tt>::
|
113
129
|
# <tt>:object => object || [object_list]</tt>::
|
114
130
|
# <tt>:within_object => object || [object_list]</tt>::
|
@@ -164,6 +180,16 @@ module Aquarium
|
|
164
180
|
# <tt>:exclude_attribute => attribute || [attribute_list]</tt>::
|
165
181
|
# Exclude the specified "things" from the matched join points.
|
166
182
|
#
|
183
|
+
# <tt>:exclude_types_and_descendents => type || [type_list]</tt>::
|
184
|
+
# <tt>:exclude_type_and_descendents => type || [type_list]</tt>::
|
185
|
+
# <tt>:exclude_types_and_ancestors => type || [type_list]</tt>::
|
186
|
+
# <tt>:exclude_type_and_ancestors => type || [type_list]</tt>::
|
187
|
+
# Exclude the specified types and their descendents, ancestors.
|
188
|
+
# If you want to exclude both the descendents _and_ ancestors, use both options.
|
189
|
+
#
|
190
|
+
# The actual advice to execute is the block or you can pass a Proc using :advice => proc.
|
191
|
+
# Note that the advice takes a join_point argument, which will include a non-nil
|
192
|
+
# JoinPoint#Context object, the object being executed, and the argument list to the method.
|
167
193
|
def initialize *options, &block
|
168
194
|
process_input options, &block
|
169
195
|
init_pointcuts
|
@@ -218,15 +244,22 @@ module Aquarium
|
|
218
244
|
opts = rationalize_parameters options.flatten.dup
|
219
245
|
# For non-hash inputs, use an empty string for the value
|
220
246
|
@specification = Aquarium::Utils::MethodUtils.method_args_to_hash(*opts) {|option| ""}
|
221
|
-
use_default_object_if_defined unless
|
247
|
+
use_default_object_if_defined unless some_type_or_pc_option_given?
|
222
248
|
use_first_nonadvice_symbol_as_method(opts) unless methods_given?
|
249
|
+
calculate_excluded_types
|
223
250
|
@advice = determine_advice block
|
224
|
-
if @advice.nil? && @specification[:noop].nil?
|
225
|
-
bad_options "No advice block nor :advice argument was given."
|
226
|
-
end
|
227
251
|
validate_specification
|
228
252
|
end
|
229
253
|
|
254
|
+
def calculate_excluded_types
|
255
|
+
type_finder_options = {}
|
256
|
+
%w[types types_and_ancestors types_and_descendents].each do |opt|
|
257
|
+
type_finder_options[opt.intern] = @specification["exclude_#{opt}".intern] if @specification["exclude_#{opt}".intern]
|
258
|
+
end
|
259
|
+
excluded_types = Aquarium::Finders::TypeFinder.new.find type_finder_options
|
260
|
+
@specification[:exclude_types_calculated] = excluded_types.matched.keys
|
261
|
+
end
|
262
|
+
|
230
263
|
def determine_advice block
|
231
264
|
# There can be only one advice; take the last one...
|
232
265
|
block || (@specification[:advice].kind_of?(Array) ? @specification[:advice].last : @specification[:advice])
|
@@ -322,8 +355,8 @@ module Aquarium
|
|
322
355
|
# Useful for debugging...
|
323
356
|
def self.advice_chain_inspect advice_chain
|
324
357
|
return "[nil]" if advice_chain.nil?
|
325
|
-
"<br/>"+advice_chain.inspect do |
|
326
|
-
"#{
|
358
|
+
"<br/>"+advice_chain.inspect do |ac|
|
359
|
+
"#{ac.class.name}:#{ac.object_id}: join_point = #{ac.static_join_point}: aspect = #{ac.aspect.object_id}, next_node = #{advice_chain_inspect ac.next_node}"
|
327
360
|
end.gsub(/\</,"<").gsub(/\>/,">")+"<br/>"
|
328
361
|
end
|
329
362
|
|
@@ -402,7 +435,7 @@ module Aquarium
|
|
402
435
|
:advised_object => #{target_self},
|
403
436
|
:parameters => args,
|
404
437
|
:block_for_method => block_for_method)
|
405
|
-
advice_chain.call advice_join_point, *args
|
438
|
+
advice_chain.call advice_join_point, #{target_self}, *args
|
406
439
|
end
|
407
440
|
#{join_point.visibility.to_s} :#{join_point.method_name}
|
408
441
|
private :#{alias_method_name}
|
@@ -495,14 +528,16 @@ module Aquarium
|
|
495
528
|
"_aspect_"
|
496
529
|
end
|
497
530
|
|
531
|
+
def some_type_or_pc_option_given?
|
532
|
+
pointcuts_given? or some_type_option_given? or objects_given? #or default_objects_given?
|
533
|
+
end
|
534
|
+
|
535
|
+
def some_type_option_given?
|
536
|
+
types_given? or types_and_ancestors_given? or types_and_descendents_given?
|
537
|
+
end
|
538
|
+
|
498
539
|
def self.determine_type_or_object join_point
|
499
540
|
join_point.type_or_object
|
500
|
-
# type_or_object = join_point.type_or_object
|
501
|
-
# method_type = join_point.instance_or_class_method
|
502
|
-
# if is_type_join_point? join_point
|
503
|
-
# type_or_object = Aquarium::Utils::MethodUtils.definer type_or_object, join_point.method_name, "#{method_type}_method_only".intern
|
504
|
-
# end
|
505
|
-
# type_or_object
|
506
541
|
end
|
507
542
|
|
508
543
|
def self.make_type_or_object_key join_point
|
@@ -553,15 +588,21 @@ module Aquarium
|
|
553
588
|
bad_options(":after can't be used with :after_returning.") if after_given_with? :after_returning
|
554
589
|
bad_options(":after can't be used with :after_raising.") if after_given_with? :after_raising
|
555
590
|
bad_options(":after_returning can't be used with :after_raising.") if after_returning_given_with? :after_raising
|
556
|
-
unless
|
557
|
-
bad_options("At least one of :pointcut(s), :type(s), :object(s) is required.")
|
591
|
+
unless some_type_or_pc_option_given?
|
592
|
+
bad_options("At least one of :pointcut(s), :type(s), :type(s)_with_ancestors, :type(s)_with_descendents, :object(s) is required.")
|
558
593
|
end
|
559
|
-
if pointcuts_given? and (
|
594
|
+
if pointcuts_given? and (some_type_option_given? or objects_given?)
|
560
595
|
bad_options("Can't specify both :pointcut(s) and one or more of :type(s), and/or :object(s).")
|
561
596
|
end
|
562
597
|
@specification.each_key do |parameter|
|
563
598
|
check_parameter parameter
|
564
599
|
end
|
600
|
+
if @advice.nil? && @specification[:noop].nil?
|
601
|
+
bad_options "No advice block nor :advice argument was given."
|
602
|
+
end
|
603
|
+
if @advice.arity == -2
|
604
|
+
bad_options "It appears that your advice parameter list is the obsolete format |jp, *args|. The correct format is |jp, object, *args|"
|
605
|
+
end
|
565
606
|
end
|
566
607
|
|
567
608
|
def check_parameter parameter
|
@@ -571,6 +612,7 @@ module Aquarium
|
|
571
612
|
def is_valid_parameter key
|
572
613
|
ALLOWED_OPTIONS.include?(key) || ALLOWED_OPTIONS_SYNONYMS_MAP.keys.include?(key) ||
|
573
614
|
ADVICE_OPTIONS_SYNONYMS_MAP.keys.include?(key) || KINDS_IN_PRIORITY_ORDER.include?(key) ||
|
615
|
+
key == :exclude_types_calculated ||
|
574
616
|
parameter_is_a_method_name?(key) # i.e., use_first_nonadvice_symbol_as_method
|
575
617
|
end
|
576
618
|
|
@@ -25,7 +25,7 @@ module Aquarium
|
|
25
25
|
%w[after after_returning after_raising].each do |after_kind|
|
26
26
|
module_eval(<<-AFTER, __FILE__, __LINE__)
|
27
27
|
def before_and_#{after_kind} *options, &block
|
28
|
-
advise
|
28
|
+
advise :before, :#{after_kind}, *options, &block
|
29
29
|
end
|
30
30
|
AFTER
|
31
31
|
end
|
@@ -28,8 +28,8 @@ module Aquarium
|
|
28
28
|
unless @specification[:exclude_objects].nil?
|
29
29
|
return true if @specification[:exclude_objects].include?(type_or_object)
|
30
30
|
end
|
31
|
-
unless @specification[:
|
32
|
-
return true if @specification[:
|
31
|
+
unless @specification[:exclude_types_calculated].nil?
|
32
|
+
return true if @specification[:exclude_types_calculated].find do |t|
|
33
33
|
case t
|
34
34
|
when String: type_or_object.name.eql?(t)
|
35
35
|
when Symbol: type_or_object.name.eql?(t.to_s)
|
@@ -8,6 +8,9 @@ module Aquarium
|
|
8
8
|
module Aspects
|
9
9
|
class JoinPoint
|
10
10
|
|
11
|
+
class ProceedMethodNotAvailable < Exception; end
|
12
|
+
class ContextNotDefined < Exception; end
|
13
|
+
|
11
14
|
class Context
|
12
15
|
attr_accessor :advice_kind, :advised_object, :parameters, :block_for_method, :returned_value, :raised_exception, :proceed_proc
|
13
16
|
|
@@ -26,12 +29,20 @@ module Aquarium
|
|
26
29
|
end
|
27
30
|
|
28
31
|
def proceed enclosing_join_point, *args, &block
|
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.)"
|
32
|
+
raise ProceedMethodNotAvailable.new("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.)") if @proceed_proc.nil?
|
33
|
+
do_invoke :call, enclosing_join_point, *args, &block
|
34
|
+
end
|
35
|
+
|
36
|
+
def invoke_original_join_point enclosing_join_point, *args, &block
|
37
|
+
do_invoke :invoke_original_join_point, enclosing_join_point, *args, &block
|
38
|
+
end
|
39
|
+
|
40
|
+
def do_invoke method, enclosing_join_point, *args, &block
|
30
41
|
args = parameters if (args.nil? or args.size == 0)
|
31
42
|
enclosing_join_point.context.block_for_method = block if block
|
32
|
-
proceed_proc.
|
43
|
+
proceed_proc.send method, enclosing_join_point, advised_object, *args
|
33
44
|
end
|
34
|
-
protected :
|
45
|
+
protected :do_invoke
|
35
46
|
|
36
47
|
alias :to_s :inspect
|
37
48
|
|
@@ -68,32 +79,9 @@ module Aquarium
|
|
68
79
|
end
|
69
80
|
end
|
70
81
|
|
71
|
-
%w[type object].each do |attr|
|
72
|
-
class_eval(<<-EOF, __FILE__, __LINE__)
|
73
|
-
# Deprecated, as JoinPoint#type overrides Module#type in a non-substitutable way!
|
74
|
-
# JoinPoint#target_#{attr} will be removed in the next release.
|
75
|
-
# Use JoinPoint#target_#{attr} instead
|
76
|
-
def #{attr}
|
77
|
-
p "WARNING: JoinPoint##{attr} is deprecated. It will be removed in the next release."
|
78
|
-
target_#{attr}
|
79
|
-
end
|
80
|
-
# Deprecated
|
81
|
-
def #{attr}= new_#{attr}
|
82
|
-
p "WARNING: JoinPoint##{attr}= is deprecated. It will be removed in the next release."
|
83
|
-
target_#{attr}= new_#{attr}
|
84
|
-
end
|
85
|
-
EOF
|
86
|
-
end
|
87
|
-
|
88
82
|
attr_accessor :target_type, :target_object, :method_name, :visibility, :context
|
89
83
|
attr_reader :instance_or_class_method
|
90
84
|
|
91
|
-
# For "symmetry" with JoinPoint#target_type
|
92
|
-
alias_method :object, :target_object
|
93
|
-
|
94
|
-
# For "symmetry" with JoinPoint#target_type=
|
95
|
-
alias_method :object=, :target_object=
|
96
|
-
|
97
85
|
def instance_method?
|
98
86
|
@instance_method
|
99
87
|
end
|
@@ -124,14 +112,26 @@ module Aquarium
|
|
124
112
|
results = Aquarium::Finders::MethodFinder.new.find type_or_object_sym => type_or_object,
|
125
113
|
:method => method_name,
|
126
114
|
:options => [visibility, instance_or_class_method]
|
127
|
-
raise
|
115
|
+
raise Aquarium::Utils::LogicError("MethodFinder returned more than one item! #{results.inspect}") if (results.matched.size + results.not_matched.size) != 1
|
128
116
|
return results.matched.size == 1 ? true : false
|
129
117
|
end
|
130
118
|
|
119
|
+
# Invoke the "enclosed" join point, which could be aspect advice wrapping the original runtime join point.
|
120
|
+
# This method can only be called if the join point has a context object defined that represents an actual
|
121
|
+
# runtime "state".
|
131
122
|
def proceed *args, &block
|
132
|
-
|
123
|
+
raise ContextNotDefined.new(":proceed can't be called unless the join point has a context object.") if context.nil?
|
124
|
+
context.proceed self, *args, &block
|
133
125
|
end
|
134
126
|
|
127
|
+
# Invoke the actual runtime join point, skipping any intermediate advice.
|
128
|
+
# This method can only be called if the join point has a context object defined that represents an actual
|
129
|
+
# runtime "state".
|
130
|
+
def invoke_original_join_point *args, &block
|
131
|
+
raise ContextNotDefined.new(":invoke_original_join_point can't be called unless the join point has a context object.") if context.nil?
|
132
|
+
context.invoke_original_join_point self, *args, &block
|
133
|
+
end
|
134
|
+
|
135
135
|
def make_current_context_join_point context_options
|
136
136
|
new_jp = dup
|
137
137
|
if new_jp.context.nil?
|