aquarium 0.1.8 → 0.2.0
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 +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?
|