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,302 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../spec_example_classes'
3
+
4
+ require 'aquarium/extensions/hash'
5
+ require 'aquarium/aspects/join_point'
6
+
7
+ class Dummy
8
+ def eql?; false; end
9
+ def count; 0; end
10
+ end
11
+
12
+ describe Aquarium::Aspects::JoinPoint, "#initialize with invalid parameters" do
13
+
14
+ it "should require either a :type or an :object parameter when creating." do
15
+ lambda { Aquarium::Aspects::JoinPoint.new :method_name => :count }.should raise_error(Aquarium::Utils::InvalidOptions)
16
+ lambda { Aquarium::Aspects::JoinPoint.new :type => String, :object => "", :method_name => :count }.should raise_error(Aquarium::Utils::InvalidOptions)
17
+ end
18
+
19
+ it "should require a :method_name parameter when creating." do
20
+ lambda { Aquarium::Aspects::JoinPoint.new :type => String }.should raise_error(Aquarium::Utils::InvalidOptions)
21
+ end
22
+
23
+ it "should except :method as a synonym for the :method_name parameter." do
24
+ lambda { Aquarium::Aspects::JoinPoint.new :type => String, :method => :split }.should_not raise_error(Aquarium::Utils::InvalidOptions)
25
+ end
26
+ end
27
+
28
+ describe Aquarium::Aspects::JoinPoint, "#initialize with invalid parameters" do
29
+ it "should assume the :method_name refers to an instance method, by default." do
30
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split
31
+ jp.is_instance_method?.should be_true
32
+ end
33
+
34
+ it "should treat the :method_name as refering to an instance method if :is_instance_method is specified as true." do
35
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_instance_method => true
36
+ jp.is_instance_method?.should be_true
37
+ end
38
+
39
+ it "should treat the :method_name as refering to a class method if :is_instance_method is specified as false." do
40
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_instance_method => false
41
+ jp.is_instance_method?.should be_false
42
+ end
43
+
44
+ it "should treat the :method_name as refering to an instance method if :is_class_method is specified as false." do
45
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_class_method => false
46
+ jp.is_instance_method?.should be_true
47
+ end
48
+
49
+ it "should treat the :method_name as refering to a class method if :is_class_method is specified as true." do
50
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_class_method => true
51
+ jp.is_instance_method?.should be_false
52
+ end
53
+
54
+ it "should treat give precedence to :is_instance_method if appears with :is_class_method." do
55
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_instance_method => false, :is_class_method => true
56
+ jp.is_instance_method?.should be_false
57
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method => :split, :is_instance_method => true, :is_class_method => false
58
+ jp.is_instance_method?.should be_true
59
+ end
60
+ end
61
+
62
+ describe Aquarium::Aspects::JoinPoint, "#dup" do
63
+ it "should duplicate the fields in the join point." do
64
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :count
65
+ jp2 = jp.dup
66
+ jp2.should eql(jp)
67
+ end
68
+ end
69
+
70
+ describe Aquarium::Aspects::JoinPoint, "#eql?" do
71
+ setup do
72
+ @jp1 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
73
+ @jp2 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
74
+ @jp3 = Aquarium::Aspects::JoinPoint.new :type => Array, :method_name => :size
75
+ @jp4 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
76
+ @jp5 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
77
+ end
78
+
79
+ it "should return true for the same join point." do
80
+ @jp1.should eql(@jp1)
81
+ end
82
+
83
+ it "should return true for an identical join point." do
84
+ @jp1.should eql(@jp2)
85
+ end
86
+
87
+ it "should return false for a non-identical join point." do
88
+ @jp1.should_not eql(@jp3)
89
+ end
90
+
91
+ it "should return false when one join point matches a method for a class and the other matches the same method in an instance of the class." do
92
+ @jp3.should_not eql(@jp4)
93
+ end
94
+
95
+ it "should return false for a non-join point object." do
96
+ @jp1.should_not eql("foo")
97
+ end
98
+
99
+ it "should return false for two join points that are equal except for the ids of the object they reference." do
100
+ @jp4.should_not eql(@jp5)
101
+ end
102
+ end
103
+
104
+ describe Aquarium::Aspects::JoinPoint, "#==" do
105
+ setup do
106
+ @jp1 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
107
+ @jp2 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
108
+ @jp3 = Aquarium::Aspects::JoinPoint.new :type => Array, :method_name => :size
109
+ @jp4 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
110
+ @jp5 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
111
+ end
112
+
113
+ it "should return true for the same join point." do
114
+ @jp1.should == @jp1
115
+ end
116
+
117
+ it "should return true for an identical join point." do
118
+ @jp1.should == @jp2
119
+ end
120
+
121
+ it "should return false for a non-identical join point." do
122
+ @jp1.should_not == @jp3
123
+ end
124
+
125
+ it "should return false when one join point matches a method for a class and the other matches the same method in an instance of the class." do
126
+ @jp3.should_not == @jp4
127
+ end
128
+
129
+ it "should return false for a non-join point object." do
130
+ @jp1.should_not == "foo"
131
+ end
132
+
133
+ it "should return false for two join points that are equal except for the ids of the object they reference." do
134
+ @jp4.should_not == @jp5
135
+ end
136
+ end
137
+
138
+ describe Aquarium::Aspects::JoinPoint, "#<=>" do
139
+ setup do
140
+ @jp1 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
141
+ @jp2 = Aquarium::Aspects::JoinPoint.new :type => Dummy, :method_name => :count
142
+ @jp3 = Aquarium::Aspects::JoinPoint.new :type => Array, :method_name => :size
143
+ @jp4 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
144
+ @jp5 = Aquarium::Aspects::JoinPoint.new :object => [], :method_name => :size
145
+ end
146
+
147
+ it "should sort return 0 for the same join points" do
148
+ (@jp1.<=>@jp1).should == 0
149
+ end
150
+
151
+ it "should sort return 0 for equivalent join points" do
152
+ (@jp1.<=>@jp2).should == 0
153
+ end
154
+
155
+ it "should sort by type name first" do
156
+ end
157
+ end
158
+
159
+ describe Aquarium::Aspects::JoinPoint, "#make_current_context_join_point when the Aquarium::Aspects::JoinPoint::Context object is nil" do
160
+ it "should return a new join_point that contains the non-context information of the advised_object plus a new Aquarium::Aspects::JoinPoint::Context with the specified context information." do
161
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
162
+ jp.context.should be_nil
163
+ object = "12,34"
164
+ jp_with_context = jp.make_current_context_join_point :advice_kind => :before, :advised_object => object, :parameters => [","], :returned_value => ["12", "34"]
165
+ jp_with_context.object_id.should_not == jp.object_id
166
+ jp.context.should be_nil
167
+ jp_with_context.context.should_not be_nil
168
+ jp_with_context.context.advice_kind.should == :before
169
+ jp_with_context.context.advised_object.should == object
170
+ jp_with_context.context.parameters.should == [","]
171
+ jp_with_context.context.returned_value.should == ["12", "34"]
172
+ jp_with_context.context.raised_exception.should be_nil
173
+ end
174
+ end
175
+
176
+ describe Aquarium::Aspects::JoinPoint, "#make_current_context_join_point when the Aquarium::Aspects::JoinPoint::Context object is not nil" do
177
+ it "should return a new join_point that contains the non-context information of the advised_object plus an updated Aquarium::Aspects::JoinPoint::Context with the specified context information." do
178
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
179
+ object = "12,34"
180
+ jp.context = Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :advised_object => object, :parameters => [","], :returned_value => ["12", "34"]
181
+ exception = RuntimeError.new
182
+ jp_after = jp.make_current_context_join_point :advice_kind => :after, :returned_value => ["12", "34", "56"], :raised_exception => exception
183
+ jp_after.object_id.should_not == jp.object_id
184
+ jp_after.context.should_not eql(jp.context)
185
+ jp.context.advice_kind.should == :before
186
+ jp.context.advised_object.should == object
187
+ jp.context.parameters.should == [","]
188
+ jp.context.returned_value.should == ["12", "34"]
189
+ jp.context.raised_exception.should be_nil
190
+ jp_after.context.advice_kind.should == :after
191
+ jp_after.context.advised_object.should == object
192
+ jp_after.context.parameters.should == [","]
193
+ jp_after.context.returned_value.should == ["12", "34", "56"]
194
+ jp_after.context.raised_exception.should == exception
195
+ end
196
+ end
197
+
198
+ describe Aquarium::Aspects::JoinPoint, "#type_or_object" do
199
+ it "should return the type if the object is nil" do
200
+ jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
201
+ jp.type_or_object.should eql(String)
202
+ end
203
+
204
+ it "should return the object if the type is nil" do
205
+ jp = Aquarium::Aspects::JoinPoint.new :object => String.new, :method_name => :split
206
+ jp.type_or_object.should eql("")
207
+ end
208
+ end
209
+
210
+ describe Aquarium::Aspects::JoinPoint::Context, "#initialize" do
211
+ it "should require :advice_kind, :advised_object and :parameters arguments." do
212
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advised_object => "object", :parameters => [","]}.should raise_error(Aquarium::Utils::InvalidOptions)
213
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :parameters => [","]}.should raise_error(Aquarium::Utils::InvalidOptions)
214
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :advised_object => "object"}.should raise_error(Aquarium::Utils::InvalidOptions)
215
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :advised_object => "object", :parameters => [","]}.should_not raise_error(Aquarium::Utils::InvalidOptions)
216
+ end
217
+
218
+ it "should accept a :returned_value argument." do
219
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :advised_object => "object", :parameters => [","], :returned_value => ["12", "34"]}.should_not raise_error(Aquarium::Utils::InvalidOptions)
220
+ end
221
+
222
+ it "should accept a :raised_exception argument." do
223
+ lambda { Aquarium::Aspects::JoinPoint::Context.new :advice_kind => :before, :advised_object => "object", :parameters => [","], :raised_exception => NameError.new}.should_not raise_error(Aquarium::Utils::InvalidOptions)
224
+ end
225
+
226
+ end
227
+
228
+ describe Aquarium::Aspects::JoinPoint::Context, "#target_object" do
229
+ it "should be a synonym for #advised_object." do
230
+ @object = "12,34"
231
+ @jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
232
+ @jp_with_context = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
233
+ @jp_with_context.context.target_object.should == @jp_with_context.context.advised_object
234
+ end
235
+ end
236
+
237
+ describe Aquarium::Aspects::JoinPoint::Context, "#target_object=" do
238
+ it "should be a synonym for #advised_object=." do
239
+ @object = "12,34"
240
+ @object2 = "12,34,56"
241
+ @jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
242
+ @jp_with_context = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
243
+ @jp_with_context.context.target_object = @object2
244
+ @jp_with_context.context.target_object.should == @object2
245
+ @jp_with_context.context.advised_object.should == @object2
246
+ end
247
+ end
248
+
249
+ def do_common_eql_setup
250
+ @object = "12,34"
251
+ @object2 = "12,34,56"
252
+ @jp = Aquarium::Aspects::JoinPoint.new :type => String, :method_name => :split
253
+ @jp_with_context1 = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
254
+ @jp_with_context2 = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
255
+ @jp_with_context2b = @jp.make_current_context_join_point :advice_kind => :after, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
256
+ @jp_with_context2c = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object2, :parameters => [","], :returned_value => ["12", "34"]
257
+ @jp_with_context2d = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => ["2"], :returned_value => ["1", ",34"]
258
+ end
259
+
260
+ describe Aquarium::Aspects::JoinPoint::Context, "#eql?" do
261
+ setup do
262
+ do_common_eql_setup
263
+ end
264
+
265
+ it "should return true for identical contexts." do
266
+ @jp_with_context1.context.should eql(@jp_with_context2.context)
267
+ end
268
+
269
+ it "should return false for different contexts." do
270
+ @jp_with_context1.context.should_not eql(@jp_with_context2b.context)
271
+ @jp_with_context1.context.should_not eql(@jp_with_context2c.context)
272
+ @jp_with_context1.context.should_not eql(@jp_with_context2d.context)
273
+ end
274
+
275
+ it "should return false if two equal but different objects are specified." do
276
+ @jp_with_context1 = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
277
+ jp_with_diff_object = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => "12,34", :parameters => [","], :returned_value => ["12", "34"]
278
+ @jp_with_context1.context.should_not eql(jp_with_diff_object.context)
279
+ end
280
+ end
281
+
282
+ describe Aquarium::Aspects::JoinPoint::Context, "#==" do
283
+ setup do
284
+ do_common_eql_setup
285
+ end
286
+
287
+ it "should return true for identical contexts." do
288
+ @jp_with_context1.context.should == @jp_with_context2.context
289
+ end
290
+
291
+ it "should return false for different contexts." do
292
+ @jp_with_context1.context.should_not == @jp_with_context2b.context
293
+ @jp_with_context1.context.should_not == @jp_with_context2c.context
294
+ @jp_with_context1.context.should_not == @jp_with_context2d.context
295
+ end
296
+
297
+ it "should return false if two equal but different objects are specified." do
298
+ @jp_with_context1 = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => @object, :parameters => [","], :returned_value => ["12", "34"]
299
+ jp_with_diff_object = @jp.make_current_context_join_point :advice_kind => :before, :advised_object => "12,34", :parameters => [","], :returned_value => ["12", "34"]
300
+ @jp_with_context1.context.should_not == jp_with_diff_object.context
301
+ end
302
+ end
@@ -0,0 +1,131 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../spec_example_classes'
3
+ require 'aquarium/utils'
4
+ require 'aquarium/extensions'
5
+ require 'aquarium/aspects/pointcut'
6
+ require 'aquarium/aspects/pointcut_composition'
7
+
8
+ describe "Aquarium::Aspects::Pointcut#and" do
9
+ include Aquarium::Utils::HashUtils
10
+ include Aquarium::Utils::HtmlEscaper
11
+
12
+ before(:each) do
13
+ @example_types = {}
14
+ [ClassWithPublicInstanceMethod, ClassWithProtectedInstanceMethod, ClassWithPrivateInstanceMethod,
15
+ ClassWithPublicClassMethod, ClassWithPrivateClassMethod].each {|c| @example_types[c] = []}
16
+ @empty_set = Set.new
17
+ end
18
+
19
+ it "should return an empty Aquarium::Aspects::Pointcut if the left-hand Aquarium::Aspects::Pointcut is empty, independent of the right-hand Aquarium::Aspects::Pointcut." do
20
+ pc1 = Aquarium::Aspects::Pointcut.new
21
+ pc2 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
22
+ (pc1.and(pc2)).should == pc1
23
+ pc3 = Aquarium::Aspects::Pointcut.new :object => ClassWithPublicInstanceMethod.new
24
+ (pc1.and(pc3)).should == pc1
25
+ end
26
+
27
+ it "should return an empty Aquarium::Aspects::Pointcut if the second pointcut has no join points." do
28
+ pc1 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
29
+ pc2 = Aquarium::Aspects::Pointcut.new
30
+ (pc1.and(pc2)).should == pc2
31
+ pc3 = Aquarium::Aspects::Pointcut.new :object => ClassWithPublicInstanceMethod.new
32
+ (pc3.and(pc2)).should == pc2
33
+ end
34
+
35
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the intersection of the left- and right-hand side Aquarium::Aspects::Pointcuts, each with multiple types." do
36
+ pc1 = Aquarium::Aspects::Pointcut.new :types => [ClassWithAttribs, ClassWithPublicInstanceMethod, ClassWithPrivateInstanceMethod], :attributes => [/^attr/], :attribute_options => [:readers]
37
+ pc2 = Aquarium::Aspects::Pointcut.new :types => [ClassWithAttribs, ClassWithPublicInstanceMethod, ClassWithProtectedInstanceMethod], :attributes => :attrRW_ClassWithAttribs
38
+ pc = pc1.and pc2
39
+ expected_jp = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttribs, :method => :attrRW_ClassWithAttribs
40
+ pc.join_points_matched.should == Set.new([expected_jp])
41
+ pc.join_points_not_matched.should == @empty_set
42
+ end
43
+
44
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the intersection of the left- and right-hand side Aquarium::Aspects::Pointcuts, each with multiple objects." do
45
+ cwa = ClassWithAttribs.new
46
+ pub = ClassWithPublicInstanceMethod.new
47
+ pri = ClassWithPrivateInstanceMethod.new
48
+ pro = ClassWithProtectedInstanceMethod.new
49
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => [cwa, pub, pri], :attributes => [/^attr/], :attribute_options => [:readers]
50
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => [cwa, pub, pro], :attributes => :attrRW_ClassWithAttribs
51
+ pc = pc1.and pc2
52
+ expected_jp = Aquarium::Aspects::JoinPoint.new :object => cwa, :method => :attrRW_ClassWithAttribs
53
+ pc.join_points_matched.should == Set.new([expected_jp])
54
+ pc.join_points_not_matched.should == @empty_set
55
+ end
56
+
57
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the intersection of the left- and right-hand side Aquarium::Aspects::Pointcuts, each with a single type." do
58
+ pc1 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:readers]
59
+ pc2 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => :attrRW_ClassWithAttribs
60
+ pc = pc1.and pc2
61
+ expected_jp = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttribs, :method => :attrRW_ClassWithAttribs
62
+ pc.join_points_matched.should == Set.new([expected_jp])
63
+ pc.join_points_not_matched.should == @empty_set
64
+ end
65
+
66
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the intersection of the left- and right-hand side Aquarium::Aspects::Pointcuts, each with a single object." do
67
+ cwa = ClassWithAttribs.new
68
+ pc1 = Aquarium::Aspects::Pointcut.new :object => cwa, :attributes => [/^attr/], :attribute_options => [:readers]
69
+ pc2 = Aquarium::Aspects::Pointcut.new :object => cwa, :attributes => :attrRW_ClassWithAttribs
70
+ pc = pc1.and pc2
71
+ expected_jp = Aquarium::Aspects::JoinPoint.new :object => cwa, :method => :attrRW_ClassWithAttribs
72
+ pc.join_points_matched.should == Set.new([expected_jp])
73
+ pc.join_points_not_matched.should == @empty_set
74
+ end
75
+
76
+ it "should be unitary for type-based Aquarium::Aspects::Pointcuts." do
77
+ pc1 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:writers]
78
+ pc2 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:writers]
79
+ pc = pc1.and pc2
80
+ pc.should == pc1
81
+ pc.should == pc2
82
+ end
83
+
84
+ it "should be unitary for object-based Aquarium::Aspects::Pointcuts." do
85
+ cwa = ClassWithAttribs.new
86
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
87
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
88
+ pc = pc1.and pc2
89
+ pc.should == pc1
90
+ pc.should == pc2
91
+ end
92
+
93
+ it "should be commutative for type-based Aquarium::Aspects::Pointcuts." do
94
+ pc1 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:writers]
95
+ pc2 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
96
+ pc12 = pc1.and pc2
97
+ pc21 = pc2.and pc1
98
+ pc12.should == pc21
99
+ end
100
+
101
+ it "should be commutative for object-based Aquarium::Aspects::Pointcuts." do
102
+ cwa = ClassWithAttribs.new
103
+ pub = ClassWithPublicInstanceMethod.new
104
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
105
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => pub, :attributes => [/^attr/], :attribute_options => [:writers]
106
+ pc12 = pc1.and pc2
107
+ pc21 = pc2.and pc1
108
+ pc12.should == pc21
109
+ end
110
+
111
+ it "should be associativity for type-based Aquarium::Aspects::Pointcuts." do
112
+ pc1 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:writers]
113
+ pc2 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:readers]
114
+ pc3 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
115
+ pc123a = (pc1.and(pc2)).and(pc3)
116
+ pc123b = pc1.and(pc2.and(pc3))
117
+ pc123a.should == pc123b
118
+ end
119
+
120
+ it "should be associativity for object-based Aquarium::Aspects::Pointcuts." do
121
+ cwa = ClassWithAttribs.new
122
+ pub = ClassWithPublicInstanceMethod.new
123
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
124
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:readers]
125
+ pc3 = Aquarium::Aspects::Pointcut.new :objects => pub
126
+ pc123a = (pc1.and(pc2)).and(pc3)
127
+ pc123b = pc1.and(pc2.and(pc3))
128
+ pc123a.should == pc123b
129
+ end
130
+
131
+ end
@@ -0,0 +1,111 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/../spec_example_classes'
3
+ require 'aquarium/utils'
4
+ require 'aquarium/extensions'
5
+ require 'aquarium/aspects/pointcut'
6
+ require 'aquarium/aspects/pointcut_composition'
7
+
8
+ describe "Aquarium::Aspects::Pointcut#and" do
9
+ include Aquarium::Utils::HashUtils
10
+
11
+ before(:each) do
12
+ classes = [ClassWithProtectedInstanceMethod, ClassWithPrivateInstanceMethod, ClassWithPublicClassMethod, ClassWithPrivateClassMethod]
13
+ jps_array = classes.map {|c| Aquarium::Aspects::JoinPoint.new :type => c, :method => :all}
14
+ @not_matched_jps = Set.new(jps_array)
15
+ end
16
+
17
+ it "should return a Aquarium::Aspects::Pointcut equal to the second, appended, non-empty Aquarium::Aspects::Pointcut if self is empty (has no join points)." do
18
+ pc1 = Aquarium::Aspects::Pointcut.new
19
+ pc2 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
20
+ pc1.or(pc2).should eql(pc2)
21
+ pc3 = Aquarium::Aspects::Pointcut.new :object => ClassWithPublicInstanceMethod.new
22
+ pc1.or(pc3).should eql(pc3)
23
+ end
24
+
25
+ it "should return a Aquarium::Aspects::Pointcut equal to self if the second pointcut is empty." do
26
+ pc1 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
27
+ pc2 = Aquarium::Aspects::Pointcut.new
28
+ pc1.or(pc2).should eql(pc1)
29
+ pc3 = Aquarium::Aspects::Pointcut.new :object => ClassWithPublicInstanceMethod.new
30
+ pc3.or(pc2).should eql(pc3)
31
+ end
32
+
33
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the union of the left- and right-hand side Aquarium::Aspects::Pointcuts for type-based Aquarium::Aspects::Pointcuts." do
34
+ pc1 = Aquarium::Aspects::Pointcut.new :types => ClassWithAttribs, :attributes => [/^attr/], :attribute_options => [:writers, :suppress_ancestor_methods]
35
+ pc2 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/, :method_options => :suppress_ancestor_methods
36
+ pc = pc1.or pc2
37
+ jp1 = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttribs, :method => :attrRW_ClassWithAttribs=
38
+ jp2 = Aquarium::Aspects::JoinPoint.new :type => ClassWithAttribs, :method => :attrW_ClassWithAttribs=
39
+ jp3 = Aquarium::Aspects::JoinPoint.new :type => ClassWithPublicInstanceMethod, :method => :public_instance_test_method
40
+ pc.join_points_matched.should == Set.new([jp1, jp2, jp3])
41
+ pc.join_points_not_matched.should == @not_matched_jps
42
+ end
43
+
44
+ it "should return a new Aquarium::Aspects::Pointcut whose join points are the union of the left- and right-hand side Aquarium::Aspects::Pointcuts for object-based Aquarium::Aspects::Pointcuts." do
45
+ cwa = ClassWithAttribs.new
46
+ pub = ClassWithPublicInstanceMethod.new
47
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => [cwa], :attributes => [/^attr/], :attribute_options => [:writers, :suppress_ancestor_methods]
48
+ pc2 = Aquarium::Aspects::Pointcut.new :object => pub, :method_options => :suppress_ancestor_methods
49
+ pc = pc1.or pc2
50
+ jp1 = Aquarium::Aspects::JoinPoint.new :object => cwa, :method => :attrRW_ClassWithAttribs=
51
+ jp2 = Aquarium::Aspects::JoinPoint.new :object => cwa, :method => :attrW_ClassWithAttribs=
52
+ jp3 = Aquarium::Aspects::JoinPoint.new :object => pub, :method => :public_instance_test_method
53
+ pc.join_points_matched.sort.should == [jp1, jp2, jp3].sort
54
+ pc.join_points_not_matched.sort.should == []
55
+ end
56
+
57
+ it "should be unitary for type-based Aquarium::Aspects::Pointcuts." do
58
+ pc1 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:writers]
59
+ pc2 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:writers]
60
+ pc = pc1.or pc2
61
+ pc.should eql(pc1)
62
+ pc.should eql(pc2)
63
+ end
64
+
65
+ it "should be unitary for object-based Aquarium::Aspects::Pointcuts." do
66
+ cwa = ClassWithAttribs.new
67
+ pc1 = Aquarium::Aspects::Pointcut.new :object => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
68
+ pc2 = Aquarium::Aspects::Pointcut.new :object => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
69
+ pc = pc1.or pc2
70
+ pc.should eql(pc1)
71
+ pc.should eql(pc2)
72
+ end
73
+
74
+ it "should be commutative for type-based Aquarium::Aspects::Pointcuts." do
75
+ pc1 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:writers]
76
+ pc2 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
77
+ pc12 = pc1.or pc2
78
+ pc21 = pc2.or pc1
79
+ pc12.should eql(pc21)
80
+ end
81
+
82
+ it "should be commutative for object-based Aquarium::Aspects::Pointcuts." do
83
+ cwa = ClassWithAttribs.new
84
+ pub = ClassWithPublicInstanceMethod.new
85
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
86
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => pub, :attributes => [/^attr/], :attribute_options => [:writers]
87
+ pc12 = pc1.or pc2
88
+ pc21 = pc2.or pc1
89
+ pc12.should eql(pc21)
90
+ end
91
+
92
+ it "should be associativity for type-based Aquarium::Aspects::Pointcuts." do
93
+ pc1 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:writers]
94
+ pc2 = Aquarium::Aspects::Pointcut.new :types => "ClassWithAttribs", :attributes => [/^attr/], :attribute_options => [:readers]
95
+ pc3 = Aquarium::Aspects::Pointcut.new :types => /Class.*Method/
96
+ pc123a = (pc1.or(pc2)).or(pc3)
97
+ pc123b = pc1.or(pc2.or(pc3))
98
+ pc123a.should eql(pc123b)
99
+ end
100
+
101
+ it "should be associativity for object-based Aquarium::Aspects::Pointcuts." do
102
+ cwa = ClassWithAttribs.new
103
+ pub = ClassWithPublicInstanceMethod.new
104
+ pc1 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:writers]
105
+ pc2 = Aquarium::Aspects::Pointcut.new :objects => cwa, :attributes => [/^attr/], :attribute_options => [:readers]
106
+ pc3 = Aquarium::Aspects::Pointcut.new :objects => pub
107
+ pc123a = (pc1.or(pc2)).or(pc3)
108
+ pc123b = pc1.or(pc2.or(pc3))
109
+ pc123a.should eql(pc123b)
110
+ end
111
+ end