aquarium 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGES +59 -2
  2. data/README +33 -16
  3. data/RELEASE-PLAN +28 -5
  4. data/UPGRADE +11 -0
  5. data/examples/aspect_design_example.rb +2 -2
  6. data/examples/aspect_design_example_spec.rb +2 -2
  7. data/examples/design_by_contract_example.rb +4 -4
  8. data/examples/design_by_contract_example_spec.rb +4 -4
  9. data/examples/method_missing_example.rb +4 -1
  10. data/examples/method_missing_example_spec.rb +4 -1
  11. data/examples/method_tracing_example.rb +2 -2
  12. data/examples/method_tracing_example_spec.rb +16 -16
  13. data/lib/aquarium/aspects/advice.rb +47 -25
  14. data/lib/aquarium/aspects/aspect.rb +81 -39
  15. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +1 -1
  16. data/lib/aquarium/aspects/exclusion_handler.rb +2 -2
  17. data/lib/aquarium/aspects/join_point.rb +28 -28
  18. data/lib/aquarium/aspects/pointcut.rb +61 -15
  19. data/lib/aquarium/extras/design_by_contract.rb +7 -7
  20. data/lib/aquarium/finders.rb +0 -1
  21. data/lib/aquarium/finders/method_finder.rb +10 -20
  22. data/lib/aquarium/finders/type_finder.rb +141 -75
  23. data/lib/aquarium/utils.rb +1 -0
  24. data/lib/aquarium/utils/logic_error.rb +9 -0
  25. data/lib/aquarium/utils/method_utils.rb +4 -3
  26. data/lib/aquarium/utils/nil_object.rb +1 -0
  27. data/lib/aquarium/utils/type_utils.rb +19 -0
  28. data/lib/aquarium/version.rb +2 -2
  29. data/spec/aquarium/aspects/advice_chain_node_spec.rb +2 -2
  30. data/spec/aquarium/aspects/advice_spec.rb +28 -5
  31. data/spec/aquarium/aspects/aspect_invocation_spec.rb +522 -289
  32. data/spec/aquarium/aspects/aspect_spec.rb +59 -41
  33. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +7 -7
  34. data/spec/aquarium/aspects/aspect_with_subtypes_spec.rb +2 -2
  35. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -2
  36. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +1 -1
  37. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +34 -34
  38. data/spec/aquarium/aspects/join_point_spec.rb +79 -0
  39. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +13 -3
  40. data/spec/aquarium/aspects/pointcut_spec.rb +310 -63
  41. data/spec/aquarium/extras/design_by_contract_spec.rb +4 -4
  42. data/spec/aquarium/finders/method_finder_spec.rb +208 -54
  43. data/spec/aquarium/finders/type_finder_spec.rb +24 -88
  44. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +206 -0
  45. data/spec/aquarium/spec_example_classes.rb +75 -12
  46. data/spec/aquarium/utils/logic_error_spec.rb +10 -0
  47. data/spec/aquarium/utils/type_utils_sample_classes.rb +203 -0
  48. data/spec/aquarium/utils/type_utils_spec.rb +47 -1
  49. metadata +48 -39
  50. data/lib/aquarium/finders/object_finder.rb +0 -75
  51. data/spec/aquarium/finders/object_finder_spec.rb +0 -231
data/CHANGES CHANGED
@@ -1,7 +1,64 @@
1
+ == Version 0.2.0
2
+
3
+ V0.2.0 changes the parameter list used for advice blocks and adds numerous enhancements, robustness
4
+ improvements and more complete "spec'ing".
5
+
6
+ Bug fixes:
7
+ none
8
+
9
+ Enhancements:
10
+ 13402 Support a subclass syntax like AspectJ's "Type+"
11
+ 13984 More flexible argument list to the advise block
12
+ 14053 Remove JoinPoint#type, JoinPoint#type=, JoinPoint#object, and JoinPoint#object=
13
+ 14061 Add a control flow mechanism to skipping (sic) intermediate advice
14
+ 15164 Deprecate ObjectFinder
15
+ 15413 Remove ObjectFinder
16
+ 15710 Eliminate redundant public methods in various "finders"
17
+
18
+ #13402 adds new invocation options to specify types and their descendents (subclasses or modules
19
+ that include the specified module(s)) and ancestors. The latter should be used cautiously as it
20
+ will include things like Kernel, Object, and Class! I used new command options rather than the
21
+ AspectJ "+" suffix (and the proposed, but never implemented "-" suffix for ancestors), because
22
+ the "+" would be confusing with regular expressions and not in the spirit of trying to make
23
+ the pointcut language "easy to read". So, the following are now available:
24
+ :type_and_ancestors
25
+ :types_and_ancestors
26
+ :type_and_descendents
27
+ :types_and_descendents
28
+ And the corresponding:
29
+ :exclude_type_and_ancestors
30
+ :exclude_types_and_ancestors
31
+ :exclude_type_and_descendents
32
+ :exclude_types_and_descendents
33
+ If you want both the ancestors and descendents, just use both options with the same value.
34
+
35
+ #13984 adds the object as the second argument to the advice block parameter list. This change
36
+ reflects the fact that the object is often needed, but calling jp.context.advised_object is a
37
+ bit tedious. THIS CHANGE BREAKS BACKWARDS COMPATIBILITY!! An exception is raised if advice has
38
+ the signature |jp, *args|.
39
+
40
+ #14061 adds a new method, JoinPoint#invoke_original_join_point, which will invoke the original
41
+ method without intermediate advice. If called within around advice, you can write advice that
42
+ vetoes all subsequent advice, yet invokes the original method. Use this technique cautiously,
43
+ however, since you may not always know what other advices are involved and what side effects
44
+ this control-flow change might cause.
45
+
46
+ #15164 and 15413 remove ObjectFinder because it is not used and it requires ObjectSpace, which
47
+ has high overhead and won't be enabled, by default, in JRuby (it will be optional).
48
+
49
+ #15710 removes redundant methods that were becoming a maintenance issue, in particular,
50
+ MethodFinder#find_all_by and TypeFinder#find_by_name. This is a non-backwards-compatible API
51
+ change.
52
+
53
+ Finally, note that I have not yet been able to resolve bug #15202, "Intermittent confusion
54
+ between classes and objects when invoking advice." I believe this is a very rare occurrence
55
+ and only likely to ever happen during the "torture-test" of running the RSpec suite. Please post
56
+ a comment to Tracker if you encounter it!
57
+
1
58
  == Version 0.1.8
2
59
 
3
- V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also adds
4
- several feature enhancements and refactorings. There are no known upgrade issues.
60
+ V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also
61
+ adds several feature enhancements and refactorings. There are no known upgrade issues.
5
62
 
6
63
  Bug fixes:
7
64
  none
data/README CHANGED
@@ -50,7 +50,7 @@ Many of AspectJ's behaviors that aren't currently supported are planned for futu
50
50
  * At this time, the pointcut language supported by Aquarium is not nearly as feature-rich as AspectJ's language. For example, there are no runtime pointcut designators supported, such as "if" conditions and "cflow" (context flow). Most of AspectJ pointcut language features are planned, however.
51
51
  * While AspectJ provides "intertype declaration" capabilities (i.e., adding state and behavior to existing classes), Ruby's native metaprogramming support satisfies this need. There may be convenience hooks added in a future release, however.
52
52
  * User defined advice precedence is not supported. However, advice precedence is unambiguous; the last aspects created while modules are loaded at runtime have higher precedence than earlier aspects. Ensuring a particular order is not always easy, of course.
53
- * Unlike AspectJ, Aquarium can advise individual objects, can remove advice, and it supports named advice that can be defined separately from the aspects that use the advice.
53
+ * Unlike AspectJ, Aquarium can advise individual objects, can remove advice, and it supports named advice that can be defined separately from the aspects that use the advice. Aquarium can also advise ancestor (parent) types, not just derived (descendent) types of specified types.
54
54
 
55
55
  === Examples
56
56
 
@@ -61,20 +61,20 @@ In most cases, you can either declare the appropriate classes or use the optiona
61
61
  Here is an example that traces invocations of all public instance methods (included inherited ones) of the classes or modules Foo and Bar.
62
62
 
63
63
  require 'aquarium'
64
- Aspect.new :around, :types => [Foo, Bar], :methods => :all do |join_point, *args|
65
- p "Entering: #{join_point.target_type.name}##{join_point.method_name}"
64
+ Aspect.new :around, :types => [Foo, Bar], :methods => :all do |join_point, object, *args|
65
+ p "Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
66
66
  result = join_point.proceed
67
- p "Leaving: #{join_point.target_type.name}##{join_point.method_name}"
67
+ p "Leaving: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
68
68
  result # block needs to return the result of the "proceed"!
69
69
  end
70
70
 
71
71
  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, excluding inherited (ancestor) 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!).
72
72
 
73
73
  require 'aquarium/aspects/dsl/object_dsl'
74
- around :types => [Foo, Bar], :methods => :all do |join_point, *args|
75
- p "Entering: #{join_point.target_type.name}##{join_point.method_name}"
74
+ around :types => [Foo, Bar], :methods => :all do |join_point, object, *args|
75
+ p "Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
76
76
  result = join_point.proceed
77
- p "Leaving: #{join_point.target_type.name}##{join_point.method_name}"
77
+ p "Leaving: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
78
78
  result # block needs to return the result of the "proceed"!
79
79
  end
80
80
 
@@ -106,7 +106,7 @@ If you use the DSL inside a class and omit the :type(s) and :object(s) options,
106
106
  end
107
107
  ...
108
108
  class Foo
109
- around :critical_operation do |join_point, *args|
109
+ around :critical_operation do |join_point, object, *args|
110
110
  p "Entering: Foo#critical_operation"
111
111
  result = join_point.proceed
112
112
  p "Leaving: Foo#critical_operation"
@@ -116,22 +116,22 @@ If you use the DSL inside a class and omit the :type(s) and :object(s) options,
116
116
 
117
117
  It is important to note that aspect "instances" usually behave like class (static) variables, in terms of the lifetime of their effects. In the example shown, class Foo is permanently modified to do the print statements shown for all "critical methods", unless you save the result of calling "around" to a variable, e.g., critical_operation_logging, and you explicitly call "critical_operation_logging.unadvise" at some future time. Put another way, the effects scope just like changes made when you reopen a class or module.
118
118
 
119
- A common mistake is to create an aspect in an initialize method and assign it to an attribute. This usually means that you are creating long-lived, redundant aspects every time an instance is created. The aspect modifications remain in effect even when the instances themselves are garbage collected!
119
+ A common mistake is to create an aspect in an initialize method and assign it to an attribute. This usually means that you are creating long-lived, redundant aspects every time an instance of your class is created. The aspect modifications remain in effect even when the instances themselves are garbage collected!
120
120
 
121
121
  Here are some more succinct examples, illustrating the API (using the DSL methods).
122
122
 
123
123
  You can pass in pointcuts defined elsewhere:
124
124
 
125
125
  my_pointcut = Pointcut.new :types => /Foo::Bar::/, :methods => /^do_/
126
- around :pointcuts => my_pointcut do |jp, *args| ... # Pass in a pointcut
127
- around :pointcuts => [my_pointcut, ...] do |jp, *args| ... # Pass in a pointcut array
126
+ around :pointcuts => my_pointcut do |jp, obj, *args| ... # Pass in a pointcut
127
+ around :pointcuts => [my_pointcut, ...] do |jp, obj, *args| ... # Pass in a pointcut array
128
128
 
129
129
  As a convenience, since a JoinPoint is like a Pointcut with one element, you can pass a JoinPoint object where Pointcut objects are expected:
130
130
 
131
131
  my_join_point1 = JoinPoint.new :type => Foo::Bar, :method => do_this
132
132
  my_join_point2 = JoinPoint.new :type => Foo::Bar, :method => do_that
133
- around :pointcuts => my_join_point1 do |jp, *args| ...
134
- around :pointcuts => [my_join_point1, my_join_point2, ...] do |jp, *args| ...
133
+ around :pointcuts => my_join_point1 do |jp, obj, *args| ...
134
+ around :pointcuts => [my_join_point1, my_join_point2, ...] do |jp, obj, *args| ...
135
135
 
136
136
  You can specify a single type, a type name, a type regular expression, or an array of the same. Note that :type and :types are synonymous "sugar".
137
137
 
@@ -142,6 +142,13 @@ You can specify a single type, a type name, a type regular expression, or an arr
142
142
  around :types => /A::.*Helper$/, ...
143
143
  around :types => [/A::.*Helper$/, /B::Foo.*/], ...
144
144
 
145
+ You can specify types and their descendents (subclasses or included modules) or ancestors.
146
+
147
+ around :type_and_ancestors = A, ...
148
+ around :types_and_ancestors = A, ...
149
+ around :type_and_descendents = A, ...
150
+ around :types_and_descendents = A, ...
151
+
145
152
  You can specify a single object or an array of objects. Note that :object and :objects are synonymous.
146
153
 
147
154
  a1 = A.new
@@ -204,6 +211,10 @@ of the same types. (This is not likely to happen a lot in real applications, but
204
211
  around ..., :exclude_join_points = [jp, ...]
205
212
  around ..., :exclude_type = t, ...
206
213
  around ..., :exclude_types = [t, ...]
214
+ around ..., :exclude_type_and_ancestors = t, ...
215
+ around ..., :exclude_types_and_ancestors = [t, ...]
216
+ around ..., :exclude_type_and_descendents = t, ...
217
+ around ..., :exclude_types_and_descendents = [t, ...]
207
218
  around ..., :exclude_object = o, ...
208
219
  around ..., :exclude_objects = [o, ...]
209
220
  around ..., :exclude_method = m, ...
@@ -243,18 +254,24 @@ You can advice methods both before after. This is different from around advice,
243
254
  before_and_after_raising_within :types =>, ... # synonym
244
255
  before_and_after_raising_within_or_returning_from :types =>, ... # synonym
245
256
 
246
- You can pass a block as the advice. Notice that all advice blocks and Procs (see below) are required to accept two arguments, the JoinPoint, which will contain useful context information, and "*args", which will contain the parameters used when invoking the join point (method). A block or advice argument is required, but it can be empty.
257
+ If you pass a block to Aspect.new, it will be the advice. When invoked, the advice will be passed the following three arguments,
258
+ 1) the JoinPoint, which will contain a JoinPoint::Context object with useful context information,
259
+ 2) the object being sent the current message, and
260
+ 3) the parameters passed with the original message.
261
+ Recall that Proc don't check the number of arguments (while lambdas do), so if you don't care about any trailing parameters, you can leave them out of the parameter list. Recall that the other difference between the two is that a return statement in a Proc returns from the method that contains it. As rule, do NOT use return statements in advices!
247
262
 
248
- around :type => [...], :methods => :all do |join_point, *args|
263
+ around :type => [...], :methods => :all do |join_point, object, *args|
249
264
  advice_to_execute_before_the_jp
250
265
  result = join_point.proceed # Invoke the join point, passing *args implicitly (you can override...)
251
266
  advice_to_execute_after_the_jp
252
267
  result # return the result of the "proceed", unless you override the value.
253
268
  end
254
- around(:type => [...], :methods => :all) {|join_point, *args| ...} # (...) necessary for precedence...
269
+ around(:type => [...], :methods => :all) {|join_point, object, *args| ...} # (...) necessary for precedence...
255
270
 
256
271
  In the example, we show that you must be careful to return the correct value, usually the value returned by "proceed" or a value created by the block itself.
257
272
 
273
+ Note, prior to V0.2.0, the advice argument list was |join_point, *args|. Aquarium will look for such obsolete signatures (by looking at the arity of the proc) and raise an exception, if found. This check will be removed in a future release.
274
+
258
275
  Rather than passing a block as the advice, you can pass a previously-created Proc:
259
276
 
260
277
  around :type => [...], :methods => :all, :advice => advice
data/RELEASE-PLAN CHANGED
@@ -15,11 +15,34 @@ This change will obvious break existing aspects.
15
15
 
16
16
  === Versions 0.3+.0
17
17
 
18
- More refinements to the API and functionality. The main thrust will be expanding the pointcut
19
- language to include conditionals and stack context constructs, as well as more intuitive ways
20
- of expressing sets of types, such as types nested arbitrarily deep in module "namespaces",
21
- etc.
18
+ More refinements and simpliciations to the API and functionality, including much-needed redundancy
19
+ reduction. I haven't used mocks much in the specs, but they are clearly needed now to improve
20
+ performance of RSpec runs.
22
21
 
22
+ The main thrust of new feature work will be expanding the pointcut language to include
23
+ conditionals and stack context constructs, as well as more intuitive ways of expressing sets of
24
+ types, such as types nested arbitrarily deep in module "namespaces" (e.g., #13403), etc.
25
+
26
+ I'm also thinking about an alternative syntax for the DSL. Instead of just this:
27
+
28
+ Aspect.new :around :pointcuts => [pc1, pc2, ...] do |jp, object, *args|
29
+ # advise
30
+ end
31
+
32
+ How about something like the following?
33
+
34
+ around do
35
+ pointcuts pc1 or pc2
36
+ advise_with do |jp, object, *args|
37
+ # advise
38
+ end
39
+ end
40
+
41
+ I'm not sure it adds much (at this stage of thinking about it...) except that it could make
42
+ composition of complete pointcuts easier.
43
+
23
44
  === Version 1.0.0
24
45
 
25
- Reasonable stable and full-featured API and DSL.
46
+ Reasonable stable and full-featured API and DSL. Also, to justify Aquarium's existence ;), I want
47
+ to produce some non-trivial examples of refactoring known APIs and demonstrating improved clarity,
48
+ productivity, modularity, etc., etc.
data/UPGRADE CHANGED
@@ -1,3 +1,14 @@
1
+ == Upgrading to Aquarium-0.2.0
2
+
3
+ This release changes the expected advice parameter list from |join_point, *method_args| to
4
+ |join_point, object, *method_args|, where "object" is the current object receiving the message
5
+ corresponding to the advised join point. Therefore, when you upgrade, you will need to modify your
6
+ advices to take this extra argument, even if you don't use it. Since an advice of |jp, *args| would
7
+ silently "absorb" the object into the beginning of "*args", thereby potentially causing confusion,
8
+ Aquarium will raise an exception if the advice block or proc has this obsolete signature.
9
+
10
+ Note that "JoinPoint#Context.advised_object" is still supported, even if it is now less useful.
11
+
1
12
  == Upgrading to Aquarium-0.1.8
2
13
 
3
14
  V0.1.7 did not successfully "register" at rubyforge. This releases fixes that problem and also adds
@@ -34,9 +34,9 @@ include Aquarium::Aspects
34
34
 
35
35
  # Observe state changes in the class, using the class-defined pointcut.
36
36
 
37
- observer = Aspect.new :after, :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, *args|
37
+ observer = Aspect.new :after, :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, obj, *args|
38
38
  p "State has changed. "
39
- state = jp.context.advised_object.state
39
+ state = obj.state
40
40
  p " New state is #{state.nil? ? 'nil' : state.inspect}"
41
41
  p " Equivalent to *args: #{args.inspect}"
42
42
  end
@@ -33,8 +33,8 @@ include Aquarium::Aspects
33
33
  describe "An example of an aspect using a class-defined pointcut." do
34
34
  it "should observe state changes in the class." do
35
35
  @new_state = nil
36
- observer = Aspect.new :after, :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, *args|
37
- @new_state = jp.context.advised_object.state
36
+ observer = Aspect.new :after, :pointcut => Aquarium::ClassWithStateAndBehavior::STATE_CHANGE do |jp, obj, *args|
37
+ @new_state = obj.state
38
38
  @new_state.should be_eql(*args)
39
39
  end
40
40
  object = Aquarium::ClassWithStateAndBehavior.new(:a1, :a2, :a3)
@@ -15,7 +15,7 @@ module Aquarium
15
15
  p "inside :action"
16
16
  end
17
17
 
18
- precondition :method => :action, :message => "Must pass more than one argument." do |jp, *args|
18
+ precondition :method => :action, :message => "Must pass more than one argument." do |jp, obj, *args|
19
19
  args.size > 0
20
20
  end
21
21
  end
@@ -37,7 +37,7 @@ module Aquarium
37
37
  end
38
38
 
39
39
  postcondition :method => :action,
40
- :message => "Must pass more than one argument and first argument must be non-empty." do |jp, *args|
40
+ :message => "Must pass more than one argument and first argument must be non-empty." do |jp, obj, *args|
41
41
  args.size > 0 && ! args[0].empty?
42
42
  end
43
43
  end
@@ -71,8 +71,8 @@ module Aquarium
71
71
  @invar = 1
72
72
  end
73
73
 
74
- invariant :methods => /action$/, :message => "Must not change the @invar value." do |jp, *args|
75
- jp.context.advised_object.invar == 0
74
+ invariant :methods => /action$/, :message => "Must not change the @invar value." do |jp, obj, *args|
75
+ obj.invar == 0
76
76
  end
77
77
  end
78
78
  end
@@ -16,7 +16,7 @@ module Aquarium
16
16
  end
17
17
  attr_reader :state
18
18
 
19
- precondition :method => :action, :message => "Must pass more than one argument." do |jp, *args|
19
+ precondition :method => :action, :message => "Must pass more than one argument." do |jp, obj, *args|
20
20
  args.size > 0
21
21
  end
22
22
  end
@@ -42,7 +42,7 @@ module Aquarium
42
42
  attr_reader :state
43
43
 
44
44
  postcondition :method => :action,
45
- :message => "Must pass more than one argument and first argument must be non-empty." do |jp, *args|
45
+ :message => "Must pass more than one argument and first argument must be non-empty." do |jp, obj, *args|
46
46
  args.size > 0 && ! args[0].empty?
47
47
  end
48
48
  end
@@ -73,8 +73,8 @@ module Aquarium
73
73
  @invar = 1
74
74
  end
75
75
 
76
- invariant :methods => /action$/, :message => "Must not change the @invar value." do |jp, *args|
77
- jp.context.advised_object.invar == 0
76
+ invariant :methods => /action$/, :message => "Must not change the @invar value." do |jp, obj, *args|
77
+ obj.invar == 0
78
78
  end
79
79
  end
80
80
  end
@@ -19,6 +19,9 @@ module Aquarium
19
19
  def method_missing sym, *args
20
20
  p "Echoing: #{sym.to_s}: #{args.join(" ")}"
21
21
  end
22
+ def respond_to? sym, include_private = false
23
+ true
24
+ end
22
25
  end
23
26
  end
24
27
 
@@ -28,7 +31,7 @@ echo1.say "hello", "world!"
28
31
  echo1.log "something", "interesting..."
29
32
  echo1.shout "theater", "in", "a", "crowded", "firehouse!"
30
33
 
31
- Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Echo, :method => :method_missing do |join_point, sym, *args|
34
+ Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Echo, :method => :method_missing do |join_point, obj, sym, *args|
32
35
  if sym == :log
33
36
  p "--- Sending to log: #{args.join(" ")}"
34
37
  else
@@ -20,6 +20,9 @@ module Aquarium
20
20
  @log << "Echoing: #{sym.to_s}: #{args.join(" ")}"
21
21
  end
22
22
  def logged_messages; @log; end
23
+ def respond_to? sym, include_private = false
24
+ true
25
+ end
23
26
  end
24
27
  end
25
28
 
@@ -39,7 +42,7 @@ end
39
42
  describe "An example of a class' method_missing with around advice" do
40
43
  it "should only handle invocations not processed by the around advice." do
41
44
  @intercepted_message = nil
42
- aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Echo, :method => :method_missing do |join_point, sym, *args|
45
+ aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Echo, :method => :method_missing do |join_point, obj, sym, *args|
43
46
  if sym == :log
44
47
  @intercepted_message = "log: #{args.join(" ")}"
45
48
  else
@@ -39,7 +39,7 @@ bar1.do_something_else :b3, :b4
39
39
  include Aquarium::Aspects
40
40
 
41
41
  Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :all,
42
- :method_options => :exclude_ancestor_methods do |execution_point, *args|
42
+ :method_options => :exclude_ancestor_methods do |execution_point, obj, *args|
43
43
  begin
44
44
  p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
45
45
  execution_point.proceed
@@ -58,7 +58,7 @@ bar1.do_something_else :b7, :b8
58
58
  # The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
59
59
  # of the "proceed", rather than the value returned by "p"!
60
60
  Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize,
61
- :method_options => :private do |execution_point, *args|
61
+ :method_options => :private do |execution_point, obj, *args|
62
62
  begin
63
63
  p "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
64
64
  execution_point.proceed
@@ -56,13 +56,13 @@ describe "An example with advice on the public instance methods (excluding ances
56
56
  it "should trace all calls to the public methods defined by Foo" do
57
57
  # The "begin/ensure/end" idiom shown causes the advice to return the correct value; the result
58
58
  # of the "proceed", rather than the value returned by "p"!
59
- aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Foo, :methods => :all, :method_options => :exclude_ancestor_methods do |execution_point, *args|
59
+ aspect = Aquarium::Aspects::Aspect.new :around,
60
+ :type => Aquarium::Foo, :methods => :all, :method_options => :exclude_ancestor_methods do |execution_point, obj, *args|
60
61
  begin
61
- o = execution_point.context.advised_object
62
- o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
62
+ obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
63
63
  execution_point.proceed
64
64
  ensure
65
- o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
65
+ obj.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
66
66
  end
67
67
  end
68
68
 
@@ -79,13 +79,13 @@ end
79
79
 
80
80
  describe "An example with advice on the public instance methods (excluding ancestor methods) of Bar" do
81
81
  it "should not trace any calls to the public methods defined by the included BarModule" do
82
- aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => :all, :method_options => :exclude_ancestor_methods do |execution_point, *args|
82
+ aspect = Aquarium::Aspects::Aspect.new :around,
83
+ :type => Aquarium::Bar, :methods => :all, :method_options => :exclude_ancestor_methods do |execution_point, obj, *args|
83
84
  begin
84
- o = execution_point.context.advised_object
85
- o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
85
+ obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
86
86
  execution_point.proceed
87
87
  ensure
88
- o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
88
+ obj.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
89
89
  end
90
90
  end
91
91
 
@@ -98,13 +98,13 @@ end
98
98
 
99
99
  describe "An example with advice on the public instance methods (including ancestor methods) of Bar" do
100
100
  it "should trace all calls to the public methods defined by the included BarModule" do
101
- aspect = Aquarium::Aspects::Aspect.new :around, :type => Aquarium::Bar, :methods => /^do_/ do |execution_point, *args|
101
+ aspect = Aquarium::Aspects::Aspect.new :around,
102
+ :type => Aquarium::Bar, :methods => /^do_/ do |execution_point, obj, *args|
102
103
  begin
103
- o = execution_point.context.advised_object
104
- o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
104
+ obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
105
105
  execution_point.proceed
106
106
  ensure
107
- o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
107
+ obj.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
108
108
  end
109
109
  end
110
110
 
@@ -123,13 +123,13 @@ end
123
123
  describe "An example with advice on the private initialize method of Foo and Bar" do
124
124
  it "should trace all calls to initialize" do
125
125
  before_methods = Aquarium::Foo.private_instance_methods.sort #- Object.private_methods.sort
126
- aspect = Aquarium::Aspects::Aspect.new :around, :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize, :method_options => :private do |execution_point, *args|
126
+ aspect = Aquarium::Aspects::Aspect.new :around,
127
+ :types => [Aquarium::Foo, Aquarium::Bar], :methods => :initialize, :method_options => :private do |execution_point, obj, *args|
127
128
  begin
128
- o = execution_point.context.advised_object
129
- o.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
129
+ obj.log "Entering: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
130
130
  execution_point.proceed
131
131
  ensure
132
- o.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
132
+ obj.log "Leaving: #{execution_point.target_type.name}##{execution_point.method_name}: args = #{args.inspect}"
133
133
  end
134
134
  end
135
135