aquarium 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +26 -5
- data/README +8 -8
- data/RELEASE-PLAN +20 -2
- data/TODO.rb +26 -0
- data/UPGRADE +5 -5
- data/examples/aspect_design_example.rb +1 -1
- data/examples/aspect_design_example_spec.rb +1 -1
- data/examples/design_by_contract_example.rb +4 -9
- data/examples/design_by_contract_example_spec.rb +7 -9
- data/examples/exception_wrapping_example.rb +48 -0
- data/examples/exception_wrapping_example_spec.rb +49 -0
- data/examples/reusable_aspect_hack_example.rb +56 -0
- data/examples/reusable_aspect_hack_example_spec.rb +80 -0
- data/lib/aquarium.rb +1 -0
- data/lib/aquarium/aspects.rb +1 -1
- data/lib/aquarium/aspects/advice.rb +16 -13
- data/lib/aquarium/aspects/aspect.rb +81 -56
- data/lib/aquarium/aspects/join_point.rb +4 -4
- data/lib/aquarium/aspects/pointcut.rb +49 -73
- data/lib/aquarium/dsl.rb +2 -0
- data/lib/aquarium/dsl/aspect_dsl.rb +77 -0
- data/lib/aquarium/{aspects/dsl → dsl}/object_dsl.rb +2 -2
- data/lib/aquarium/extras/design_by_contract.rb +1 -1
- data/lib/aquarium/finders.rb +1 -1
- data/lib/aquarium/finders/method_finder.rb +26 -26
- data/lib/aquarium/finders/type_finder.rb +45 -39
- data/lib/aquarium/utils/array_utils.rb +6 -5
- data/lib/aquarium/utils/default_logger.rb +2 -1
- data/lib/aquarium/utils/options_utils.rb +178 -67
- data/lib/aquarium/utils/set_utils.rb +8 -3
- data/lib/aquarium/version.rb +1 -1
- data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -14
- data/spec/aquarium/aspects/aspect_spec.rb +91 -7
- data/spec/aquarium/aspects/pointcut_spec.rb +61 -0
- data/spec/aquarium/{aspects/dsl → dsl}/aspect_dsl_spec.rb +76 -32
- data/spec/aquarium/finders/method_finder_spec.rb +80 -80
- data/spec/aquarium/finders/type_finder_spec.rb +57 -52
- data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -12
- data/spec/aquarium/spec_example_types.rb +4 -3
- data/spec/aquarium/utils/array_utils_spec.rb +9 -7
- data/spec/aquarium/utils/options_utils_spec.rb +106 -5
- data/spec/aquarium/utils/set_utils_spec.rb +14 -0
- metadata +12 -7
- data/lib/aquarium/aspects/dsl.rb +0 -2
- data/lib/aquarium/aspects/dsl/aspect_dsl.rb +0 -64
data/CHANGES
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
== Version 0.4.1
|
2
|
+
|
3
|
+
V0.4.1 adds a few bug fixes, a few more user examples, internal refactoring and some performance
|
4
|
+
improvements.
|
5
|
+
|
6
|
+
Bug fixes:
|
7
|
+
19116 When an exception is thrown during advice execution, the error message always reports the advice type is :before!
|
8
|
+
19261 after_raising DSL method provides no way to specify exceptions
|
9
|
+
|
10
|
+
Enhancements:
|
11
|
+
18705 Remove duplication and complexity in options-handling code
|
12
|
+
19320 Move the Aquarium::...::AspectDSL file to Aquarium::DSL for convenience
|
13
|
+
19399 Improve the Design by Contract example
|
14
|
+
|
15
|
+
I added a new :exceptions argument (synonym :exception) that takes a single exception or list thereof.
|
16
|
+
You can only use this argument with :after_raising. If you specify exceptions with the latter and use
|
17
|
+
the :exceptions argument, the values will be merged.
|
18
|
+
|
19
|
+
I thought it was ugly to have to type "include Aquarium::Aspects::DSL::AspectDSL", so I moved the code so
|
20
|
+
now it's "include Aquarium::DSL". However, for backwards compatibility, the old module still works.
|
21
|
+
|
1
22
|
== Version 0.4.0
|
2
23
|
|
3
24
|
V0.4.0 adds specs to characterize and test advising Java classes when running on JRuby and adds several
|
@@ -300,7 +321,7 @@ Fixing 13650 required an API change, which is why I've tagged this release "0.1.
|
|
300
321
|
something like "0.1.1" (and the changes don't seem big enough to warrant "0.2.0"...).
|
301
322
|
|
302
323
|
Previously, requiring "aquarium.rb" in the top-level "lib" directory would implicitly require
|
303
|
-
lib/aquarium/
|
324
|
+
lib/aquarium/dsl/aspect_dsl.rb, which
|
304
325
|
has Object include the AspectDSL module. This module adds methods like :before and :after to Object.
|
305
326
|
Unfortunately, those methods collide with methods of the same name that Rails adds to Object. It was
|
306
327
|
also a bit presumptuous of me to assume that everyone wanted those methods on Object ;)
|
@@ -314,22 +335,22 @@ then do the following:
|
|
314
335
|
|
315
336
|
class MyClass # reopen "MyClass"
|
316
337
|
# Add the methods as _class_ methods
|
317
|
-
include Aquarium::
|
338
|
+
include Aquarium::DSL
|
318
339
|
end
|
319
340
|
|
320
341
|
or, use (class|module)_eval:
|
321
342
|
|
322
|
-
require 'aquarium/
|
343
|
+
require 'aquarium/dsl/aspect_dsl'
|
323
344
|
|
324
345
|
MyClass.class_eval do
|
325
346
|
# Add the methods as _class_ methods
|
326
|
-
include Aquarium::
|
347
|
+
include Aquarium::DSL
|
327
348
|
end
|
328
349
|
|
329
350
|
To add the methods as _instance_ methods on individual objects:
|
330
351
|
|
331
352
|
object = MyClass.new
|
332
|
-
object.extend(Aquarium::
|
353
|
+
object.extend(Aquarium::DSL)
|
333
354
|
|
334
355
|
|
335
356
|
Note: as discussed at http://practicalruby.blogspot.com/2007/02/reopen-with-moduleeval.html,
|
data/README
CHANGED
@@ -85,9 +85,9 @@ Here is an example that traces invocations of all public instance methods (inclu
|
|
85
85
|
result # block needs to return the result of the "proceed"!
|
86
86
|
end
|
87
87
|
|
88
|
-
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/
|
88
|
+
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/dsl/object_dsl', since other toolkits, like Rails, define similar methods on Object!).
|
89
89
|
|
90
|
-
require 'aquarium/
|
90
|
+
require 'aquarium/dsl/object_dsl'
|
91
91
|
around :calls_to => :all_methods, :on_types => [Foo, Bar] do |join_point, object, *args|
|
92
92
|
p "Entering: #{join_point.target_type.name}##{join_point.method_name} for object #{object}"
|
93
93
|
result = join_point.proceed
|
@@ -99,23 +99,23 @@ See "examples/method_tracing_example.rb" for a more detailed version of this exa
|
|
99
99
|
|
100
100
|
If you don't want to add these methods to Object, you can also add them individually to modules, classes, or objects:
|
101
101
|
|
102
|
-
require 'aquarium
|
102
|
+
require 'aquarium'
|
103
103
|
...
|
104
104
|
module MyModule
|
105
|
-
include Aquarium::
|
105
|
+
include Aquarium::DSL
|
106
106
|
end
|
107
107
|
|
108
108
|
class MyClass
|
109
|
-
include Aquarium::
|
109
|
+
include Aquarium::DSL
|
110
110
|
end
|
111
111
|
|
112
112
|
my_object = MyOtherClass.new
|
113
|
-
my_object.extend (Aquarium::
|
113
|
+
my_object.extend (Aquarium::DSL)
|
114
114
|
|
115
115
|
If you use the DSL inside a class and omit the :type(s) and :object(s) options, "self" is assumed.
|
116
116
|
|
117
117
|
class Foo
|
118
|
-
include Aquarium::
|
118
|
+
include Aquarium::DSL
|
119
119
|
...
|
120
120
|
def critical_operation *args
|
121
121
|
...
|
@@ -199,7 +199,7 @@ Some of the synonyms:
|
|
199
199
|
If no types or objects are specified, the object defaults to "self". However, this default is only supported when using the DSL to create an aspect, e.g.,
|
200
200
|
|
201
201
|
class MyClass
|
202
|
-
include Aquarium::
|
202
|
+
include Aquarium::DSL
|
203
203
|
def doit; ...; end
|
204
204
|
|
205
205
|
around :method => doit, ... # Implicit :object => self, i.e., MyClass
|
data/RELEASE-PLAN
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
Mostly bug fixes and enhancements. Only "minor" breakages of backwards compatibility.
|
6
6
|
|
7
|
-
=== Versions 0.2
|
7
|
+
=== Versions 0.2
|
8
8
|
|
9
9
|
Change the argument list for advice to |join_point, object, *args| from |join_point, *args|,
|
10
10
|
where "*args" are the arguments passed to the invoked join point (method). Since nontrivial
|
@@ -13,7 +13,7 @@ advice usually needs the object being advised, it became clear that having to ca
|
|
13
13
|
|
14
14
|
This change will obvious break existing aspects.
|
15
15
|
|
16
|
-
=== Versions 0.3
|
16
|
+
=== Versions 0.3+
|
17
17
|
|
18
18
|
More refinements and simplifications to the API and functionality, including much-needed
|
19
19
|
redundancy reduction. I haven't used mocks much in the specs, but they would help improve the
|
@@ -43,6 +43,24 @@ composition of complex pointcuts easier.
|
|
43
43
|
|
44
44
|
I also want to ensure full support for running in JRuby. In particular, you should be able to
|
45
45
|
advise Java types!
|
46
|
+
|
47
|
+
=== Version 0.5
|
48
|
+
|
49
|
+
My goals for this release are performance improvements (#19321) and investigating some ideas
|
50
|
+
for a real DSL, meaning declarative statements in blocks, rather than method arguments. It will be
|
51
|
+
redundant somewhat with the existing "method-argument form", as it exists today, but it will set
|
52
|
+
the stage for much more complex aspect definitions than would be convenient with the current form.
|
53
|
+
|
54
|
+
I'll maintain the current form for backwards compatibility and also because it is convenient for
|
55
|
+
simpler aspects.
|
56
|
+
|
57
|
+
=== Version 0.6+
|
58
|
+
|
59
|
+
I have been thinking about higher-order abstractions that work above the "Pointcut + Advice
|
60
|
+
Model" of Aquarium (and AspectJ...) today. I consider the pointcut + advice model to be an
|
61
|
+
important, maybe essential, building block of AOP, but if that's all AOP is, then we've probably
|
62
|
+
already hit the limit of what we can expect AOP to do. That doesn't seem right to me, but it's
|
63
|
+
not at all clear what the higher-order abstractions should be.
|
46
64
|
|
47
65
|
=== Version 1.0.0
|
48
66
|
|
data/TODO.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
# New DSL ideas.
|
3
|
+
Aspect.new do
|
4
|
+
|
5
|
+
pointcut do
|
6
|
+
named_pointcuts :matching => /^STATE/, :within_types_and_descendents => /Foo|Bar/
|
7
|
+
or {
|
8
|
+
calls_to /index/
|
9
|
+
in_types_and_ancestors_of [String, Fixnum]
|
10
|
+
} and {
|
11
|
+
calls_to /outdex/
|
12
|
+
in_types_and_ancestors_of /^Foo/
|
13
|
+
}
|
14
|
+
end
|
15
|
+
within_pointcuts do
|
16
|
+
calls_to main
|
17
|
+
in_type TopLevel
|
18
|
+
end
|
19
|
+
but_not_within pointcuts do
|
20
|
+
calls_from /callback$/
|
21
|
+
in_types_and_descendents_of /Active/
|
22
|
+
end
|
23
|
+
after do |join_point, object, *args|
|
24
|
+
p join_point.inspect
|
25
|
+
end
|
26
|
+
end
|
data/UPGRADE
CHANGED
@@ -53,21 +53,21 @@ As an alternative, if you just want these methods added selectively in certain t
|
|
53
53
|
following:
|
54
54
|
|
55
55
|
<ruby>
|
56
|
-
require 'aquarium/
|
56
|
+
require 'aquarium/dsl/aspect_dsl'
|
57
57
|
|
58
58
|
class MyClass # reopen "MyClass"
|
59
59
|
# Add the methods as _class_ methods
|
60
|
-
include Aquarium::
|
60
|
+
include Aquarium::DSL
|
61
61
|
end
|
62
62
|
</ruby>
|
63
63
|
|
64
64
|
or, use (class|module)_eval:
|
65
65
|
<ruby>
|
66
|
-
require 'aquarium/
|
66
|
+
require 'aquarium/dsl/aspect_dsl'
|
67
67
|
|
68
68
|
MyClass.class_eval do
|
69
69
|
# Add the methods as _class_ methods
|
70
|
-
include Aquarium::
|
70
|
+
include Aquarium::DSL
|
71
71
|
end
|
72
72
|
</ruby>
|
73
73
|
|
@@ -75,7 +75,7 @@ To add the methods as _instance_ methods on individual objects:
|
|
75
75
|
|
76
76
|
<ruby>
|
77
77
|
object = MyClass.new
|
78
|
-
object.extend(Aquarium::
|
78
|
+
object.extend(Aquarium::DSL)
|
79
79
|
</ruby>
|
80
80
|
|
81
81
|
See the CHANGES for more details.
|
@@ -33,12 +33,12 @@ Aquarium::PreCond.new.action :a1
|
|
33
33
|
module Aquarium
|
34
34
|
class PostCond
|
35
35
|
def action *args
|
36
|
-
|
36
|
+
args.empty? ? args.dup : args + [:a]
|
37
37
|
end
|
38
38
|
|
39
39
|
postcondition :calls_to => :action,
|
40
|
-
:message => "Must
|
41
|
-
args.size
|
40
|
+
:message => "Must return a copy of the input args with :a appended to it." do |jp, obj, *args|
|
41
|
+
jp.context.returned_value.size == args.size + 1 && jp.context.returned_value[-1] == :a
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
@@ -49,13 +49,8 @@ begin
|
|
49
49
|
rescue Aquarium::Extras::DesignByContract::ContractError => e
|
50
50
|
p e.inspect
|
51
51
|
end
|
52
|
-
begin
|
53
|
-
Aquarium::PostCond.new.action ""
|
54
|
-
rescue Aquarium::Extras::DesignByContract::ContractError => e
|
55
|
-
p e.inspect
|
56
|
-
end
|
57
52
|
p "This call will pass because the postcondition is satisfied:"
|
58
|
-
Aquarium::PostCond.new.action :
|
53
|
+
Aquarium::PostCond.new.action :x1, :x2
|
59
54
|
|
60
55
|
module Aquarium
|
61
56
|
class InvarCond
|
@@ -37,27 +37,25 @@ end
|
|
37
37
|
module Aquarium
|
38
38
|
class PostCondExample
|
39
39
|
def action *args
|
40
|
-
|
40
|
+
args.empty? ? args.dup : args + [:a]
|
41
41
|
end
|
42
|
-
|
43
|
-
|
42
|
+
|
44
43
|
postcondition :calls_to => :action,
|
45
|
-
:message => "Must
|
46
|
-
args.size
|
44
|
+
:message => "Must return a copy of the input args with :a appended to it." do |jp, obj, *args|
|
45
|
+
jp.context.returned_value.size == args.size + 1 && jp.context.returned_value[-1] == :a
|
47
46
|
end
|
48
47
|
end
|
49
48
|
end
|
50
49
|
|
51
50
|
describe "An example using a postcondition" do
|
52
51
|
it "should fail at the call exit point if the postcondition is not satisfied." do
|
53
|
-
lambda { Aquarium::PostCondExample.new.action }.should
|
54
|
-
lambda { Aquarium::PostCondExample.new.action "" }.should raise_error(Aquarium::Extras::DesignByContract::ContractError)
|
52
|
+
lambda { Aquarium::PostCondExample.new.action }.should raise_error(Aquarium::Extras::DesignByContract::ContractError)
|
55
53
|
end
|
56
54
|
end
|
57
55
|
|
58
56
|
describe "An example using a postcondition" do
|
59
|
-
it "should not fail at the call
|
60
|
-
Aquarium::PostCondExample.new.action :
|
57
|
+
it "should not fail at the call exit point if the postcondition is satisfied." do
|
58
|
+
Aquarium::PostCondExample.new.action :x1, :x2
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Example demonstrating "wrapping" an exception; rescuing an exception and
|
3
|
+
# throwing a different one. A common use for this is to map exceptions across
|
4
|
+
# "domain" boundaries, e.g., persistence and application logic domains.
|
5
|
+
# Note that you must use :around advice, since :after_raising cannot change
|
6
|
+
# the control flow.
|
7
|
+
# (However, see feature request #19119)
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
10
|
+
require 'aquarium'
|
11
|
+
|
12
|
+
module Aquarium
|
13
|
+
class Exception1 < Exception; end
|
14
|
+
class Exception2 < Exception; end
|
15
|
+
class NewException < Exception; end
|
16
|
+
|
17
|
+
class Raiser
|
18
|
+
def raise_exception1
|
19
|
+
raise Exception1.new("one")
|
20
|
+
end
|
21
|
+
def raise_exception2
|
22
|
+
raise Exception2.new("two")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Aquarium::Aspects::Aspect.new :around,
|
28
|
+
:calls_to => /^raise_exception/, :in_type => Aquarium::Raiser do |jp, obj, *args|
|
29
|
+
begin
|
30
|
+
jp.proceed
|
31
|
+
rescue Aquarium::Exception1 => e
|
32
|
+
raise Aquarium::NewException.new("Old exception message was \"#{e.message}\"")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
p "The raised Aquarium::Exception2 raised here won't be intercepted:"
|
37
|
+
begin
|
38
|
+
Aquarium::Raiser.new.raise_exception2
|
39
|
+
rescue Aquarium::Exception2 => e
|
40
|
+
p "Rescued exception: #{e.class} with message: #{e}"
|
41
|
+
end
|
42
|
+
|
43
|
+
p "The raised Aquarium::Exception1 raised here will be intercepted and Aquarium::NewException will be raised:"
|
44
|
+
begin
|
45
|
+
Aquarium::Raiser.new.raise_exception1
|
46
|
+
rescue Aquarium::NewException => e
|
47
|
+
p "Rescued exception: #{e.class} with message: #{e}"
|
48
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec/aquarium/spec_helper'
|
2
|
+
require 'aquarium'
|
3
|
+
|
4
|
+
# Example demonstrating "wrapping" an exception; rescuing an exception and
|
5
|
+
# throwing a different one. A common use for this is to map exceptions across
|
6
|
+
# "domain" boundaries, e.g., persistence and application logic domains.
|
7
|
+
# Note that you must use :around advice, since :after_raising cannot change
|
8
|
+
# the control flow.
|
9
|
+
# (However, see feature request #19119)
|
10
|
+
|
11
|
+
module Aquarium
|
12
|
+
class Exception1 < Exception; end
|
13
|
+
class Exception2 < Exception; end
|
14
|
+
class NewException < Exception; end
|
15
|
+
|
16
|
+
class Raiser
|
17
|
+
def raise_exception1
|
18
|
+
raise Exception1.new("one")
|
19
|
+
end
|
20
|
+
def raise_exception2
|
21
|
+
raise Exception2.new("two")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "Rescuing one exception type and raising a second type" do
|
27
|
+
before :all do
|
28
|
+
@aspect = Aquarium::Aspects::Aspect.new :around,
|
29
|
+
:calls_to => /^raise_exception/, :in_type => Aquarium::Raiser do |jp, obj, *args|
|
30
|
+
begin
|
31
|
+
jp.proceed
|
32
|
+
rescue Aquarium::Exception1 => e
|
33
|
+
raise Aquarium::NewException.new("New Exception: old exception message was: #{e.message}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@raiser = Aquarium::Raiser.new
|
37
|
+
end
|
38
|
+
|
39
|
+
after :all do
|
40
|
+
@aspect.unadvise
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should intercept the specified type of exception" do
|
44
|
+
lambda { @raiser.raise_exception1 }.should raise_error(Aquarium::NewException)
|
45
|
+
end
|
46
|
+
it "should not intercept other types of exceptions" do
|
47
|
+
lambda { @raiser.raise_exception2 }.should raise_error(Aquarium::Exception2)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Example demonstrating a hack for defining a reusable aspect in a module
|
3
|
+
# so that the aspect only gets created when the module is included by another
|
4
|
+
# module or class.
|
5
|
+
# Hacking like this defies the spirit of Aquarium's goal of being "intuitive",
|
6
|
+
# so I created a feature request #19122 to address this problem.
|
7
|
+
#
|
8
|
+
# WARNING: put the "include ..." statement at the END of the class declaration,
|
9
|
+
# as shown below. If you put the include statement at the beginning, as you
|
10
|
+
# normally wouuld for including a module, it won't advice any join points,
|
11
|
+
# because no methods will have been defined at that point!!
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
14
|
+
require 'aquarium'
|
15
|
+
|
16
|
+
module Aquarium
|
17
|
+
module Reusables
|
18
|
+
module TraceMethods
|
19
|
+
def self.append_features mod
|
20
|
+
Aquarium::Aspects::Aspect.new :around,
|
21
|
+
:type => mod, :methods => :all, :method_options => [:exclude_ancestor_methods] do |jp, object, *args|
|
22
|
+
p "Entering: "+jp.target_type.name+"#"+jp.method_name.to_s+": args = "+args.inspect
|
23
|
+
jp.proceed
|
24
|
+
p "Leaving: "+jp.target_type.name+"#"+jp.method_name.to_s+": args = "+args.inspect
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class NotTraced1
|
32
|
+
def doit; p "NotTraced1#doit"; end
|
33
|
+
end
|
34
|
+
p "You will be warned that no join points in NotTraced2 were matched."
|
35
|
+
p "This happens because the include statement and hence the aspect evaluation happen BEFORE any methods are defined!"
|
36
|
+
class NotTraced2
|
37
|
+
include Aquarium::Reusables::TraceMethods
|
38
|
+
def doit; p "NotTraced2#doit"; end
|
39
|
+
end
|
40
|
+
class Traced1
|
41
|
+
def doit; p "Traced1#doit"; end
|
42
|
+
include Aquarium::Reusables::TraceMethods
|
43
|
+
end
|
44
|
+
class Traced2
|
45
|
+
def doit; p "Traced1#doit"; end
|
46
|
+
include Aquarium::Reusables::TraceMethods
|
47
|
+
end
|
48
|
+
|
49
|
+
p ""
|
50
|
+
p "No method tracing:"
|
51
|
+
NotTraced1.new.doit
|
52
|
+
NotTraced1.new.doit
|
53
|
+
p ""
|
54
|
+
p "Method tracing:"
|
55
|
+
Traced1.new.doit
|
56
|
+
Traced2.new.doit
|