aquarium 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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