aquarium 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/CHANGES +4 -0
  2. data/EXAMPLES.rd +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README +250 -0
  5. data/RELEASE-PLAN +1 -0
  6. data/Rakefile +236 -0
  7. data/UPGRADE +3 -0
  8. data/examples/aspect_design_example.rb +36 -0
  9. data/examples/design_by_contract_example.rb +88 -0
  10. data/examples/method_missing_example.rb +44 -0
  11. data/examples/method_tracing_example.rb +64 -0
  12. data/lib/aquarium.rb +7 -0
  13. data/lib/aquarium/aspects.rb +6 -0
  14. data/lib/aquarium/aspects/advice.rb +189 -0
  15. data/lib/aquarium/aspects/aspect.rb +577 -0
  16. data/lib/aquarium/aspects/default_object_handler.rb +27 -0
  17. data/lib/aquarium/aspects/dsl.rb +1 -0
  18. data/lib/aquarium/aspects/dsl/aspect_dsl.rb +61 -0
  19. data/lib/aquarium/aspects/join_point.rb +158 -0
  20. data/lib/aquarium/aspects/pointcut.rb +254 -0
  21. data/lib/aquarium/aspects/pointcut_composition.rb +36 -0
  22. data/lib/aquarium/extensions.rb +5 -0
  23. data/lib/aquarium/extensions/hash.rb +85 -0
  24. data/lib/aquarium/extensions/regexp.rb +20 -0
  25. data/lib/aquarium/extensions/set.rb +49 -0
  26. data/lib/aquarium/extensions/string.rb +13 -0
  27. data/lib/aquarium/extensions/symbol.rb +22 -0
  28. data/lib/aquarium/extras.rb +4 -0
  29. data/lib/aquarium/extras/design_by_contract.rb +64 -0
  30. data/lib/aquarium/finders.rb +4 -0
  31. data/lib/aquarium/finders/finder_result.rb +121 -0
  32. data/lib/aquarium/finders/method_finder.rb +228 -0
  33. data/lib/aquarium/finders/object_finder.rb +74 -0
  34. data/lib/aquarium/finders/type_finder.rb +127 -0
  35. data/lib/aquarium/utils.rb +9 -0
  36. data/lib/aquarium/utils/array_utils.rb +29 -0
  37. data/lib/aquarium/utils/hash_utils.rb +28 -0
  38. data/lib/aquarium/utils/html_escaper.rb +17 -0
  39. data/lib/aquarium/utils/invalid_options.rb +9 -0
  40. data/lib/aquarium/utils/method_utils.rb +18 -0
  41. data/lib/aquarium/utils/nil_object.rb +13 -0
  42. data/lib/aquarium/utils/set_utils.rb +32 -0
  43. data/lib/aquarium/version.rb +30 -0
  44. data/rake_tasks/examples.rake +7 -0
  45. data/rake_tasks/examples_specdoc.rake +8 -0
  46. data/rake_tasks/examples_with_rcov.rake +8 -0
  47. data/rake_tasks/verify_rcov.rake +7 -0
  48. data/spec/aquarium/aspects/advice_chain_node_spec.rb +34 -0
  49. data/spec/aquarium/aspects/advice_spec.rb +103 -0
  50. data/spec/aquarium/aspects/aspect_invocation_spec.rb +111 -0
  51. data/spec/aquarium/aspects/aspect_spec.rb +978 -0
  52. data/spec/aquarium/aspects/aspect_with_nested_types_spec.rb +129 -0
  53. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +423 -0
  54. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +103 -0
  55. data/spec/aquarium/aspects/concurrently_accessed.rb +21 -0
  56. data/spec/aquarium/aspects/dsl/aspect_dsl_spec.rb +514 -0
  57. data/spec/aquarium/aspects/join_point_spec.rb +302 -0
  58. data/spec/aquarium/aspects/pointcut_and_composition_spec.rb +131 -0
  59. data/spec/aquarium/aspects/pointcut_or_composition_spec.rb +111 -0
  60. data/spec/aquarium/aspects/pointcut_spec.rb +800 -0
  61. data/spec/aquarium/extensions/hash_spec.rb +187 -0
  62. data/spec/aquarium/extensions/regex_spec.rb +40 -0
  63. data/spec/aquarium/extensions/set_spec.rb +105 -0
  64. data/spec/aquarium/extensions/string_spec.rb +25 -0
  65. data/spec/aquarium/extensions/symbol_spec.rb +37 -0
  66. data/spec/aquarium/extras/design_by_contract_spec.rb +68 -0
  67. data/spec/aquarium/finders/finder_result_spec.rb +359 -0
  68. data/spec/aquarium/finders/method_finder_spec.rb +878 -0
  69. data/spec/aquarium/finders/method_sorting_spec.rb +16 -0
  70. data/spec/aquarium/finders/object_finder_spec.rb +230 -0
  71. data/spec/aquarium/finders/type_finder_spec.rb +210 -0
  72. data/spec/aquarium/spec_example_classes.rb +117 -0
  73. data/spec/aquarium/spec_helper.rb +3 -0
  74. data/spec/aquarium/utils/array_utils_spec.rb +47 -0
  75. data/spec/aquarium/utils/hash_utils_spec.rb +48 -0
  76. data/spec/aquarium/utils/html_escaper_spec.rb +18 -0
  77. data/spec/aquarium/utils/method_utils_spec.rb +50 -0
  78. data/spec/aquarium/utils/nil_object_spec.rb +19 -0
  79. data/spec/aquarium/utils/set_utils_spec.rb +60 -0
  80. metadata +132 -0
@@ -0,0 +1,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