aquarium 0.1.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.
Files changed (80) hide show
  1. data/CHANGES +4 -0
  2. data/EXAMPLES.rd +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +250 -0
  5. data/RELEASE-PLAN +1 -0
  6. data/Rakefile +236 -0
  7. data/UPGRADE +3 -0
  8. data/examples/aspect_design_example.rb +36 -0
  9. data/examples/design_by_contract_example.rb +88 -0
  10. data/examples/method_missing_example.rb +44 -0
  11. data/examples/method_tracing_example.rb +64 -0
  12. data/lib/aquarium.rb +7 -0
  13. data/lib/aquarium/aspects.rb +6 -0
  14. data/lib/aquarium/aspects/advice.rb +189 -0
  15. data/lib/aquarium/aspects/aspect.rb +577 -0
  16. data/lib/aquarium/aspects/default_object_handler.rb +27 -0
  17. data/lib/aquarium/aspects/dsl.rb +1 -0
  18. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +61 -0
  19. data/lib/aquarium/aspects/join_point.rb +158 -0
  20. data/lib/aquarium/aspects/pointcut.rb +254 -0
  21. data/lib/aquarium/aspects/pointcut_composition.rb +36 -0
  22. data/lib/aquarium/extensions.rb +5 -0
  23. data/lib/aquarium/extensions/hash.rb +85 -0
  24. data/lib/aquarium/extensions/regexp.rb +20 -0
  25. data/lib/aquarium/extensions/set.rb +49 -0
  26. data/lib/aquarium/extensions/string.rb +13 -0
  27. data/lib/aquarium/extensions/symbol.rb +22 -0
  28. data/lib/aquarium/extras.rb +4 -0
  29. data/lib/aquarium/extras/design_by_contract.rb +64 -0
  30. data/lib/aquarium/finders.rb +4 -0
  31. data/lib/aquarium/finders/finder_result.rb +121 -0
  32. data/lib/aquarium/finders/method_finder.rb +228 -0
  33. data/lib/aquarium/finders/object_finder.rb +74 -0
  34. data/lib/aquarium/finders/type_finder.rb +127 -0
  35. data/lib/aquarium/utils.rb +9 -0
  36. data/lib/aquarium/utils/array_utils.rb +29 -0
  37. data/lib/aquarium/utils/hash_utils.rb +28 -0
  38. data/lib/aquarium/utils/html_escaper.rb +17 -0
  39. data/lib/aquarium/utils/invalid_options.rb +9 -0
  40. data/lib/aquarium/utils/method_utils.rb +18 -0
  41. data/lib/aquarium/utils/nil_object.rb +13 -0
  42. data/lib/aquarium/utils/set_utils.rb +32 -0
  43. data/lib/aquarium/version.rb +30 -0
  44. data/rake_tasks/examples.rake +7 -0
  45. data/rake_tasks/examples_specdoc.rake +8 -0
  46. data/rake_tasks/examples_with_rcov.rake +8 -0
  47. data/rake_tasks/verify_rcov.rake +7 -0
  48. data/spec/aquarium/aspects/advice_chain_node_spec.rb +34 -0
  49. data/spec/aquarium/aspects/advice_spec.rb +103 -0
  50. data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -0
  51. data/spec/aquarium/aspects/aspect_spec.rb +978 -0
  52. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +129 -0
  53. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +423 -0
  54. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +103 -0
  55. data/spec/aquarium/aspects/concurrently_accessed.rb +21 -0
  56. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +514 -0
  57. data/spec/aquarium/aspects/join_point_spec.rb +302 -0
  58. data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +131 -0
  59. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +111 -0
  60. data/spec/aquarium/aspects/pointcut_spec.rb +800 -0
  61. data/spec/aquarium/extensions/hash_spec.rb +187 -0
  62. data/spec/aquarium/extensions/regex_spec.rb +40 -0
  63. data/spec/aquarium/extensions/set_spec.rb +105 -0
  64. data/spec/aquarium/extensions/string_spec.rb +25 -0
  65. data/spec/aquarium/extensions/symbol_spec.rb +37 -0
  66. data/spec/aquarium/extras/design_by_contract_spec.rb +68 -0
  67. data/spec/aquarium/finders/finder_result_spec.rb +359 -0
  68. data/spec/aquarium/finders/method_finder_spec.rb +878 -0
  69. data/spec/aquarium/finders/method_sorting_spec.rb +16 -0
  70. data/spec/aquarium/finders/object_finder_spec.rb +230 -0
  71. data/spec/aquarium/finders/type_finder_spec.rb +210 -0
  72. data/spec/aquarium/spec_example_classes.rb +117 -0
  73. data/spec/aquarium/spec_helper.rb +3 -0
  74. data/spec/aquarium/utils/array_utils_spec.rb +47 -0
  75. data/spec/aquarium/utils/hash_utils_spec.rb +48 -0
  76. data/spec/aquarium/utils/html_escaper_spec.rb +18 -0
  77. data/spec/aquarium/utils/method_utils_spec.rb +50 -0
  78. data/spec/aquarium/utils/nil_object_spec.rb +19 -0
  79. data/spec/aquarium/utils/set_utils_spec.rb +60 -0
  80. metadata +132 -0
@@ -0,0 +1,103 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../spec_example_classes'
3
+ require 'aquarium/aspects/advice'
4
+ require 'aquarium/aspects/aspect'
5
+ include Aquarium::Aspects
6
+
7
+ describe Advice, "#sort_by_priority_order" do
8
+ it "should return an empty array for empty input" do
9
+ Aquarium::Aspects::Advice.sort_by_priority_order([]).should == []
10
+ end
11
+
12
+ it "should return a properly-sorted array for arbitrary input of valid advice kind symbols" do
13
+ Aquarium::Aspects::Advice.sort_by_priority_order([:after_raising, :after_returning, :before, :after, :around]).should == [:around, :before, :after, :after_returning, :after_raising]
14
+ end
15
+
16
+ it "should accept strings for the advice kinds, but return sorted symbols" do
17
+ Aquarium::Aspects::Advice.sort_by_priority_order(["after_raising", "after_returning", "before", "after", "around"]).should == [:around, :before, :after, :after_returning, :after_raising]
18
+ end
19
+ end
20
+
21
+ describe Advice, "that raises an exception" do
22
+ it "should add the kind of advice to the exception message." do
23
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
24
+ raise SpecExceptionForTesting.new("advice called with args: #{args.inspect}")
25
+ end
26
+ begin
27
+ Watchful.new.public_watchful_method(:a1, :a2) || fail
28
+ rescue => e
29
+ e.message.should include("\"before\" advice")
30
+ end
31
+ aspect.unadvise
32
+ end
33
+
34
+ it "should add the \"Class#method\" of the advised object's type and method to the exception message." do
35
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
36
+ raise "advice called with args: #{args.inspect}"
37
+ end
38
+ begin
39
+ Watchful.new.public_watchful_method(:a1, :a2) || fail
40
+ rescue => e
41
+ e.message.should include("Watchful#public_watchful_method")
42
+ end
43
+ aspect.unadvise
44
+ end
45
+
46
+ it "should add the \"Class.method\" of the advised type's class method to the exception message." do
47
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_class_watchful_method, :method_options => [:class]} do |jp, *args|
48
+ raise "advice called with args: #{args.inspect}"
49
+ end
50
+ begin
51
+ Watchful.public_class_watchful_method(:a1, :a2) || fail
52
+ rescue => e
53
+ e.message.should include("Watchful.public_class_watchful_method")
54
+ end
55
+ aspect.unadvise
56
+ end
57
+
58
+ it "should rethrow an exception of the same type as the original exception." do
59
+ class MyException < Exception; end
60
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_class_watchful_method, :method_options => [:class]} do |jp, *args|
61
+ raise MyException.new("advice called with args: #{args.inspect}")
62
+ end
63
+ lambda { Watchful.public_class_watchful_method :a1, :a2 }.should raise_error(MyException)
64
+ aspect.unadvise
65
+ end
66
+
67
+ it "should rethrow an exception with the same backtrace as the original exception." do
68
+ class MyException < Exception; end
69
+ @backtrace = nil
70
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_class_watchful_method, :method_options => [:class]} do |jp, *args|
71
+ begin
72
+ exception = MyException.new("advice called with args: #{args.inspect}")
73
+ raise exception
74
+ rescue Exception => e
75
+ @backtrace = e.backtrace
76
+ raise e
77
+ end
78
+ end
79
+ begin
80
+ Watchful.public_class_watchful_method(:a1, :a2) || fail
81
+ rescue Exception => e
82
+ e.backtrace.should == @backtrace
83
+ end
84
+ aspect.unadvise
85
+ end
86
+ end
87
+
88
+ describe AdviceChainNodeFactory, "#make_node" do
89
+ it "should raise if an unknown advice kind is specified" do
90
+ lambda {Aquarium::Aspects::AdviceChainNodeFactory.make_node :advice_kind => :foo}.should raise_error(Aquarium::Utils::InvalidOptions)
91
+ end
92
+
93
+ it "should return a node of the type corresponding to the input advice kind" do
94
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :no).kind_of?(Aquarium::Aspects::NoAdviceChainNode).should be_true
95
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :none).kind_of?(Aquarium::Aspects::NoAdviceChainNode).should be_true
96
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :before).kind_of?(Aquarium::Aspects::BeforeAdviceChainNode).should be_true
97
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :after).kind_of?(Aquarium::Aspects::AfterAdviceChainNode).should be_true
98
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :after_raising).kind_of?(Aquarium::Aspects::AfterRaisingAdviceChainNode).should be_true
99
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :after_returning).kind_of?(Aquarium::Aspects::AfterReturningAdviceChainNode).should be_true
100
+ Aquarium::Aspects::AdviceChainNodeFactory.make_node(:advice_kind => :around).kind_of?(Aquarium::Aspects::AroundAdviceChainNode).should be_true
101
+ end
102
+ end
103
+
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../spec_example_classes'
3
+ require 'aquarium/aspects/aspect'
4
+ require 'aquarium/aspects/dsl'
5
+
6
+ describe Object, "#advise with invalid invocation parameter list" do
7
+ it "should contain at least one of :around, :before, :after, :after_returning, and :after_raising." do
8
+ lambda { advise :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
9
+ end
10
+
11
+ it "should contain no other advice types if :around advice specified." do
12
+ lambda { advise :around, :before, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
13
+ lambda { advise :around, :after, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
14
+ lambda { advise :around, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
15
+ lambda { advise :around, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
16
+ end
17
+
18
+ it "should allow only one of :after, :after_returning, or :after_raising advice to be specified." do
19
+ lambda { advise :after, :after_returning, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
20
+ lambda { advise :after, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
21
+ lambda { advise :after_returning, :after_raising, :pointcut => {:type => Watchful} }.should raise_error(Aquarium::Utils::InvalidOptions)
22
+ end
23
+ end
24
+
25
+ describe Object, "#advise arguments can specify more than one advice types" do
26
+ it "should allow :before to be specified with :after." do
27
+ lambda { advise :before, :after, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
28
+ end
29
+
30
+ it "should allow :before to be specified with :after_returning." do
31
+ lambda { advise :before, :after_returning, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
32
+ end
33
+
34
+ it "should allow :before to be specified with :after_raising." do
35
+ lambda { advise :before, :after_raising, :pointcut => {:type => Watchful}, :noop => true }.should_not raise_error(Aquarium::Utils::InvalidOptions)
36
+ end
37
+ end
38
+
39
+ describe Object, "#advise arguments for specifying the types and methods" do
40
+ it "should advise equivalent join points when :type => T and :method => m is used or :pointcut =>{:type => T, :method => m} is used." do
41
+ advice = proc {|jp,*args| "advice"}
42
+ aspect1 = advise :after, :type => Watchful, :method => :public_watchful_method, &advice
43
+ aspect2 = advise :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
44
+ # We don't use aspect1.should eql(aspect2) because the "specifications" are different.
45
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
46
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
47
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
48
+ aspect1.advice.should eql(aspect2.advice)
49
+ aspect1.unadvise
50
+ end
51
+
52
+ it "should advise equivalent join points when :type => T and :method => m is used or :pointcut => pointcut is used, where pointcut matches :type => T and :method => m." do
53
+ advice = proc {|jp,*args| "advice"}
54
+ aspect1 = advise :after, :type => Watchful, :method => :public_watchful_method, &advice
55
+ pointcut = Aquarium::Aspects::Pointcut.new :type => Watchful, :method => :public_watchful_method
56
+ aspect2 = advise :after, :pointcut => pointcut, &advice
57
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
58
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
59
+ aspect1.advice.should eql(aspect2.advice)
60
+ aspect1.unadvise
61
+ end
62
+
63
+ it "should advise equivalent join points when :pointcut =>{:type => T, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :type => T and :method => m." do
64
+ advice = proc {|jp,*args| "advice"}
65
+ aspect1 = advise :after, :pointcut => {:type => Watchful, :method => :public_watchful_method}, &advice
66
+ pointcut = Aquarium::Aspects::Pointcut.new :type => Watchful, :method => :public_watchful_method
67
+ aspect2 = advise :after, :pointcut => pointcut, &advice
68
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
69
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
70
+ aspect1.advice.should eql(aspect2.advice)
71
+ aspect1.unadvise
72
+ end
73
+ end
74
+
75
+ describe Object, "#advise arguments for specifying the objects and methods" do
76
+ it "should advise equivalent join points when :object => o and :method => m is used or :pointcut =>{:object => o, :method => m} is used." do
77
+ advice = proc {|jp,*args| "advice"}
78
+ watchful = Watchful.new
79
+ aspect1 = advise :after, :object => watchful, :method => :public_watchful_method, &advice
80
+ aspect2 = advise :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
81
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
82
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
83
+ aspect1.advice.should eql(aspect2.advice)
84
+ aspect1.unadvise
85
+ end
86
+
87
+ it "should advise equivalent join points when :object => o and :method => m is used or :pointcut => pointcut is used, where pointcut matches :object => o and :method => m." do
88
+ advice = proc {|jp,*args| "advice"}
89
+ watchful = Watchful.new
90
+ aspect1 = advise :after, :object => watchful, :method => :public_watchful_method, &advice
91
+ pointcut = Aquarium::Aspects::Pointcut.new :object => watchful, :method => :public_watchful_method
92
+ aspect2 = advise :after, :pointcut => pointcut, &advice
93
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
94
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
95
+ aspect1.advice.should eql(aspect2.advice)
96
+ aspect1.unadvise
97
+ end
98
+
99
+ it "should advise equivalent join points when :pointcut =>{:object => o, :method => m} is used or :pointcut => pointcut is used, where pointcut matches :object => o and :method => m." do
100
+ advice = proc {|jp,*args| "advice"}
101
+ watchful = Watchful.new
102
+ aspect1 = advise :after, :pointcut => {:object => watchful, :method => :public_watchful_method}, &advice
103
+ pointcut = Aquarium::Aspects::Pointcut.new :object => watchful, :method => :public_watchful_method
104
+ aspect2 = advise :after, :pointcut => pointcut, &advice
105
+ aspect1.pointcuts.should eql(aspect2.pointcuts)
106
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
107
+ aspect1.advice.should eql(aspect2.advice)
108
+ aspect1.unadvise
109
+ end
110
+ end
111
+
@@ -0,0 +1,978 @@
1
+
2
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
3
+ require File.dirname(__FILE__) + '/../spec_example_classes'
4
+ require 'aquarium/aspects'
5
+
6
+ include Aquarium::Aspects
7
+
8
+ # TODO Could refactor this whole file to use "behave_like", etc.
9
+ describe Aspect, "#new parameters that configure advice" do
10
+ it "should require the kind of advice as the first parameter." do
11
+ lambda {Aspect.new(:pointcuts => {:type => Watchful, :methods => :public_watchful_method}) {|jp, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
12
+ end
13
+
14
+ it "should at least one of :method(s), :pointcut(s), :type(s), or :object(s)." do
15
+ lambda {Aspect.new(:after) {|jp, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
16
+ end
17
+
18
+ it "should at least one of :pointcut(s), :type(s), or :object(s) unless :default_object => object is given." do
19
+ aspect = Aspect.new(:after, :default_object => Watchful.new, :methods => :public_watchful_method) {|jp, *args| true}
20
+ aspect.unadvise
21
+ end
22
+
23
+ it "should not contain :pointcut(s) and either :type(s) or :object(s)." do
24
+ lambda {Aspect.new(:after, :pointcuts => {:type => Watchful, :methods => :public_watchful_method}, :type => Watchful, :methods => :public_watchful_method) {|jp, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
25
+ lambda {Aspect.new(:after, :pointcuts => {:type => Watchful, :methods => :public_watchful_method}, :object => Watchful.new, :methods => :public_watchful_method) {|jp, *args| true}}.should raise_error(Aquarium::Utils::InvalidOptions)
26
+ end
27
+
28
+ it "should include an advice block or :advice => advice parameter." do
29
+ lambda {Aspect.new(:after, :type => Watchful, :methods => :public_watchful_method)}.should raise_error(Aquarium::Utils::InvalidOptions)
30
+ end
31
+ end
32
+
33
+ describe Aspect, "#new :pointcut parameter with a hash of :type(s), :object(s), and/or :method(s)" do
34
+ it "should accept a {:type(s) => [T1, ...], :methods = [...]} hash, indicating the types and methods to advise." do
35
+ advice_called = false
36
+ aspect = Aspect.new :before, :pointcut => {:type => [Watchful], :methods => :public_watchful_method} do |jp, *args|
37
+ advice_called = true
38
+ jp.should_not == nil
39
+ args.size.should == 4
40
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
41
+ end
42
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
43
+ advice_called.should be_true
44
+ aspect.unadvise
45
+ end
46
+
47
+ it "should accept a {:type(s) => T, :methods = [...]} hash, indicating the type and methods to advise." do
48
+ advice_called = false
49
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
50
+ advice_called = true
51
+ jp.should_not == nil
52
+ args.size.should == 4
53
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
54
+ end
55
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
56
+ advice_called.should be_true
57
+ aspect.unadvise
58
+ end
59
+
60
+ %w[public protected private].each do |protection|
61
+ it "should accept a {:type(s) => T, :methods = [...], :method_options =>[:instance, #{protection}]} hash, indicating the type and #{protection} instance methods to advise." do
62
+ advice_called = false
63
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => /watchful_method/, :method_options =>[:instance, protection.intern]} do |jp, *args|
64
+ advice_called = true
65
+ jp.should_not == nil
66
+ args.size.should == 4
67
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
68
+ end
69
+ Watchful.new.method("#{protection}_watchful_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
70
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
71
+ advice_called.should be_true
72
+ aspect.unadvise
73
+ end
74
+ end
75
+
76
+ it "should accept a {:type(s) => T, :methods = [...], :method_options =>[:instance]} hash, indicating the type and (public) instance methods to advise." do
77
+ advice_called = false
78
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
79
+ advice_called = true
80
+ jp.should_not == nil
81
+ args.size.should == 4
82
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
83
+ end
84
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
85
+ advice_called.should be_true
86
+ aspect.unadvise
87
+ end
88
+
89
+ %w[public private].each do |protection|
90
+ it "should accept a {:type(s) => T, :methods = [...], :method_options =>[:class, :#{protection}]} hash, indicating the type and #{protection} class methods to advise." do
91
+ advice_called = false
92
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => /class_watchful_method$/, :method_options =>[:class, protection.intern]} do |jp, *args|
93
+ advice_called = true
94
+ jp.should_not == nil
95
+ args.size.should == 4
96
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
97
+ end
98
+ Watchful.method("#{protection}_class_watchful_method".intern).call :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
99
+ advice_called.should be_true
100
+ aspect.unadvise
101
+ end
102
+ end
103
+
104
+ it "should accept a {:type(s) => T, :methods = [...], :method_options =>:class} hash, indicating the type and (public) class methods to advise." do
105
+ advice_called = false
106
+ aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_class_watchful_method, :method_options =>:class} do |jp, *args|
107
+ advice_called = true
108
+ jp.should_not == nil
109
+ args.size.should == 4
110
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
111
+ end
112
+ Watchful.public_class_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
113
+ advice_called.should be_true
114
+ aspect.unadvise
115
+ end
116
+
117
+ it "should accept a {:objects(s) => [O1, ...], :methods = [...]} hash, indicating the objects and methods to advise." do
118
+ watchful1 = Watchful.new
119
+ watchful2 = Watchful.new
120
+ advice_called = false
121
+ aspect = Aspect.new :before, :pointcut => {:objects => [watchful1, watchful2], :methods => :public_watchful_method} do |jp, *args|
122
+ advice_called = true
123
+ jp.should_not == nil
124
+ args.size.should == 4
125
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
126
+ end
127
+ watchful1.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
128
+ watchful2.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
129
+ advice_called.should be_true
130
+ aspect.unadvise
131
+ end
132
+
133
+ it "should accept a {:objects(s) => O, :methods = [...]} hash, indicating the objects and methods to advise." do
134
+ watchful = Watchful.new
135
+ advice_called = false
136
+ aspect = Aspect.new :before, :pointcut => {:objects => watchful, :methods => :public_watchful_method} do |jp, *args|
137
+ advice_called = true
138
+ jp.should_not == nil
139
+ args.size.should == 4
140
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
141
+ end
142
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
143
+ advice_called.should be_true
144
+ aspect.unadvise
145
+ end
146
+ end
147
+
148
+ describe Aspect, "#new :pointcut parameter with a PointCut object or an array of Pointcuts" do
149
+ it "should accept a single Pointcut object." do
150
+ advice_called = false
151
+ pointcut = Pointcut.new :type => [Watchful], :methods => :public_watchful_method
152
+ aspect = Aspect.new :before, :pointcut => pointcut do |jp, *args|
153
+ advice_called = true
154
+ jp.should_not == nil
155
+ args.size.should == 4
156
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
157
+ end
158
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
159
+ advice_called.should be_true
160
+ aspect.unadvise
161
+ end
162
+
163
+ it "should accept an array of Pointcut objects." do
164
+ advice_called = 0
165
+ pointcut1 = Pointcut.new :type => [Watchful], :methods => :public_watchful_method
166
+ pointcut2 = Pointcut.new :type => [Watchful], :methods => :public_class_watchful_method, :method_options => [:class]
167
+ aspect = Aspect.new :before, :pointcut => [pointcut1, pointcut2] do |jp, *args|
168
+ advice_called += 1
169
+ jp.should_not == nil
170
+ args.size.should == 4
171
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
172
+ end
173
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
174
+ Watchful.public_class_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
175
+ advice_called.should == 2
176
+ aspect.unadvise
177
+ end
178
+
179
+ it "should treat an array of Pointcuts as if they are one Pointcut \"or'ed\" together." do
180
+ advice_called = 0
181
+ advice = Proc.new {|jp, *args|
182
+ advice_called += 1
183
+ jp.should_not == nil
184
+ args.size.should == 4
185
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
186
+ }
187
+ pointcut1 = Pointcut.new :type => [Watchful], :methods => :public_watchful_method
188
+ pointcut2 = Pointcut.new :type => [Watchful], :methods => :public_class_watchful_method, :method_options => [:class]
189
+ pointcut12 = pointcut1.or pointcut2
190
+ aspect1 = Aspect.new :before, :pointcut => [pointcut1, pointcut2], :advice => advice
191
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
192
+ Watchful.public_class_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
193
+ advice_called.should == 2
194
+ aspect1.unadvise
195
+ advice_called = 0
196
+ aspect2 = Aspect.new :before, :pointcut => pointcut12, :advice => advice
197
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
198
+ Watchful.public_class_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
199
+ advice_called.should == 2
200
+ aspect2.unadvise
201
+ aspect1.join_points_matched.should eql(aspect2.join_points_matched)
202
+ end
203
+ end
204
+
205
+ describe Aspect, "#new :type(s), :object(s), and :method(s) parameters" do
206
+ it "should accept :type(s) => [T1, ...] and :methods => [...] parameters indicating the types to advise." do
207
+ advice_called = false
208
+ aspect = Aspect.new :before, :types => [Watchful], :methods => :public_watchful_method do |jp, *args|
209
+ advice_called = true
210
+ jp.should_not == nil
211
+ args.size.should == 4
212
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
213
+ end
214
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
215
+ advice_called.should be_true
216
+ aspect.unadvise
217
+ end
218
+
219
+ it "should accept :type(s) => T and :methods => [...] parameters indicating the types to advise." do
220
+ advice_called = false
221
+ aspect = Aspect.new :before, :type => Watchful, :methods => :public_watchful_method do |jp, *args|
222
+ advice_called = true
223
+ jp.should_not == nil
224
+ args.size.should == 4
225
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
226
+ end
227
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
228
+ advice_called.should be_true
229
+ aspect.unadvise
230
+ end
231
+
232
+ it "should accept :type(s) => /regexp/ and :methods => [...] parameters indicating the types to advise." do
233
+ advice_called = false
234
+ aspect = Aspect.new :before, :type => /^Watchful/, :methods => :public_watchful_method do |jp, *args|
235
+ advice_called = true
236
+ jp.should_not == nil
237
+ args.size.should == 4
238
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
239
+ end
240
+ Watchful.new.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
241
+ advice_called.should be_true
242
+ aspect.unadvise
243
+ end
244
+
245
+ it "should accept :object(s) => [O1, ...] and :methods => [...] parameters indicating the objects to advise." do
246
+ watchful1 = Watchful.new
247
+ watchful2 = Watchful.new
248
+ advice_called = false
249
+ aspect = Aspect.new :before, :object => [watchful1, watchful2], :methods => :public_watchful_method do |jp, *args|
250
+ advice_called = true
251
+ jp.should_not == nil
252
+ args.size.should == 4
253
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
254
+ end
255
+ watchful1.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
256
+ watchful2.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
257
+ advice_called.should be_true
258
+ aspect.unadvise
259
+ end
260
+
261
+ it "should accept :object(s) => O and :methods => [...] parameters indicating the objects to advise." do
262
+ watchful = Watchful.new
263
+ advice_called = false
264
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method do |jp, *args|
265
+ advice_called = true
266
+ jp.should_not == nil
267
+ args.size.should == 4
268
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
269
+ end
270
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
271
+ advice_called.should be_true
272
+ aspect.unadvise
273
+ end
274
+
275
+ it "should accept :object(s) => O and :methods => [...] parameters indicating the objects to advise." do
276
+ watchful = Watchful.new
277
+ advice_called = false
278
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method do |jp, *args|
279
+ advice_called = true
280
+ jp.should_not == nil
281
+ args.size.should == 4
282
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
283
+ end
284
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
285
+ advice_called.should be_true
286
+ aspect.unadvise
287
+ end
288
+ end
289
+
290
+ describe Aspect, "#new block parameter" do
291
+ it "should accept a block as the advice to use." do
292
+ watchful = Watchful.new
293
+ advice_called = false
294
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method do |jp, *args|
295
+ advice_called = true
296
+ jp.should_not == nil
297
+ args.size.should == 4
298
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
299
+ end
300
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
301
+ advice_called.should be_true
302
+ aspect.unadvise
303
+ end
304
+
305
+ it "should accept an :advice => Proc parameter indicating the advice to use." do
306
+ watchful = Watchful.new
307
+ advice_called = false
308
+ advice = Proc.new {|jp, *args|
309
+ advice_called = true
310
+ jp.should_not == nil
311
+ args.size.should == 4
312
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
313
+ }
314
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :advice => advice
315
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
316
+ advice_called.should be_true
317
+ aspect.unadvise
318
+ end
319
+
320
+ it "should accept a :call => Proc parameter as a synonym for :advice." do
321
+ watchful = Watchful.new
322
+ advice_called = false
323
+ advice = Proc.new {|jp, *args|
324
+ advice_called = true
325
+ jp.should_not == nil
326
+ args.size.should == 4
327
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
328
+ }
329
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :call => advice
330
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
331
+ advice_called.should be_true
332
+ aspect.unadvise
333
+ end
334
+
335
+ it "should accept a :invoke => Proc parameter as a synonym for :advice." do
336
+ watchful = Watchful.new
337
+ advice_called = false
338
+ advice = Proc.new {|jp, *args|
339
+ advice_called = true
340
+ jp.should_not == nil
341
+ args.size.should == 4
342
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
343
+ }
344
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :invoke => advice
345
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
346
+ advice_called.should be_true
347
+ aspect.unadvise
348
+ end
349
+
350
+ it "should accept a :advise_with => Proc parameter as a synonym for :advice." do
351
+ watchful = Watchful.new
352
+ advice_called = false
353
+ advice = Proc.new {|jp, *args|
354
+ advice_called = true
355
+ jp.should_not == nil
356
+ args.size.should == 4
357
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
358
+ }
359
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :advise_with => advice
360
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
361
+ advice_called.should be_true
362
+ aspect.unadvise
363
+ end
364
+
365
+ it "should ignore all synonyms if there is an :advice => Proc parameter." do
366
+ watchful = Watchful.new
367
+ advice_called = false
368
+ advice = Proc.new {|jp, *args|
369
+ advice_called = true
370
+ jp.should_not == nil
371
+ args.size.should == 4
372
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
373
+ }
374
+ advice2 = Proc.new {|jp, *args| raise "should not be called"}
375
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :invoke => advice2, :advice => advice
376
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
377
+ advice_called.should be_true
378
+ aspect.unadvise
379
+ end
380
+
381
+ it "should ignore all but the last :advice => Proc parameter." do
382
+ watchful = Watchful.new
383
+ advice_called = false
384
+ advice = Proc.new {|jp, *args|
385
+ advice_called = true
386
+ jp.should_not == nil
387
+ args.size.should == 4
388
+ args.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
389
+ }
390
+ advice2 = Proc.new {|jp, *args| raise "should not be called"}
391
+ aspect = Aspect.new :before, :object => watchful, :methods => :public_watchful_method, :advice => advice2, :advice => advice
392
+ watchful.public_watchful_method :a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2'
393
+ advice_called.should be_true
394
+ aspect.unadvise
395
+ end
396
+ end
397
+
398
+ describe Aspect, "#new invoked for the private implementation methods inserted by other aspects" do
399
+ it "should have no affect." do
400
+ class WithAspectLikeMethod
401
+ def _aspect_foo; end
402
+ end
403
+ aspect = Aspect.new(:after, :pointcut => {:type => WithAspectLikeMethod, :methods => :_aspect_foo}) {|jp, *args| fail}
404
+ WithAspectLikeMethod.new._aspect_foo
405
+ aspect.unadvise
406
+ end
407
+ end
408
+
409
+ describe Aspect, "#new modifications to the list of methods for the advised object or type" do
410
+ before(:all) do
411
+ @advice = Proc.new {}
412
+ end
413
+ after(:each) do
414
+ @aspect.unadvise
415
+ end
416
+
417
+ it "should not include new public instance or class methods for the advised type." do
418
+ all_public_methods_before = all_public_methods_of_type Watchful
419
+ @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :method_options => :suppress_ancestor_methods}, :advice => @advice
420
+ (all_public_methods_of_type(Watchful) - all_public_methods_before).should == []
421
+ end
422
+
423
+ it "should not include new protected instance or class methods for the advised type." do
424
+ all_protected_methods_before = all_protected_methods_of_type Watchful
425
+ @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :method_options => :suppress_ancestor_methods}, :advice => @advice
426
+ (all_protected_methods_of_type(Watchful) - all_protected_methods_before).should == []
427
+ end
428
+
429
+ it "should not include new public methods for the advised object." do
430
+ watchful = Watchful.new
431
+ all_public_methods_before = all_public_methods_of_object Watchful
432
+ @aspect = Aspect.new :after, :pointcut => {:object => watchful, :method_options => :suppress_ancestor_methods}, :advice => @advice
433
+ (all_public_methods_of_object(Watchful) - all_public_methods_before).should == []
434
+ end
435
+
436
+ it "should not include new protected methods for the advised object." do
437
+ watchful = Watchful.new
438
+ all_protected_methods_before = all_protected_methods_of_object Watchful
439
+ @aspect = Aspect.new :after, :pointcut => {:object => watchful, :method_options => :suppress_ancestor_methods}, :advice => @advice
440
+ (all_protected_methods_of_object(Watchful) - all_protected_methods_before).should == []
441
+ end
442
+ end
443
+
444
+ describe Aspect, "#new with :before advice" do
445
+ after(:each) do
446
+ @aspect.unadvise if @aspect
447
+ end
448
+
449
+ it "should pass the context information to the advice, including self and the method parameters." do
450
+ watchful = Watchful.new
451
+ context = nil
452
+ @aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
453
+ context = jp.context
454
+ end
455
+ block_called = 0
456
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }
457
+ block_called.should == 1
458
+ context.advice_kind.should == :before
459
+ context.advised_object.should == watchful
460
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
461
+ context.returned_value.should == nil
462
+ context.raised_exception.should == nil
463
+ end
464
+
465
+ it "should evaluate the advice before the method body and its block (if any)." do
466
+ @aspect = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
467
+ @advice_called += 1
468
+ end
469
+ do_watchful_public_protected_private
470
+ end
471
+ end
472
+
473
+ describe Aspect, "#new with :after advice" do
474
+ after(:each) do
475
+ @aspect.unadvise if @aspect
476
+ end
477
+
478
+ it "should pass the context information to the advice, including self, the method parameters, and the return value when the method returns normally." do
479
+ watchful = Watchful.new
480
+ context = nil
481
+ @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
482
+ context = jp.context
483
+ end
484
+ block_called = 0
485
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }
486
+ block_called.should == 1
487
+ context.advice_kind.should == :after
488
+ context.advised_object.should == watchful
489
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
490
+ context.returned_value.should == block_called
491
+ context.raised_exception.should == nil
492
+ end
493
+
494
+ it "should pass the context information to the advice, including self, the method parameters, and the rescued exception when an exception is raised." do
495
+ watchful = Watchful.new
496
+ context = nil
497
+ @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :methods => /public_watchful_method/} do |jp, *args|
498
+ context = jp.context
499
+ end
500
+ block_called = 0
501
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }}.should raise_error(Watchful::WatchfulError)
502
+ block_called.should == 1
503
+ context.advised_object.should == watchful
504
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
505
+ context.returned_value.should == nil
506
+ context.raised_exception.kind_of?(Watchful::WatchfulError).should be_true
507
+ end
508
+
509
+ it "should evaluate the advice after the method body and its block (if any)." do
510
+ @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
511
+ @advice_called += 1
512
+ end
513
+ do_watchful_public_protected_private
514
+ end
515
+ end
516
+
517
+ describe Aspect, "#new with :after_returning advice" do
518
+ after(:each) do
519
+ @aspect.unadvise if @aspect
520
+ end
521
+
522
+ it "should pass the context information to the advice, including self, the method parameters, and the return value." do
523
+ watchful = Watchful.new
524
+ context = nil
525
+ @aspect = Aspect.new :after_returning, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
526
+ context = jp.context
527
+ end
528
+ block_called = 0
529
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }
530
+ block_called.should == 1
531
+ context.advice_kind.should == :after_returning
532
+ context.advised_object.should == watchful
533
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
534
+ context.returned_value.should == block_called
535
+ context.raised_exception.should == nil
536
+ end
537
+
538
+ it "should evaluate the advice after the method body and its block (if any)." do
539
+ @aspect = Aspect.new :after_returning, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
540
+ @advice_called += 1
541
+ end
542
+ do_watchful_public_protected_private
543
+ end
544
+ end
545
+
546
+ describe Aspect, "#new with :after_raising advice" do
547
+ after(:each) do
548
+ @aspect.unadvise if @aspect
549
+ end
550
+
551
+ it "should pass the context information to the advice, including self, the method parameters, and the rescued exception." do
552
+ watchful = Watchful.new
553
+ context = nil
554
+ @aspect = Aspect.new :after_raising, :pointcut => {:type => Watchful, :methods => /public_watchful_method/} do |jp, *args|
555
+ context = jp.context
556
+ end
557
+ block_called = 0
558
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }}.should raise_error(Watchful::WatchfulError)
559
+ block_called.should == 1
560
+ context.advised_object.should == watchful
561
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
562
+ context.advice_kind.should == :after_raising
563
+ context.returned_value.should == nil
564
+ context.raised_exception.kind_of?(Watchful::WatchfulError).should be_true
565
+ end
566
+
567
+ it "should evaluate the advice after the method body and its block (if any)." do
568
+ @aspect = Aspect.new :after_raising, :pointcut => {:type => Watchful, :methods => /public_watchful_method/} do |jp, *args|
569
+ @advice_called += 1
570
+ end
571
+ do_watchful_public_protected_private true
572
+ end
573
+
574
+ it "should not advise rescue clauses for raised exceptions of types that don't match the specified exception" do
575
+ class MyError < StandardError; end
576
+ aspect_advice_invoked = false
577
+ @aspect = Aspect.new(:after_raising => MyError, :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, *args| aspect_advice_invoked = true}
578
+ block_invoked = false
579
+ watchful = Watchful.new
580
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
581
+ aspect_advice_invoked.should be_false
582
+ block_invoked.should be_true
583
+ end
584
+
585
+ it "should not advise rescue clauses for raised exceptions of types that don't match the list of specified exceptions" do
586
+ class MyError1 < StandardError; end
587
+ class MyError2 < StandardError; end
588
+ aspect_advice_invoked = false
589
+ @aspect = Aspect.new(:after_raising => [MyError1, MyError2], :pointcut => {:type => Watchful, :methods => /public_watchful_method/}) {|jp, *args| aspect_advice_invoked = true}
590
+ block_invoked = false
591
+ watchful = Watchful.new
592
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3) {|*args| block_invoked = true}}.should raise_error(Watchful::WatchfulError)
593
+ aspect_advice_invoked.should be_false
594
+ block_invoked.should be_true
595
+ end
596
+
597
+ it "should advise all rescue clauses in the matched methods, if no specific exceptions are specified" do
598
+ class ClassThatRaises
599
+ class CTRException < Exception; end
600
+ def raises
601
+ raise CTRException
602
+ end
603
+ end
604
+ aspect_advice_invoked = false
605
+ @aspect = Aspect.new :after_raising, :pointcut => {:type => ClassThatRaises, :methods => :raises} do |jp, *args|
606
+ aspect_advice_invoked = true
607
+ end
608
+ aspect_advice_invoked.should be_false
609
+ ctr = ClassThatRaises.new
610
+ lambda {ctr.raises}.should raise_error(ClassThatRaises::CTRException)
611
+ aspect_advice_invoked.should be_true
612
+ end
613
+ end
614
+
615
+ describe Aspect, "#new with :before and :after advice" do
616
+ after(:each) do
617
+ @aspect.unadvise if @aspect
618
+ end
619
+
620
+ it "should pass the context information to the advice, including self and the method parameters, plus the return value for the after-advice case." do
621
+ contexts = []
622
+ @aspect = Aspect.new :before, :after, :pointcut => {:type => Watchful, :methods => [:public_watchful_method]} do |jp, *args|
623
+ contexts << jp.context
624
+ end
625
+ watchful = Watchful.new
626
+ public_block_called = 0
627
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| public_block_called += 1 }
628
+ public_block_called.should == 1
629
+ contexts.size.should == 2
630
+ contexts[0].advice_kind.should == :before
631
+ contexts[1].advice_kind.should == :after
632
+ contexts[0].returned_value.should == nil
633
+ contexts[1].returned_value.should == 1
634
+ contexts.each do |context|
635
+ context.advised_object.should == watchful
636
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
637
+ context.raised_exception.should == nil
638
+ end
639
+
640
+ %w[protected private].each do |protection|
641
+ block_called = 0
642
+ watchful.send("#{protection}_watchful_method", :b1, :b2, :b3) {|*args| block_called += 1}
643
+ block_called.should == 1
644
+ contexts.size.should == 2
645
+ end
646
+ end
647
+
648
+ it "should evaluate the advice before and after the method body and its block (if any)." do
649
+ @aspect = Aspect.new :before, :after, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
650
+ @advice_called += 1
651
+ end
652
+ do_watchful_public_protected_private false, 2
653
+ end
654
+ end
655
+
656
+ describe Aspect, "#new with :before and :after_returning advice" do
657
+ after(:each) do
658
+ @aspect.unadvise if @aspect
659
+ end
660
+
661
+ it "should pass the context information to the advice, including self and the method parameters, plus the return value for the after-advice case." do
662
+ watchful = Watchful.new
663
+ contexts = []
664
+ @aspect = Aspect.new :before, :after_returning, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
665
+ contexts << jp.context
666
+ end
667
+ block_called = 0
668
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }
669
+ block_called.should == 1
670
+ contexts.size.should == 2
671
+ contexts[0].advice_kind.should == :before
672
+ contexts[1].advice_kind.should == :after_returning
673
+ contexts[0].returned_value.should == nil
674
+ contexts[1].returned_value.should == block_called
675
+ contexts.each do |context|
676
+ context.advised_object.should == watchful
677
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
678
+ context.raised_exception.should == nil
679
+ end
680
+ end
681
+
682
+ it "should evaluate the advice before and after the method body and its block (if any)." do
683
+ @aspect = Aspect.new :before, :after_returning, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
684
+ @advice_called += 1
685
+ end
686
+ do_watchful_public_protected_private false, 2
687
+ end
688
+ end
689
+
690
+ describe Aspect, "#new with :before and :after_raising advice" do
691
+ after(:each) do
692
+ @aspect.unadvise if @aspect
693
+ end
694
+
695
+ it "should pass the context information to the advice, including self and the method parameters, plus the raised exception for the after-advice case." do
696
+ watchful = Watchful.new
697
+ contexts = []
698
+ @aspect = Aspect.new :before, :after_raising, :pointcut => {:type => Watchful, :methods => :public_watchful_method_that_raises} do |jp, *args|
699
+ contexts << jp.context
700
+ end
701
+ block_called = 0
702
+ lambda {watchful.public_watchful_method_that_raises(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| block_called += 1 }}.should raise_error(Watchful::WatchfulError)
703
+ block_called.should == 1
704
+ contexts.size.should == 2
705
+ contexts[0].advice_kind.should == :before
706
+ contexts[1].advice_kind.should == :after_raising
707
+ contexts[0].raised_exception.should == nil
708
+ contexts[1].raised_exception.kind_of?(Watchful::WatchfulError).should be_true
709
+ contexts.each do |context|
710
+ context.advised_object.should == watchful
711
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
712
+ context.returned_value.should == nil
713
+ end
714
+ end
715
+
716
+ it "should evaluate the advice before and after the method body and its block (if any)." do
717
+ @aspect = Aspect.new :before, :after_raising, :pointcut => {:type => Watchful, :methods => :public_watchful_method_that_raises} do |jp, *args|
718
+ @advice_called += 1
719
+ end
720
+ do_watchful_public_protected_private true, 2
721
+ end
722
+ end
723
+
724
+ describe Aspect, "#new with :around advice" do
725
+ after(:each) do
726
+ @aspect.unadvise if @aspect
727
+ end
728
+
729
+ it "should pass the context information to the advice, including the object, advice kind, the method invocation parameters, etc." do
730
+ contexts = []
731
+ @aspect = Aspect.new :around, :pointcut => {:type => Watchful, :methods => [:public_watchful_method]} do |jp, *args|
732
+ contexts << jp.context
733
+ end
734
+ watchful = Watchful.new
735
+ public_block_called = false
736
+ protected_block_called = false
737
+ private_block_called = false
738
+ watchful.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| public_block_called = true }
739
+ watchful.send(:protected_watchful_method, :b1, :b2, :b3) {|*args| protected_block_called = true}
740
+ watchful.send(:private_watchful_method, :c1, :c2, :c3) {|*args| private_block_called = true}
741
+ public_block_called.should be_false # proceed is never called!
742
+ protected_block_called.should be_true
743
+ private_block_called.should be_true
744
+ contexts.size.should == 1
745
+ contexts[0].advised_object.should == watchful
746
+ contexts[0].advice_kind.should == :around
747
+ contexts[0].returned_value.should == nil
748
+ contexts[0].parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
749
+ contexts[0].raised_exception.should == nil
750
+ end
751
+
752
+ it "should advise subclass invocations of methods advised in the superclass." do
753
+ context = nil
754
+ @aspect = Aspect.new :around, :pointcut => {:type => Watchful, :methods => [:public_watchful_method]} do |jp, *args|
755
+ context = jp.context
756
+ end
757
+ child = WatchfulChild.new
758
+ public_block_called = false
759
+ protected_block_called = false
760
+ private_block_called = false
761
+ child.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| fail }
762
+ child.send(:protected_watchful_method, :b1, :b2, :b3) {|*args| protected_block_called = true}
763
+ child.send(:private_watchful_method, :c1, :c2, :c3) {|*args| private_block_called = true}
764
+ public_block_called.should be_false # proceed is never called!
765
+ protected_block_called.should be_true
766
+ private_block_called.should be_true
767
+ context.advised_object.should == child
768
+ context.advice_kind.should == :around
769
+ context.returned_value.should == nil
770
+ context.parameters.should == [:a1, :a2, :a3, {:h1 => 'h1', :h2 => 'h2'}]
771
+ context.raised_exception.should == nil
772
+ end
773
+
774
+ it "should not advise subclass overrides of superclass methods, when advising superclasses (but calls to superclass methods are advised)." do
775
+ class WatchfulChild2 < Watchful
776
+ def public_watchful_method *args
777
+ @override_called = true
778
+ yield(*args) if block_given?
779
+ end
780
+ attr_reader :override_called
781
+ def initialize
782
+ super
783
+ @override_called = false
784
+ end
785
+ end
786
+ @aspect = Aspect.new(:around, :pointcut => {:type => Watchful, :methods => [:public_watchful_method]}) {|jp, *args| fail}
787
+ child = WatchfulChild2.new
788
+ public_block_called = false
789
+ child.public_watchful_method(:a1, :a2, :a3, :h1 => 'h1', :h2 => 'h2') { |*args| public_block_called = true }
790
+ public_block_called.should be_true # advice never called
791
+ end
792
+
793
+ it "should evaluate the advice and only evaluate the method body and its block (if any) when JoinPoint#proceed is called." do
794
+ do_around_spec
795
+ end
796
+
797
+ it "should pass the block that was passed to the method by default if the block is not specified explicitly in the around advice in the call to JoinPoint#proceed." do
798
+ do_around_spec
799
+ end
800
+
801
+ it "should pass the parameters and block that were passed to the method by default if JoinPoint#proceed is invoked without parameters and a block." do
802
+ do_around_spec
803
+ end
804
+
805
+ it "should pass parameters passed explicitly to JoinPoint#proceed, rather than the original method parameters, but also pass the original block if a new block is not specified." do
806
+ do_around_spec :a4, :a5, :a6
807
+ end
808
+
809
+ it "should pass parameters and a block passed explicitly to JoinPoint#proceed, rather than the original method parameters and block." do
810
+ override_block_called = false
811
+ @aspect = Aspect.new :around, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
812
+ jp.proceed(:a4, :a5, :a6) {|*args| override_block_called = true}
813
+ end
814
+ watchful = Watchful.new
815
+ orig_block_called = false
816
+ watchful.public_watchful_method(:a1, :a2, :a3) {|*args| orig_block_called = true}
817
+ override_block_called.should be_true
818
+ orig_block_called.should be_false
819
+ watchful.public_watchful_method_args.should == [:a4, :a5, :a6]
820
+ end
821
+
822
+ def do_around_spec *args_passed_to_proceed
823
+ @aspect = Aspect.new :around, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do |jp, *args|
824
+ @advice_called += 1
825
+ returned_value = args_passed_to_proceed.empty? ? jp.proceed : jp.proceed(*args_passed_to_proceed)
826
+ @advice_called += 1
827
+ returned_value
828
+ end
829
+ do_watchful_public_protected_private false, 2, (args_passed_to_proceed.empty? ? nil : args_passed_to_proceed)
830
+ end
831
+ end
832
+
833
+
834
+
835
+ describe Aspect, "#unadvise" do
836
+ before(:all) do
837
+ @advice = Proc.new {}
838
+ end
839
+ it "should do nothing for unadvised types." do
840
+ expected_methods = Watchful.private_methods.sort
841
+ aspect = Aspect.new :around, :type => Watchful, :method => /does_not_exist/, :advice => @advice
842
+ (Watchful.private_methods.sort - expected_methods).should == []
843
+ aspect.unadvise
844
+ (Watchful.private_methods.sort - expected_methods).should == []
845
+ aspect.unadvise
846
+ (Watchful.private_methods.sort - expected_methods).should == []
847
+ end
848
+
849
+ it "should do nothing for unadvised objects." do
850
+ watchful = Watchful.new
851
+ expected_methods = Watchful.private_methods.sort
852
+ aspect = Aspect.new :around, :type => Watchful, :method => /does_not_exist/, :advice => @advice
853
+ (Watchful.private_methods.sort - expected_methods).should == []
854
+ aspect.unadvise
855
+ (Watchful.private_methods.sort - expected_methods).should == []
856
+ aspect.unadvise
857
+ (Watchful.private_methods.sort - expected_methods).should == []
858
+ end
859
+
860
+ it "should remove all advice added by the aspect." do
861
+ advice_called = false
862
+ aspect = Aspect.new(:after, :pointcut => {:type => Watchful, :method_options => :suppress_ancestor_methods}) {|jp, *args| advice_called = true}
863
+ aspect.unadvise
864
+ watchful = Watchful.new
865
+
866
+ %w[public protected private].each do |protection|
867
+ advice_called = false
868
+ block_called = false
869
+ watchful.send("#{protection}_watchful_method".intern, :a1, :a2, :a3) {|*args| block_called = true}
870
+ advice_called.should be_false
871
+ block_called.should be_true
872
+ end
873
+ end
874
+
875
+ it "should remove all advice overhead if all advices are removed." do
876
+ class Foo
877
+ def bar; end
878
+ end
879
+ before = Foo.private_instance_methods.sort
880
+ aspect = Aspect.new(:after, :pointcut => {:type => Foo, :method_options => :suppress_ancestor_methods}) {|jp, *args| true}
881
+ after = Foo.private_instance_methods
882
+ (after - before).should_not == []
883
+ aspect.unadvise
884
+ after = Foo.private_instance_methods
885
+ (after - before).should == []
886
+ end
887
+ end
888
+
889
+ describe Aspect, "#eql?" do
890
+ before(:all) do
891
+ @advice = Proc.new {}
892
+ end
893
+ after(:each) do
894
+ @aspect1.unadvise
895
+ @aspect2.unadvise
896
+ end
897
+
898
+ it "should return true if both aspects have the same specification and pointcuts." do
899
+ @aspect1 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, :advice => @advice
900
+ @aspect2 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, :advice => @advice
901
+ @aspect1.should eql(@aspect2)
902
+ end
903
+
904
+ it "should return true if both aspects have the same specification and pointcuts, even if the advice procs are not equal." do
905
+ @aspect1 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do true end
906
+ @aspect2 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method} do false end
907
+ @aspect1.should eql(@aspect2)
908
+ end
909
+
910
+ it "should return false if each aspect advises pointcuts in different objects, even if the the objects are equivalent." do
911
+ @aspect1 = Aspect.new :before, :pointcut => {:object => Watchful.new, :methods => :public_watchful_method} do true end
912
+ @aspect2 = Aspect.new :before, :pointcut => {:object => Watchful.new, :methods => :public_watchful_method} do false end
913
+ @aspect1.should_not eql(@aspect2)
914
+ end
915
+ end
916
+
917
+ describe Aspect, "#==" do
918
+ before(:all) do
919
+ @advice = Proc.new {}
920
+ end
921
+ after(:each) do
922
+ @aspect1.unadvise
923
+ @aspect2.unadvise
924
+ end
925
+
926
+ it "should be equivalent to #eql?." do
927
+ @aspect1 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, :advice => @advice
928
+ @aspect2 = Aspect.new :before, :pointcut => {:type => Watchful, :methods => :public_watchful_method}, :advice => @advice
929
+ @aspect1.specification.should == @aspect2.specification
930
+ @aspect1.pointcuts.should == @aspect2.pointcuts
931
+ @aspect1.should eql(@aspect2)
932
+ @aspect1.should == @aspect2
933
+ end
934
+ end
935
+
936
+ describe Aspect, "#advice_chain_inspect" do
937
+ it "should return the string '[nil]' if passed a nil advice chain" do
938
+ Aspect.advice_chain_inspect(nil).should == "[nil]"
939
+ chain = NoAdviceChainNode.new({:aspect => nil})
940
+ Aspect.advice_chain_inspect(chain).should include("NoAdviceChainNode")
941
+ end
942
+ end
943
+
944
+ def all_public_methods_of_type type
945
+ (type.public_methods + type.public_instance_methods).sort
946
+ end
947
+ def all_protected_methods_of_type type
948
+ (type.protected_methods + type.protected_instance_methods).sort
949
+ end
950
+ def all_public_methods_of_object object
951
+ object.public_methods.sort
952
+ end
953
+ def all_protected_methods_of_object object
954
+ object.protected_methods.sort
955
+ end
956
+
957
+ def do_watchful_public_protected_private raises = false, expected_advice_called_value = 1, args_passed_to_proceed = nil
958
+ %w[public protected private].each do |protection|
959
+ do_watchful_spec protection, raises, expected_advice_called_value, args_passed_to_proceed
960
+ end
961
+ end
962
+
963
+ def do_watchful_spec protection, raises, expected_advice_called_value, args_passed_to_proceed
964
+ suffix = raises ? "_that_raises" : ""
965
+ expected_advice_called = protection == "public" ? expected_advice_called_value : 0
966
+ watchful = Watchful.new
967
+ @advice_called = 0
968
+ block_called = 0
969
+ if raises
970
+ lambda {watchful.send("#{protection}_watchful_method#{suffix}".intern, :a1, :a2, :a3) {|*args| block_called += 1}}.should raise_error(Watchful::WatchfulError)
971
+ else
972
+ watchful.send("#{protection}_watchful_method#{suffix}".intern, :a1, :a2, :a3) {|*args| block_called += 1}
973
+ end
974
+ @advice_called.should == expected_advice_called
975
+ block_called.should == 1
976
+ expected_args = (protection == "public" && !args_passed_to_proceed.nil?) ? args_passed_to_proceed : [:a1, :a2, :a3]
977
+ watchful.instance_variable_get("@#{protection}_watchful_method#{suffix}_args".intern).should == expected_args
978
+ end