dm-core 0.9.11 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -50
  5. data/Manifest.txt +66 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +6 -7
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/deps.rip +2 -0
  12. data/dm-core.gemspec +11 -15
  13. data/lib/dm-core.rb +105 -110
  14. data/lib/dm-core/adapters.rb +135 -16
  15. data/lib/dm-core/adapters/abstract_adapter.rb +251 -181
  16. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  17. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  18. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  19. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  22. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  23. data/lib/dm-core/associations/many_to_many.rb +372 -90
  24. data/lib/dm-core/associations/many_to_one.rb +220 -73
  25. data/lib/dm-core/associations/one_to_many.rb +319 -255
  26. data/lib/dm-core/associations/one_to_one.rb +66 -53
  27. data/lib/dm-core/associations/relationship.rb +561 -156
  28. data/lib/dm-core/collection.rb +1101 -379
  29. data/lib/dm-core/core_ext/kernel.rb +12 -0
  30. data/lib/dm-core/core_ext/symbol.rb +10 -0
  31. data/lib/dm-core/identity_map.rb +4 -34
  32. data/lib/dm-core/migrations.rb +1283 -0
  33. data/lib/dm-core/model.rb +570 -369
  34. data/lib/dm-core/model/descendant_set.rb +81 -0
  35. data/lib/dm-core/model/hook.rb +45 -0
  36. data/lib/dm-core/model/is.rb +32 -0
  37. data/lib/dm-core/model/property.rb +247 -0
  38. data/lib/dm-core/model/relationship.rb +335 -0
  39. data/lib/dm-core/model/scope.rb +90 -0
  40. data/lib/dm-core/property.rb +808 -273
  41. data/lib/dm-core/property_set.rb +141 -98
  42. data/lib/dm-core/query.rb +1037 -483
  43. data/lib/dm-core/query/conditions/comparison.rb +872 -0
  44. data/lib/dm-core/query/conditions/operation.rb +221 -0
  45. data/lib/dm-core/query/direction.rb +43 -0
  46. data/lib/dm-core/query/operator.rb +84 -0
  47. data/lib/dm-core/query/path.rb +138 -0
  48. data/lib/dm-core/query/sort.rb +45 -0
  49. data/lib/dm-core/repository.rb +210 -94
  50. data/lib/dm-core/resource.rb +641 -421
  51. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  52. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  53. data/lib/dm-core/support/chainable.rb +22 -0
  54. data/lib/dm-core/support/deprecate.rb +12 -0
  55. data/lib/dm-core/support/logger.rb +13 -0
  56. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  57. data/lib/dm-core/transaction.rb +333 -92
  58. data/lib/dm-core/type.rb +98 -60
  59. data/lib/dm-core/types/boolean.rb +1 -1
  60. data/lib/dm-core/types/discriminator.rb +34 -20
  61. data/lib/dm-core/types/object.rb +7 -4
  62. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  63. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  64. data/lib/dm-core/types/serial.rb +3 -3
  65. data/lib/dm-core/types/text.rb +3 -4
  66. data/lib/dm-core/version.rb +1 -1
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/migrations_spec.rb +359 -0
  80. data/spec/public/model/relationship_spec.rb +924 -0
  81. data/spec/public/model_spec.rb +159 -0
  82. data/spec/public/property_spec.rb +829 -0
  83. data/spec/public/resource_spec.rb +71 -0
  84. data/spec/public/sel_spec.rb +44 -0
  85. data/spec/public/setup_spec.rb +145 -0
  86. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  87. data/spec/public/shared/collection_shared_spec.rb +1670 -0
  88. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  89. data/spec/public/shared/resource_shared_spec.rb +924 -0
  90. data/spec/public/shared/sel_shared_spec.rb +112 -0
  91. data/spec/public/transaction_spec.rb +129 -0
  92. data/spec/public/types/discriminator_spec.rb +130 -0
  93. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  94. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  95. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  96. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  97. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  99. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  100. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  101. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  102. data/spec/semipublic/associations_spec.rb +177 -0
  103. data/spec/semipublic/collection_spec.rb +142 -0
  104. data/spec/semipublic/property_spec.rb +61 -0
  105. data/spec/semipublic/query/conditions_spec.rb +528 -0
  106. data/spec/semipublic/query/path_spec.rb +443 -0
  107. data/spec/semipublic/query_spec.rb +2626 -0
  108. data/spec/semipublic/resource_spec.rb +47 -0
  109. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  110. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  111. data/spec/spec.opts +3 -1
  112. data/spec/spec_helper.rb +80 -57
  113. data/tasks/ci.rb +19 -31
  114. data/tasks/dm.rb +43 -48
  115. data/tasks/doc.rb +8 -11
  116. data/tasks/gemspec.rb +5 -5
  117. data/tasks/hoe.rb +15 -16
  118. data/tasks/install.rb +8 -10
  119. metadata +74 -111
  120. data/lib/dm-core/associations.rb +0 -207
  121. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  122. data/lib/dm-core/auto_migrations.rb +0 -105
  123. data/lib/dm-core/dependency_queue.rb +0 -32
  124. data/lib/dm-core/hook.rb +0 -11
  125. data/lib/dm-core/is.rb +0 -16
  126. data/lib/dm-core/logger.rb +0 -232
  127. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  128. data/lib/dm-core/migrator.rb +0 -29
  129. data/lib/dm-core/scope.rb +0 -58
  130. data/lib/dm-core/support.rb +0 -7
  131. data/lib/dm-core/support/array.rb +0 -13
  132. data/lib/dm-core/support/assertions.rb +0 -8
  133. data/lib/dm-core/support/errors.rb +0 -23
  134. data/lib/dm-core/support/kernel.rb +0 -11
  135. data/lib/dm-core/support/symbol.rb +0 -41
  136. data/lib/dm-core/type_map.rb +0 -80
  137. data/lib/dm-core/types.rb +0 -19
  138. data/script/all +0 -4
  139. data/spec/integration/association_spec.rb +0 -1382
  140. data/spec/integration/association_through_spec.rb +0 -203
  141. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  142. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  143. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  144. data/spec/integration/auto_migrations_spec.rb +0 -413
  145. data/spec/integration/collection_spec.rb +0 -1073
  146. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  147. data/spec/integration/dependency_queue_spec.rb +0 -46
  148. data/spec/integration/model_spec.rb +0 -197
  149. data/spec/integration/mysql_adapter_spec.rb +0 -85
  150. data/spec/integration/postgres_adapter_spec.rb +0 -731
  151. data/spec/integration/property_spec.rb +0 -253
  152. data/spec/integration/query_spec.rb +0 -514
  153. data/spec/integration/repository_spec.rb +0 -61
  154. data/spec/integration/resource_spec.rb +0 -513
  155. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  156. data/spec/integration/sti_spec.rb +0 -273
  157. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  158. data/spec/integration/transaction_spec.rb +0 -75
  159. data/spec/integration/type_spec.rb +0 -275
  160. data/spec/lib/logging_helper.rb +0 -18
  161. data/spec/lib/mock_adapter.rb +0 -27
  162. data/spec/lib/model_loader.rb +0 -100
  163. data/spec/lib/publicize_methods.rb +0 -28
  164. data/spec/models/content.rb +0 -16
  165. data/spec/models/vehicles.rb +0 -34
  166. data/spec/models/zoo.rb +0 -48
  167. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  168. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  169. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  170. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  171. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  172. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  173. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  174. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  175. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  176. data/spec/unit/associations/relationship_spec.rb +0 -71
  177. data/spec/unit/associations_spec.rb +0 -242
  178. data/spec/unit/auto_migrations_spec.rb +0 -111
  179. data/spec/unit/collection_spec.rb +0 -182
  180. data/spec/unit/data_mapper_spec.rb +0 -35
  181. data/spec/unit/identity_map_spec.rb +0 -126
  182. data/spec/unit/is_spec.rb +0 -80
  183. data/spec/unit/migrator_spec.rb +0 -33
  184. data/spec/unit/model_spec.rb +0 -321
  185. data/spec/unit/naming_conventions_spec.rb +0 -36
  186. data/spec/unit/property_set_spec.rb +0 -90
  187. data/spec/unit/property_spec.rb +0 -753
  188. data/spec/unit/query_spec.rb +0 -571
  189. data/spec/unit/repository_spec.rb +0 -93
  190. data/spec/unit/resource_spec.rb +0 -649
  191. data/spec/unit/scope_spec.rb +0 -142
  192. data/spec/unit/transaction_spec.rb +0 -493
  193. data/spec/unit/type_map_spec.rb +0 -114
  194. data/spec/unit/type_spec.rb +0 -119
@@ -0,0 +1,443 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ # class methods
4
+ describe DataMapper::Query::Path do
5
+ before :all do
6
+ class ::Author
7
+ include DataMapper::Resource
8
+
9
+ property :id, Serial
10
+ property :title, String
11
+
12
+ has n, :articles
13
+ end
14
+
15
+ class ::Article
16
+ include DataMapper::Resource
17
+
18
+ property :id, Serial
19
+ property :title, String
20
+
21
+ belongs_to :author
22
+ end
23
+
24
+ @relationship = Author.relationships[:articles]
25
+ @relationships = [ @relationship ]
26
+ @property = Article.properties[:title]
27
+ end
28
+
29
+ it { DataMapper::Query::Path.should respond_to(:new) }
30
+
31
+ describe '.new' do
32
+ describe 'when supplied an Array of Relationships' do
33
+ before do
34
+ @path = DataMapper::Query::Path.new(@relationships)
35
+ end
36
+
37
+ it 'should return a Query::Path' do
38
+ @path.should be_kind_of(DataMapper::Query::Path)
39
+ end
40
+
41
+ it 'should set Query::Path#relationships' do
42
+ @path.relationships.should eql(@relationships)
43
+ end
44
+
45
+ it 'should copy the relationships' do
46
+ @path.relationships.should_not equal(@relationships)
47
+ end
48
+ end
49
+
50
+ describe 'when supplied an Array of Relationships and a Property Symbol name' do
51
+ before do
52
+ @path = DataMapper::Query::Path.new(@relationships, @property.name)
53
+ end
54
+
55
+ it 'should return a Query::Path' do
56
+ @path.should be_kind_of(DataMapper::Query::Path)
57
+ end
58
+
59
+ it 'should set Query::Path#relationships' do
60
+ @path.relationships.should eql(@relationships)
61
+ end
62
+
63
+ it 'should copy the relationships' do
64
+ @path.relationships.should_not equal(@relationships)
65
+ end
66
+
67
+ it 'should set Query::Path#property' do
68
+ @path.property.should equal(@property)
69
+ end
70
+ end
71
+
72
+ describe 'when supplied an unknown property' do
73
+ it 'should raise an error' do
74
+ lambda { DataMapper::Query::Path.new(@relationships, :unknown) }.should raise_error(ArgumentError, "Unknown property 'unknown' in Article")
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ # instance methods
81
+ describe DataMapper::Query::Path do
82
+ before :all do
83
+ class ::Author
84
+ include DataMapper::Resource
85
+
86
+ property :id, Serial
87
+ property :title, String
88
+
89
+ has n, :articles
90
+ end
91
+
92
+ class ::Article
93
+ include DataMapper::Resource
94
+
95
+ property :id, Serial
96
+ property :title, String
97
+
98
+ belongs_to :author
99
+ end
100
+
101
+ @relationship = Author.relationships[:articles]
102
+ @relationships = [ @relationship ]
103
+ @property = Article.properties[:title]
104
+
105
+ @path = DataMapper::Query::Path.new(@relationships)
106
+ end
107
+
108
+ it { @path.should respond_to(:==) }
109
+
110
+ describe '#==' do
111
+ describe 'when other Query::Path is the same' do
112
+ before do
113
+ @other = @path
114
+
115
+ @return = @path == @other
116
+ end
117
+
118
+ it 'should return true' do
119
+ @return.should be_true
120
+ end
121
+ end
122
+
123
+ describe 'when other Query::Path does not respond to #relationships' do
124
+ before do
125
+ class << @other = @path.dup
126
+ undef_method :relationships
127
+ end
128
+
129
+ @return = @path == @other
130
+ end
131
+
132
+ it 'should return false' do
133
+ @return.should be_false
134
+ end
135
+ end
136
+
137
+ describe 'when other Query::Path does not respond to #property' do
138
+ before do
139
+ class << @other = @path.dup
140
+ undef_method :property
141
+ end
142
+
143
+ @return = @path == @other
144
+ end
145
+
146
+ it 'should return false' do
147
+ @return.should be_false
148
+ end
149
+ end
150
+
151
+ describe 'when other Query::Path has different relationships' do
152
+ before do
153
+ @other = DataMapper::Query::Path.new([ Article.relationships[:author] ])
154
+
155
+ @return = @path == @other
156
+ end
157
+
158
+ it 'should return false' do
159
+ @return.should be_false
160
+ end
161
+ end
162
+
163
+ describe 'when other Query::Path has different properties' do
164
+ before do
165
+ @other = DataMapper::Query::Path.new(@path.relationships, :title)
166
+
167
+ @return = @path == @other
168
+ end
169
+
170
+ it 'should return false' do
171
+ @return.should be_false
172
+ end
173
+ end
174
+
175
+ describe 'when other Query::Path has the same relationship and property' do
176
+ before do
177
+ @other = DataMapper::Query::Path.new(@path.relationships, @path.property)
178
+
179
+ @return = @path == @other
180
+ end
181
+
182
+ it 'should return true' do
183
+ @return.should be_true
184
+ end
185
+ end
186
+ end
187
+
188
+ it { @path.should respond_to(:eql?) }
189
+
190
+ describe '#eql?' do
191
+ describe 'when other Query::Path is the same' do
192
+ before do
193
+ @other = @path
194
+
195
+ @return = @path.eql?(@other)
196
+ end
197
+
198
+ it 'should return true' do
199
+ @return.should be_true
200
+ end
201
+ end
202
+
203
+ describe 'when other Object is not an instance of Query::Path' do
204
+ before do
205
+ class MyQueryPath < DataMapper::Query::Path; end
206
+
207
+ @other = MyQueryPath.new(@path.relationships, @path.property)
208
+
209
+ @return = @path.eql?(@other)
210
+ end
211
+
212
+ it 'should return false' do
213
+ @return.should be_false
214
+ end
215
+ end
216
+
217
+ describe 'when other Query::Path has different relationships' do
218
+ before do
219
+ @other = DataMapper::Query::Path.new([ Article.relationships[:author] ])
220
+
221
+ @return = @path.eql?(@other)
222
+ end
223
+
224
+ it 'should return false' do
225
+ @return.should be_false
226
+ end
227
+ end
228
+
229
+ describe 'when other Query::Path has different properties' do
230
+ before do
231
+ @other = DataMapper::Query::Path.new(@path.relationships, :title)
232
+
233
+ @return = @path.eql?(@other)
234
+ end
235
+
236
+ it 'should return false' do
237
+ @return.should be_false
238
+ end
239
+ end
240
+
241
+ describe 'when other Query::Path has the same relationship and property' do
242
+ before do
243
+ @other = DataMapper::Query::Path.new(@path.relationships, @path.property)
244
+
245
+ @return = @path.eql?(@other)
246
+ end
247
+
248
+ it 'should return true' do
249
+ @return.should be_true
250
+ end
251
+ end
252
+ end
253
+
254
+ it { @path.should respond_to(:model) }
255
+
256
+ describe '#model' do
257
+ it 'should return a Model' do
258
+ @path.model.should be_kind_of(DataMapper::Model)
259
+ end
260
+
261
+ it 'should return expected value' do
262
+ @path.model.should eql(Article)
263
+ end
264
+ end
265
+
266
+ it { @path.should respond_to(:property) }
267
+
268
+ describe '#property' do
269
+ describe 'when no property is defined' do
270
+ it 'should return nil' do
271
+ @path.property.should be_nil
272
+ end
273
+ end
274
+
275
+ describe 'when a property is defined' do
276
+ before do
277
+ @path = @path.class.new(@path.relationships, @property.name)
278
+ end
279
+
280
+ it 'should return a Property' do
281
+ @path.property.should be_kind_of(DataMapper::Property)
282
+ end
283
+
284
+ it 'should return expected value' do
285
+ @path.property.should eql(@property)
286
+ end
287
+ end
288
+ end
289
+
290
+ it { @path.should respond_to(:relationships) }
291
+
292
+ describe '#relationships' do
293
+ it 'should return an Array' do
294
+ @path.relationships.should be_kind_of(Array)
295
+ end
296
+
297
+ it 'should return expected value' do
298
+ @path.relationships.should eql(@relationships)
299
+ end
300
+ end
301
+
302
+ it { @path.should respond_to(:respond_to?) }
303
+
304
+ describe '#respond_to?' do
305
+ describe 'when supplied a method name provided by the parent class' do
306
+ before do
307
+ @return = @path.respond_to?(:class)
308
+ end
309
+
310
+ it 'should return true' do
311
+ @return.should be_true
312
+ end
313
+ end
314
+
315
+ describe 'when supplied a method name provided by the property' do
316
+ before do
317
+ @path = @path.class.new(@path.relationships, @property.name)
318
+
319
+ @return = @path.respond_to?(:instance_variable_name)
320
+ end
321
+
322
+ it 'should return true' do
323
+ @return.should be_true
324
+ end
325
+ end
326
+
327
+ describe 'when supplied a method name referring to a relationship' do
328
+ before do
329
+ @return = @path.respond_to?(:author)
330
+ end
331
+
332
+ it 'should return true' do
333
+ @return.should be_true
334
+ end
335
+ end
336
+
337
+ describe 'when supplied a method name referring to a property' do
338
+ before do
339
+ @return = @path.respond_to?(:title)
340
+ end
341
+
342
+ it 'should return true' do
343
+ @return.should be_true
344
+ end
345
+ end
346
+
347
+ describe 'when supplied an unknown method name' do
348
+ before do
349
+ @return = @path.respond_to?(:unknown)
350
+ end
351
+
352
+ it 'should return false' do
353
+ @return.should be_false
354
+ end
355
+ end
356
+ end
357
+
358
+ it { @path.should respond_to(:repository_name) }
359
+
360
+ describe '#repository_name' do
361
+ it 'should return a Symbol' do
362
+ @path.repository_name.should be_kind_of(Symbol)
363
+ end
364
+
365
+ it 'should return expected value' do
366
+ @path.repository_name.should eql(:default)
367
+ end
368
+ end
369
+
370
+ describe '#method_missing' do
371
+ describe 'when supplied a method name provided by the parent class' do
372
+ before do
373
+ @return = @path.class
374
+ end
375
+
376
+ it 'should return the expected value' do
377
+ @return.should eql(DataMapper::Query::Path)
378
+ end
379
+ end
380
+
381
+ describe 'when supplied a method name provided by the property' do
382
+ before do
383
+ @path = @path.class.new(@path.relationships, @property.name)
384
+
385
+ @return = @path.instance_variable_name
386
+ end
387
+
388
+ it 'should return the expected value' do
389
+ @return.should eql('@title')
390
+ end
391
+ end
392
+
393
+ describe 'when supplied a method name referring to a relationship' do
394
+ before do
395
+ @return = @path.author
396
+ end
397
+
398
+ it 'should return a Query::Path' do
399
+ @return.should be_kind_of(DataMapper::Query::Path)
400
+ end
401
+
402
+ it 'should return the expected value' do
403
+ @return.should eql(DataMapper::Query::Path.new([ @relationship, Article.relationships[:author] ]))
404
+ end
405
+ end
406
+
407
+ describe 'when supplied a method name referring to a property' do
408
+ before do
409
+ @return = @path.title
410
+ end
411
+
412
+ it 'should return a Query::Path' do
413
+ @return.should be_kind_of(DataMapper::Query::Path)
414
+ end
415
+
416
+ it 'should return the expected value' do
417
+ @return.should eql(DataMapper::Query::Path.new(@relationships, :title))
418
+ end
419
+ end
420
+
421
+ describe 'when supplied an unknown method name' do
422
+ it 'should raise an error' do
423
+ lambda { @path.unknown }.should raise_error(NoMethodError, "undefined property or relationship 'unknown' on Article")
424
+ end
425
+ end
426
+ end
427
+
428
+ ((DataMapper::Query::Conditions::Comparison.slugs | [ :not ]) - [ :eql, :in ]).each do |slug|
429
+ describe "##{slug}" do
430
+ before do
431
+ @return = @path.send(slug)
432
+ end
433
+
434
+ it 'should return a Query::Operator' do
435
+ @return.should be_kind_of(DataMapper::Query::Operator)
436
+ end
437
+
438
+ it 'should return expected value' do
439
+ @return.should eql(DataMapper::Query::Operator.new(@path, slug))
440
+ end
441
+ end
442
+ end
443
+ end
@@ -0,0 +1,2626 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ require 'ostruct'
4
+
5
+ # TODO: make some of specs for Query.new shared. the assertions and
6
+ # normalizations should happen for Query#update, Query#relative and
7
+ # Query#merge and should probably be in shared specs
8
+
9
+ # class methods
10
+ describe DataMapper::Query do
11
+ before :all do
12
+ class ::Password < DataMapper::Type
13
+ primitive String
14
+ length 40
15
+ end
16
+
17
+ class ::User
18
+ include DataMapper::Resource
19
+
20
+ property :name, String, :key => true
21
+ property :password, Password
22
+ property :balance, BigDecimal
23
+
24
+ belongs_to :referrer, self, :nullable => true
25
+ has n, :referrals, self, :inverse => :referrer
26
+ end
27
+
28
+ @repository = DataMapper::Repository.new(:default)
29
+ @model = User
30
+
31
+ @fields = [ :name ].freeze
32
+ @links = [ :referrer ].freeze
33
+ @conditions = { :name => 'Dan Kubb' }
34
+ @offset = 0
35
+ @limit = 1
36
+ @order = [ :name ].freeze
37
+ @unique = false
38
+ @add_reversed = false
39
+ @reload = false
40
+
41
+ @options = {
42
+ :fields => @fields,
43
+ :links => @links,
44
+ :conditions => @conditions,
45
+ :offset => @offset,
46
+ :limit => @limit,
47
+ :order => @order,
48
+ :unique => @unique,
49
+ :add_reversed => @add_reversed,
50
+ :reload => @reload,
51
+ }
52
+ end
53
+
54
+ it { DataMapper::Query.should respond_to(:new) }
55
+
56
+ describe '.new' do
57
+ describe 'with a repository' do
58
+ describe 'that is valid' do
59
+ before :all do
60
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
61
+ end
62
+
63
+ it { @return.should be_kind_of(DataMapper::Query) }
64
+
65
+ it 'should set the repository' do
66
+ @return.repository.should == @repository
67
+ end
68
+ end
69
+
70
+ describe 'that is invalid' do
71
+ it 'should raise an exception' do
72
+ lambda {
73
+ DataMapper::Query.new('invalid', @model, @options)
74
+ }.should raise_error(ArgumentError, '+repository+ should be DataMapper::Repository, but was String')
75
+ end
76
+ end
77
+ end
78
+
79
+ describe 'with a model' do
80
+ describe 'that is valid' do
81
+ before :all do
82
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
83
+ end
84
+
85
+ it { @return.should be_kind_of(DataMapper::Query) }
86
+
87
+ it 'should set the model' do
88
+ @return.model.should == @model
89
+ end
90
+ end
91
+
92
+ describe 'that is invalid' do
93
+ it 'should raise an exception' do
94
+ lambda {
95
+ DataMapper::Query.new(@repository, 'invalid', @options)
96
+ }.should raise_error(ArgumentError, '+model+ should be DataMapper::Model, but was String')
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'with a fields option' do
102
+ describe 'that is an Array containing a Symbol' do
103
+ before :all do
104
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
105
+ end
106
+
107
+ it { @return.should be_kind_of(DataMapper::Query) }
108
+
109
+ it 'should set the fields' do
110
+ @return.fields.should == @model.properties.values_at(*@fields)
111
+ end
112
+ end
113
+
114
+ describe 'that is an Array containing a String' do
115
+ before :all do
116
+ @options[:fields] = [ 'name' ]
117
+
118
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
119
+ end
120
+
121
+ it { @return.should be_kind_of(DataMapper::Query) }
122
+
123
+ it 'should set the fields' do
124
+ @return.fields.should == @model.properties.values_at('name')
125
+ end
126
+ end
127
+
128
+ describe 'that is an Array containing a Property' do
129
+ before :all do
130
+ @options[:fields] = @model.properties.values_at(:name)
131
+
132
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
133
+ end
134
+
135
+ it { @return.should be_kind_of(DataMapper::Query) }
136
+
137
+ it 'should set the fields' do
138
+ @return.fields.should == @model.properties.values_at(:name)
139
+ end
140
+ end
141
+
142
+ describe 'that is an Array containing a Property from an ancestor' do
143
+ before :all do
144
+ class ::Contact < User; end
145
+
146
+ @options[:fields] = User.properties.values_at(:name)
147
+
148
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
149
+ end
150
+
151
+ it { @return.should be_kind_of(DataMapper::Query) }
152
+
153
+ it 'should set the fields' do
154
+ @return.fields.should == User.properties.values_at(:name)
155
+ end
156
+ end
157
+
158
+ describe 'that is missing' do
159
+ before :all do
160
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:fields).freeze)
161
+ end
162
+
163
+ it { @return.should be_kind_of(DataMapper::Query) }
164
+
165
+ it 'should set fields to the model default properties' do
166
+ @return.fields.should == @model.properties.defaults
167
+ end
168
+ end
169
+
170
+ describe 'that is invalid' do
171
+ it 'should raise an exception' do
172
+ lambda {
173
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => :name))
174
+ }.should raise_error(ArgumentError, '+options[:fields]+ should be Array, but was Symbol')
175
+ end
176
+ end
177
+
178
+ describe 'that is an empty Array and the unique option is false' do
179
+ it 'should raise an exception' do
180
+ lambda {
181
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [], :unique => false))
182
+ }.should raise_error(ArgumentError, '+options[:fields]+ should not be empty if +options[:unique]+ is false')
183
+ end
184
+ end
185
+
186
+ describe 'that is an Array containing an unknown Symbol' do
187
+ it 'should raise an exception' do
188
+ lambda {
189
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ :unknown ]))
190
+ }.should raise_error(ArgumentError, "+options[:fields]+ entry :unknown does not map to a property in #{@model}")
191
+ end
192
+ end
193
+
194
+ describe 'that is an Array containing an unknown String' do
195
+ it 'should raise an exception' do
196
+ lambda {
197
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ 'unknown' ]))
198
+ }.should raise_error(ArgumentError, "+options[:fields]+ entry \"unknown\" does not map to a property in #{@model}")
199
+ end
200
+ end
201
+
202
+ describe 'that is an Array containing an invalid object' do
203
+ it 'should raise an exception' do
204
+ lambda {
205
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ 1 ]))
206
+ }.should raise_error(ArgumentError, '+options[:fields]+ entry 1 of an unsupported object Fixnum')
207
+ end
208
+ end
209
+
210
+ describe 'that is an Array containing an unknown Property' do
211
+ it 'should raise an exception' do
212
+ lambda {
213
+ DataMapper::Query.new(@repository, @model, @options.update(:fields => [ DataMapper::Property.new(@model, :unknown, String) ]))
214
+ }.should raise_error(ArgumentError, "+options[:field]+ entry :unknown does not map to a property in #{@model}")
215
+ end
216
+ end
217
+ end
218
+
219
+ describe 'with a links option' do
220
+ describe 'that is an Array containing a Symbol' do
221
+ before :all do
222
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
223
+ end
224
+
225
+ it { @return.should be_kind_of(DataMapper::Query) }
226
+
227
+ it 'should set the links' do
228
+ @return.links.should == @model.relationships.values_at(*@links)
229
+ end
230
+ end
231
+
232
+ describe 'that is an Array containing a String' do
233
+ before :all do
234
+ @options[:links] = [ 'referrer' ]
235
+
236
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
237
+ end
238
+
239
+ it { @return.should be_kind_of(DataMapper::Query) }
240
+
241
+ it 'should set the links' do
242
+ @return.links.should == @model.relationships.values_at('referrer')
243
+ end
244
+ end
245
+
246
+ describe 'that is an Array containing a Relationship' do
247
+ before :all do
248
+ @options[:links] = @model.relationships.values_at(:referrer)
249
+
250
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
251
+ end
252
+
253
+ it { @return.should be_kind_of(DataMapper::Query) }
254
+
255
+ it 'should set the links' do
256
+ @return.links.should == @model.relationships.values_at(:referrer)
257
+ end
258
+ end
259
+
260
+ describe 'that is missing' do
261
+ before :all do
262
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:links).freeze)
263
+ end
264
+
265
+ it { @return.should be_kind_of(DataMapper::Query) }
266
+
267
+ it 'should set links to an empty Array' do
268
+ @return.links.should == []
269
+ end
270
+ end
271
+
272
+ describe 'that is invalid' do
273
+ it 'should raise an exception' do
274
+ lambda {
275
+ DataMapper::Query.new(@repository, @model, @options.update(:links => :referral))
276
+ }.should raise_error(ArgumentError, '+options[:links]+ should be Array, but was Symbol')
277
+ end
278
+ end
279
+
280
+ describe 'that is an empty Array' do
281
+ it 'should raise an exception' do
282
+ lambda {
283
+ DataMapper::Query.new(@repository, @model, @options.update(:links => []))
284
+ }.should raise_error(ArgumentError, '+options[:links]+ should not be empty')
285
+ end
286
+ end
287
+
288
+ describe 'that is an Array containing an unknown Symbol' do
289
+ it 'should raise an exception' do
290
+ lambda {
291
+ DataMapper::Query.new(@repository, @model, @options.update(:links => [ :unknown ]))
292
+ }.should raise_error(ArgumentError, "+options[:links]+ entry :unknown does not map to a relationship in #{@model}")
293
+ end
294
+ end
295
+
296
+ describe 'that is an Array containing an unknown String' do
297
+ it 'should raise an exception' do
298
+ lambda {
299
+ DataMapper::Query.new(@repository, @model, @options.update(:links => [ 'unknown' ]))
300
+ }.should raise_error(ArgumentError, "+options[:links]+ entry \"unknown\" does not map to a relationship in #{@model}")
301
+ end
302
+ end
303
+
304
+ describe 'that is an Array containing an invalid object' do
305
+ it 'should raise an exception' do
306
+ lambda {
307
+ DataMapper::Query.new(@repository, @model, @options.update(:links => [ 1 ]))
308
+ }.should raise_error(ArgumentError, '+options[:links]+ entry 1 of an unsupported object Fixnum')
309
+ end
310
+ end
311
+ end
312
+
313
+ describe 'with a conditions option' do
314
+ describe 'that is a valid Hash' do
315
+ describe 'with the Property key' do
316
+ before :all do
317
+ @options[:conditions] = { @model.properties[:name] => 'Dan Kubb' }
318
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
319
+ end
320
+
321
+ it { @return.should be_kind_of(DataMapper::Query) }
322
+
323
+ it 'should set the conditions' do
324
+ @return.conditions.should ==
325
+ DataMapper::Query::Conditions::Operation.new(
326
+ :and,
327
+ DataMapper::Query::Conditions::Comparison.new(
328
+ :eql,
329
+ @model.properties[:name],
330
+ 'Dan Kubb'
331
+ )
332
+ )
333
+ end
334
+
335
+ it 'should be valid' do
336
+ @return.should be_valid
337
+ end
338
+ end
339
+
340
+ describe 'with the Symbol key mapping to a Property' do
341
+ before :all do
342
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
343
+ end
344
+
345
+ it { @return.should be_kind_of(DataMapper::Query) }
346
+
347
+ it 'should set the conditions' do
348
+ @return.conditions.should ==
349
+ DataMapper::Query::Conditions::Operation.new(
350
+ :and,
351
+ DataMapper::Query::Conditions::Comparison.new(
352
+ :eql,
353
+ @model.properties[:name],
354
+ 'Dan Kubb'
355
+ )
356
+ )
357
+ end
358
+
359
+ it 'should be valid' do
360
+ @return.should be_valid
361
+ end
362
+ end
363
+
364
+ describe 'with the String key mapping to a Property' do
365
+ before :all do
366
+ @options[:conditions] = { 'name' => 'Dan Kubb' }
367
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
368
+ end
369
+
370
+ it { @return.should be_kind_of(DataMapper::Query) }
371
+
372
+ it 'should set the conditions' do
373
+ @return.conditions.should ==
374
+ DataMapper::Query::Conditions::Operation.new(
375
+ :and,
376
+ DataMapper::Query::Conditions::Comparison.new(
377
+ :eql,
378
+ @model.properties[:name],
379
+ 'Dan Kubb'
380
+ )
381
+ )
382
+ end
383
+
384
+ it 'should be valid' do
385
+ @return.should be_valid
386
+ end
387
+ end
388
+
389
+ supported_by :all do
390
+ describe 'with the Symbol key mapping to a Relationship' do
391
+ before :all do
392
+ @user = @model.create(:name => 'Dan Kubb')
393
+
394
+ @options[:conditions] = { :referrer => @user }
395
+
396
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
397
+ end
398
+
399
+ it { @return.should be_kind_of(DataMapper::Query) }
400
+
401
+ it 'should set the conditions' do
402
+ @return.conditions.should ==
403
+ DataMapper::Query::Conditions::Operation.new(
404
+ :and,
405
+ DataMapper::Query::Conditions::Comparison.new(
406
+ :eql,
407
+ @model.relationships[:referrer],
408
+ @user
409
+ )
410
+ )
411
+ end
412
+
413
+ it 'should be valid' do
414
+ @return.should be_valid
415
+ end
416
+ end
417
+
418
+ describe 'with the String key mapping to a Relationship' do
419
+ before :all do
420
+ @user = @model.create(:name => 'Dan Kubb')
421
+
422
+ @options[:conditions] = { 'referrer' => @user }
423
+
424
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
425
+ end
426
+
427
+ it { @return.should be_kind_of(DataMapper::Query) }
428
+
429
+ it 'should set the conditions' do
430
+ @return.conditions.should ==
431
+ DataMapper::Query::Conditions::Operation.new(
432
+ :and,
433
+ DataMapper::Query::Conditions::Comparison.new(
434
+ :eql,
435
+ @model.relationships['referrer'],
436
+ @user
437
+ )
438
+ )
439
+ end
440
+
441
+ it 'should be valid' do
442
+ @return.should be_valid
443
+ end
444
+ end
445
+
446
+ describe 'with the Symbol key mapping to a Relationship and a nil value' do
447
+ before :all do
448
+ @options[:conditions] = { :referrer => nil }
449
+
450
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
451
+ end
452
+
453
+ it { @return.should be_kind_of(DataMapper::Query) }
454
+
455
+ it 'should set the conditions' do
456
+ @return.conditions.should ==
457
+ DataMapper::Query::Conditions::Operation.new(
458
+ :and,
459
+ DataMapper::Query::Conditions::Comparison.new(
460
+ :eql,
461
+ @model.relationships[:referrer],
462
+ nil
463
+ )
464
+ )
465
+ end
466
+
467
+ it 'should be valid' do
468
+ @return.should be_valid
469
+ end
470
+ end
471
+
472
+ describe 'with the Symbol key mapping to a Relationship and an empty Array' do
473
+ before :all do
474
+ @options[:conditions] = { :referrer => [] }
475
+
476
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
477
+ end
478
+
479
+ it { @return.should be_kind_of(DataMapper::Query) }
480
+
481
+ it 'should set the conditions' do
482
+ @return.conditions.should ==
483
+ DataMapper::Query::Conditions::Operation.new(
484
+ :and,
485
+ DataMapper::Query::Conditions::Comparison.new(
486
+ :in,
487
+ @model.relationships[:referrer],
488
+ []
489
+ )
490
+ )
491
+ end
492
+
493
+ it 'should be invalid' do
494
+ @return.should_not be_valid
495
+ end
496
+ end
497
+ end
498
+
499
+ describe 'with the Query::Operator key' do
500
+ before :all do
501
+ @options[:conditions] = { :name.gte => 'Dan Kubb' }
502
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
503
+ end
504
+
505
+ it { @return.should be_kind_of(DataMapper::Query) }
506
+
507
+ it 'should set the conditions' do
508
+ @return.conditions.should ==
509
+ DataMapper::Query::Conditions::Operation.new(
510
+ :and,
511
+ DataMapper::Query::Conditions::Comparison.new(
512
+ :gte,
513
+ @model.properties[:name],
514
+ 'Dan Kubb'
515
+ )
516
+ )
517
+ end
518
+
519
+ it 'should be valid' do
520
+ @return.should be_valid
521
+ end
522
+ end
523
+
524
+ describe 'with the Query::Path key' do
525
+ before :all do
526
+ @options[:conditions] = { @model.referrer.name => 'Dan Kubb' }
527
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
528
+ end
529
+
530
+ it { @return.should be_kind_of(DataMapper::Query) }
531
+
532
+ it 'should set the conditions' do
533
+ @return.conditions.should ==
534
+ DataMapper::Query::Conditions::Operation.new(
535
+ :and,
536
+ DataMapper::Query::Conditions::Comparison.new(
537
+ :eql,
538
+ @model.referrer.name,
539
+ 'Dan Kubb'
540
+ )
541
+ )
542
+ end
543
+
544
+ it 'should set the links' do
545
+ @return.links.should == [ @model.relationships[:referrals], @model.relationships[:referrer] ]
546
+ end
547
+
548
+ it 'should be valid' do
549
+ @return.should be_valid
550
+ end
551
+ end
552
+
553
+ describe 'with the String key mapping to a Query::Path' do
554
+ before :all do
555
+ @options[:conditions] = { 'referrer.name' => 'Dan Kubb' }
556
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
557
+ end
558
+
559
+ it { @return.should be_kind_of(DataMapper::Query) }
560
+
561
+ it 'should set the conditions' do
562
+ @return.conditions.should ==
563
+ DataMapper::Query::Conditions::Operation.new(
564
+ :and,
565
+ DataMapper::Query::Conditions::Comparison.new(
566
+ :eql,
567
+ @model.referrer.name,
568
+ 'Dan Kubb'
569
+ )
570
+ )
571
+ end
572
+
573
+ it 'should set the links' do
574
+ @return.links.should == [ @model.relationships[:referrals], @model.relationships[:referrer] ]
575
+ end
576
+
577
+ it 'should be valid' do
578
+ @return.should be_valid
579
+ end
580
+ end
581
+
582
+ describe 'with a Proc value' do
583
+ before :all do
584
+ @options[:conditions] = { :name => lambda { 'Dan Kubb' } }
585
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
586
+ end
587
+
588
+ it { @return.should be_kind_of(DataMapper::Query) }
589
+
590
+ it 'should set the conditions' do
591
+ @return.conditions.should ==
592
+ DataMapper::Query::Conditions::Operation.new(
593
+ :and,
594
+ DataMapper::Query::Conditions::Comparison.new(
595
+ :eql,
596
+ @model.properties[:name],
597
+ 'Dan Kubb'
598
+ )
599
+ )
600
+ end
601
+
602
+ it 'should be valid' do
603
+ @return.should be_valid
604
+ end
605
+ end
606
+
607
+ describe 'with an Array with 1 entry' do
608
+ before :all do
609
+ @options[:conditions] = { :name => [ 'Dan Kubb' ] }
610
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
611
+ end
612
+
613
+ it { @return.should be_kind_of(DataMapper::Query) }
614
+
615
+ it 'should set the conditions' do
616
+ pending do
617
+ @return.conditions.should ==
618
+ DataMapper::Query::Conditions::Operation.new(
619
+ :and,
620
+ DataMapper::Query::Conditions::Comparison.new(
621
+ :eql,
622
+ @model.properties[:name],
623
+ 'Dan Kubb'
624
+ )
625
+ )
626
+ end
627
+ end
628
+
629
+ it 'should be valid' do
630
+ @return.should be_valid
631
+ end
632
+ end
633
+
634
+ describe 'with an Array with duplicate entries' do
635
+ before :all do
636
+ @options[:conditions] = { :name => [ 'John Doe', 'Dan Kubb', 'John Doe' ] }
637
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
638
+ end
639
+
640
+ it { @return.should be_kind_of(DataMapper::Query) }
641
+
642
+ it 'should set the conditions' do
643
+ @return.conditions.should ==
644
+ DataMapper::Query::Conditions::Operation.new(
645
+ :and,
646
+ DataMapper::Query::Conditions::Comparison.new(
647
+ :in,
648
+ @model.properties[:name],
649
+ [ 'John Doe', 'Dan Kubb' ]
650
+ )
651
+ )
652
+ end
653
+
654
+ it 'should be valid' do
655
+ @return.should be_valid
656
+ end
657
+ end
658
+
659
+ describe 'with a custom Property' do
660
+ before :all do
661
+ @options[:conditions] = { :password => 'password' }
662
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
663
+ end
664
+
665
+ it { @return.should be_kind_of(DataMapper::Query) }
666
+
667
+ it 'should set the conditions' do
668
+ @return.conditions.should ==
669
+ DataMapper::Query::Conditions::Operation.new(
670
+ :and,
671
+ DataMapper::Query::Conditions::Comparison.new(
672
+ :eql,
673
+ @model.properties[:password],
674
+ 'password'
675
+ )
676
+ )
677
+ end
678
+
679
+ it 'should be valid' do
680
+ @return.should be_valid
681
+ end
682
+ end
683
+
684
+ describe 'with a Symbol for a String property' do
685
+ before :all do
686
+ @options[:conditions] = { :name => 'Dan Kubb'.to_sym }
687
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
688
+ end
689
+
690
+ it { @return.should be_kind_of(DataMapper::Query) }
691
+
692
+ it 'should set the conditions' do
693
+ @return.conditions.should ==
694
+ DataMapper::Query::Conditions::Operation.new(
695
+ :and,
696
+ DataMapper::Query::Conditions::Comparison.new(
697
+ :eql,
698
+ @model.properties[:name],
699
+ 'Dan Kubb' # typecast value
700
+ )
701
+ )
702
+ end
703
+
704
+ it 'should be valid' do
705
+ @return.should be_valid
706
+ end
707
+ end
708
+
709
+ describe 'with a Float for a BigDecimal property' do
710
+ before :all do
711
+ @options[:conditions] = { :balance => 50.5 }
712
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
713
+ end
714
+
715
+ it { @return.should be_kind_of(DataMapper::Query) }
716
+
717
+ it 'should set the conditions' do
718
+ @return.conditions.should ==
719
+ DataMapper::Query::Conditions::Operation.new(
720
+ :and,
721
+ DataMapper::Query::Conditions::Comparison.new(
722
+ :eql,
723
+ @model.properties[:balance],
724
+ BigDecimal('50.5') # typecast value
725
+ )
726
+ )
727
+ end
728
+
729
+ it 'should be valid' do
730
+ @return.should be_valid
731
+ end
732
+ end
733
+ end
734
+
735
+ describe 'that is a valid Array' do
736
+ before :all do
737
+ @options[:conditions] = [ 'name = ?', 'Dan Kubb' ]
738
+
739
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
740
+ end
741
+
742
+ it { @return.should be_kind_of(DataMapper::Query) }
743
+
744
+ it 'should set the conditions' do
745
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
746
+ end
747
+
748
+ it 'should be valid' do
749
+ @return.should be_valid
750
+ end
751
+ end
752
+
753
+ describe 'that is missing' do
754
+ before :all do
755
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:conditions).freeze)
756
+ end
757
+
758
+ it { @return.should be_kind_of(DataMapper::Query) }
759
+
760
+ it 'should set conditions to an empty And operation' do
761
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and)
762
+ end
763
+
764
+ it 'should be valid' do
765
+ @return.should be_valid
766
+ end
767
+ end
768
+
769
+ describe 'that is invalid' do
770
+ it 'should raise an exception' do
771
+ lambda {
772
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => 'invalid'))
773
+ }.should raise_error(ArgumentError, '+options[:conditions]+ should be DataMapper::Query::Conditions::AbstractOperation or DataMapper::Query::Conditions::AbstractComparison or Hash or Array, but was String')
774
+ end
775
+ end
776
+
777
+ describe 'that is an empty Array' do
778
+ it 'should raise an exception' do
779
+ lambda {
780
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => []))
781
+ }.should raise_error(ArgumentError, '+options[:conditions]+ should not be empty')
782
+ end
783
+ end
784
+
785
+ describe 'that is an Array with a blank statement' do
786
+ it 'should raise an exception' do
787
+ lambda {
788
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => [ ' ' ]))
789
+ }.should raise_error(ArgumentError, '+options[:conditions]+ should have a statement for the first entry')
790
+ end
791
+ end
792
+
793
+ describe 'that is a Hash with a Symbol key that is not for a Property in the model' do
794
+ it 'should raise an exception' do
795
+ lambda {
796
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { :unknown => 1 }))
797
+ }.should raise_error(ArgumentError, "condition :unknown does not map to a property or relationship in #{@model}")
798
+ end
799
+ end
800
+
801
+ describe 'that is a Hash with a String key that is not for a Property in the model' do
802
+ it 'should raise an exception' do
803
+ lambda {
804
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { 'unknown' => 1 }))
805
+ }.should raise_error(ArgumentError, "condition \"unknown\" does not map to a property or relationship in #{@model}")
806
+ end
807
+ end
808
+
809
+ describe 'that is a Hash with a Query::Operator key that is not for a Property in the model' do
810
+ it 'should raise an exception' do
811
+ lambda {
812
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { :unknown.asc => 1 }))
813
+ }.should raise_error(ArgumentError, 'condition #<DataMapper::Query::Operator @target=:unknown @operator=:asc> used an invalid operator asc')
814
+ end
815
+ end
816
+
817
+ describe 'that is a Hash with a not operator that has an empty Array' do
818
+ it 'should raise an exception' do
819
+ lambda {
820
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { :name.not => [] }))
821
+ }.should raise_error(ArgumentError, 'Cannot use \'not\' operator with a bind value that is an empty Array for #<DataMapper::Query::Operator @target=:name @operator=:not>')
822
+ end
823
+ end
824
+
825
+ describe 'that is a Hash with a key of a type that is not permitted' do
826
+ it 'should raise an exception' do
827
+ lambda {
828
+ DataMapper::Query.new(@repository, @model, @options.update(:conditions => { 1 => 1 }))
829
+ }.should raise_error(ArgumentError, 'condition 1 of an unsupported object Fixnum')
830
+ end
831
+ end
832
+ end
833
+
834
+ describe 'with an offset option' do
835
+ describe 'that is valid' do
836
+ before :all do
837
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
838
+ end
839
+
840
+ it { @return.should be_kind_of(DataMapper::Query) }
841
+
842
+ it 'should set the offset' do
843
+ @return.offset.should == @offset
844
+ end
845
+ end
846
+
847
+ describe 'that is missing' do
848
+ before :all do
849
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:offset).freeze)
850
+ end
851
+
852
+ it { @return.should be_kind_of(DataMapper::Query) }
853
+
854
+ it 'should set offset to 0' do
855
+ @return.offset.should == 0
856
+ end
857
+ end
858
+
859
+ describe 'that is invalid' do
860
+ it 'should raise an exception' do
861
+ lambda {
862
+ DataMapper::Query.new(@repository, @model, @options.update(:offset => '0'))
863
+ }.should raise_error(ArgumentError, '+options[:offset]+ should be Integer, but was String')
864
+ end
865
+ end
866
+
867
+ describe 'that is less than 0' do
868
+ it 'should raise an exception' do
869
+ lambda {
870
+ DataMapper::Query.new(@repository, @model, @options.update(:offset => -1))
871
+ }.should raise_error(ArgumentError, '+options[:offset]+ must be greater than or equal to 0, but was -1')
872
+ end
873
+ end
874
+
875
+ describe 'that is greater than 0 and a nil limit' do
876
+ it 'should raise an exception' do
877
+ lambda {
878
+ DataMapper::Query.new(@repository, @model, @options.except(:limit).update(:offset => 1))
879
+ }.should raise_error(ArgumentError, '+options[:offset]+ cannot be greater than 0 if limit is not specified')
880
+ end
881
+ end
882
+ end
883
+
884
+ describe 'with a limit option' do
885
+ describe 'that is valid' do
886
+ before :all do
887
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
888
+ end
889
+
890
+ it { @return.should be_kind_of(DataMapper::Query) }
891
+
892
+ it 'should set the limit' do
893
+ @return.limit.should == @limit
894
+ end
895
+ end
896
+
897
+ describe 'that is missing' do
898
+ before :all do
899
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:limit).freeze)
900
+ end
901
+
902
+ it { @return.should be_kind_of(DataMapper::Query) }
903
+
904
+ it 'should set limit to nil' do
905
+ @return.limit.should be_nil
906
+ end
907
+ end
908
+
909
+ describe 'that is invalid' do
910
+ it 'should raise an exception' do
911
+ lambda {
912
+ DataMapper::Query.new(@repository, @model, @options.update(:limit => '1'))
913
+ }.should raise_error(ArgumentError, '+options[:limit]+ should be Integer, but was String')
914
+ end
915
+ end
916
+
917
+ describe 'that is less than 0' do
918
+ it 'should raise an exception' do
919
+ lambda {
920
+ DataMapper::Query.new(@repository, @model, @options.update(:limit => -1))
921
+ }.should raise_error(ArgumentError, '+options[:limit]+ must be greater than or equal to 0, but was -1')
922
+ end
923
+ end
924
+ end
925
+
926
+ describe 'with an order option' do
927
+ describe 'that is an Array containing a Symbol' do
928
+ before :all do
929
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
930
+ end
931
+
932
+ it { @return.should be_kind_of(DataMapper::Query) }
933
+
934
+ it 'should set the order' do
935
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
936
+ end
937
+ end
938
+
939
+ describe 'that is an Array containing a String' do
940
+ before :all do
941
+ @options[:order] = [ 'name' ]
942
+
943
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
944
+ end
945
+
946
+ it { @return.should be_kind_of(DataMapper::Query) }
947
+
948
+ it 'should set the order' do
949
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
950
+ end
951
+ end
952
+
953
+ describe 'that is an Array containing a Property' do
954
+ before :all do
955
+ @options[:order] = @model.properties.values_at(:name)
956
+
957
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
958
+ end
959
+
960
+ it { @return.should be_kind_of(DataMapper::Query) }
961
+
962
+ it 'should set the order' do
963
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
964
+ end
965
+ end
966
+
967
+ describe 'that is an Array containing a Property from an ancestor' do
968
+ before :all do
969
+ class ::Contact < User; end
970
+
971
+ @options[:order] = User.properties.values_at(:name)
972
+
973
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
974
+ end
975
+
976
+ it { @return.should be_kind_of(DataMapper::Query) }
977
+
978
+ it 'should set the order' do
979
+ @return.order.should == [ DataMapper::Query::Direction.new(User.properties[:name]) ]
980
+ end
981
+ end
982
+
983
+ describe 'that is an Array containing an Operator' do
984
+ before :all do
985
+ @options[:order] = [ :name.asc ]
986
+
987
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
988
+ end
989
+
990
+ it { @return.should be_kind_of(DataMapper::Query) }
991
+
992
+ it 'should set the order' do
993
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
994
+ end
995
+ end
996
+
997
+ describe 'that is an Array containing an Query::Direction' do
998
+ before :all do
999
+ @options[:order] = [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
1000
+
1001
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1002
+ end
1003
+
1004
+ it { @return.should be_kind_of(DataMapper::Query) }
1005
+
1006
+ it 'should set the order' do
1007
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :asc) ]
1008
+ end
1009
+ end
1010
+
1011
+ describe 'that is an Array containing an Query::Direction with a Property from an ancestor' do
1012
+ before :all do
1013
+ class ::Contact < User; end
1014
+
1015
+ @options[:order] = [ DataMapper::Query::Direction.new(User.properties[:name], :asc) ]
1016
+
1017
+ @return = DataMapper::Query.new(@repository, Contact, @options.freeze)
1018
+ end
1019
+
1020
+ it { @return.should be_kind_of(DataMapper::Query) }
1021
+
1022
+ it 'should set the order' do
1023
+ @return.order.should == [ DataMapper::Query::Direction.new(User.properties[:name], :asc) ]
1024
+ end
1025
+ end
1026
+
1027
+ describe 'that is missing' do
1028
+ before :all do
1029
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:order).freeze)
1030
+ end
1031
+
1032
+ it { @return.should be_kind_of(DataMapper::Query) }
1033
+
1034
+ it 'should set order to the model default order' do
1035
+ @return.order.should == @model.default_order(@repository.name)
1036
+ end
1037
+ end
1038
+
1039
+ describe 'that is invalid' do
1040
+ it 'should raise an exception' do
1041
+ lambda {
1042
+ DataMapper::Query.new(@repository, @model, @options.update(:order => :name))
1043
+ }.should raise_error(ArgumentError, '+options[:order]+ should be Array, but was Symbol')
1044
+ end
1045
+ end
1046
+
1047
+ describe 'that is an empty Array and the fields option contains a non-operator' do
1048
+ it 'should raise an exception' do
1049
+ lambda {
1050
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [], :fields => [ :name ]))
1051
+ }.should raise_error(ArgumentError, '+options[:order]+ should not be empty if +options[:fields] contains a non-operator')
1052
+ end
1053
+ end
1054
+
1055
+ describe 'that is an Array containing an unknown String' do
1056
+ it 'should raise an exception' do
1057
+ lambda {
1058
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ 'unknown' ]))
1059
+ }.should raise_error(ArgumentError, "+options[:order]+ entry \"unknown\" does not map to a property in #{@model}")
1060
+ end
1061
+ end
1062
+
1063
+ describe 'that is an Array containing an invalid object' do
1064
+ it 'should raise an exception' do
1065
+ lambda {
1066
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ 1 ]))
1067
+ }.should raise_error(ArgumentError, '+options[:order]+ entry 1 of an unsupported object Fixnum')
1068
+ end
1069
+ end
1070
+
1071
+ describe 'that contains a Query::Direction with a property that is not part of the model' do
1072
+ before :all do
1073
+ @property = DataMapper::Property.new(@model, :unknown, String)
1074
+ @direction = DataMapper::Query::Direction.new(@property, :desc)
1075
+ end
1076
+
1077
+ it 'should raise an exception' do
1078
+ lambda {
1079
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ @direction ]))
1080
+ }.should raise_error(ArgumentError, "+options[:order]+ entry :unknown does not map to a property in #{@model}")
1081
+ end
1082
+ end
1083
+
1084
+ describe 'that contains a Query::Operator with a target that is not part of the model' do
1085
+ it 'should raise an exception' do
1086
+ lambda {
1087
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ :unknown.desc ]))
1088
+ }.should raise_error(ArgumentError, "+options[:order]+ entry :unknown does not map to a property in #{@model}")
1089
+ end
1090
+ end
1091
+
1092
+ describe 'that contains a Query::Operator with an unknown operator' do
1093
+ it 'should raise an exception' do
1094
+ lambda {
1095
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ :name.gt ]))
1096
+ }.should raise_error(ArgumentError, '+options[:order]+ entry #<DataMapper::Query::Operator @target=:name @operator=:gt> used an invalid operator gt')
1097
+ end
1098
+ end
1099
+
1100
+ describe 'that contains a Property that is not part of the model' do
1101
+ before :all do
1102
+ @property = DataMapper::Property.new(@model, :unknown, String)
1103
+ end
1104
+
1105
+ it 'should raise an exception' do
1106
+ lambda {
1107
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ @property ]))
1108
+ }.should raise_error(ArgumentError, "+options[:order]+ entry :unknown does not map to a property in #{@model}")
1109
+ end
1110
+ end
1111
+
1112
+ describe 'that contains a Symbol that is not for a Property in the model' do
1113
+ it 'should raise an exception' do
1114
+ lambda {
1115
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ :unknown ]))
1116
+ }.should raise_error(ArgumentError, "+options[:order]+ entry :unknown does not map to a property in #{@model}")
1117
+ end
1118
+ end
1119
+
1120
+ describe 'that contains a String that is not for a Property in the model' do
1121
+ it 'should raise an exception' do
1122
+ lambda {
1123
+ DataMapper::Query.new(@repository, @model, @options.update(:order => [ 'unknown' ]))
1124
+ }.should raise_error(ArgumentError, "+options[:order]+ entry \"unknown\" does not map to a property in #{@model}")
1125
+ end
1126
+ end
1127
+ end
1128
+
1129
+ describe 'with a unique option' do
1130
+ describe 'that is valid' do
1131
+ before :all do
1132
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1133
+ end
1134
+
1135
+ it { @return.should be_kind_of(DataMapper::Query) }
1136
+
1137
+ it 'should set the unique? flag' do
1138
+ @return.unique?.should == @unique
1139
+ end
1140
+ end
1141
+
1142
+ describe 'that is missing' do
1143
+ before :all do
1144
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:unique).freeze)
1145
+ end
1146
+
1147
+ it { @return.should be_kind_of(DataMapper::Query) }
1148
+
1149
+ it 'should set the query to not be unique' do
1150
+ @return.should_not be_unique
1151
+ end
1152
+ end
1153
+
1154
+ describe 'that is invalid' do
1155
+ it 'should raise an exception' do
1156
+ lambda {
1157
+ DataMapper::Query.new(@repository, @model, @options.update(:unique => nil))
1158
+ }.should raise_error(ArgumentError, '+options[:unique]+ should be true or false, but was nil')
1159
+ end
1160
+ end
1161
+ end
1162
+
1163
+ describe 'with an add_reversed option' do
1164
+ describe 'that is valid' do
1165
+ before :all do
1166
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1167
+ end
1168
+
1169
+ it { @return.should be_kind_of(DataMapper::Query) }
1170
+
1171
+ it 'should set the add_reversed? flag' do
1172
+ @return.add_reversed?.should == @add_reversed
1173
+ end
1174
+ end
1175
+
1176
+ describe 'that is missing' do
1177
+ before :all do
1178
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:add_reversed).freeze)
1179
+ end
1180
+
1181
+ it { @return.should be_kind_of(DataMapper::Query) }
1182
+
1183
+ it 'should set the query to not add in reverse order' do
1184
+ # TODO: think about renaming the flag to not sound 'clumsy'
1185
+ @return.should_not be_add_reversed
1186
+ end
1187
+ end
1188
+
1189
+ describe 'that is invalid' do
1190
+ it 'should raise an exception' do
1191
+ lambda {
1192
+ DataMapper::Query.new(@repository, @model, @options.update(:add_reversed => nil))
1193
+ }.should raise_error(ArgumentError, '+options[:add_reversed]+ should be true or false, but was nil')
1194
+ end
1195
+ end
1196
+ end
1197
+
1198
+ describe 'with a reload option' do
1199
+ describe 'that is valid' do
1200
+ before :all do
1201
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1202
+ end
1203
+
1204
+ it { @return.should be_kind_of(DataMapper::Query) }
1205
+
1206
+ it 'should set the reload? flag' do
1207
+ @return.reload?.should == @reload
1208
+ end
1209
+ end
1210
+
1211
+ describe 'that is missing' do
1212
+ before :all do
1213
+ @return = DataMapper::Query.new(@repository, @model, @options.except(:reload).freeze)
1214
+ end
1215
+
1216
+ it { @return.should be_kind_of(DataMapper::Query) }
1217
+
1218
+ it 'should set the query to not reload' do
1219
+ @return.should_not be_reload
1220
+ end
1221
+ end
1222
+
1223
+ describe 'that is invalid' do
1224
+ it 'should raise an exception' do
1225
+ lambda {
1226
+ DataMapper::Query.new(@repository, @model, @options.update(:reload => nil))
1227
+ }.should raise_error(ArgumentError, '+options[:reload]+ should be true or false, but was nil')
1228
+ end
1229
+ end
1230
+ end
1231
+
1232
+ describe 'with options' do
1233
+ describe 'that are unknown' do
1234
+ before :all do
1235
+ @options.update(@options.delete(:conditions))
1236
+
1237
+ @return = DataMapper::Query.new(@repository, @model, @options.freeze)
1238
+ end
1239
+
1240
+ it { @return.should be_kind_of(DataMapper::Query) }
1241
+
1242
+ it 'should set the conditions' do
1243
+ @return.conditions.should ==
1244
+ DataMapper::Query::Conditions::Operation.new(
1245
+ :and,
1246
+ DataMapper::Query::Conditions::Comparison.new(
1247
+ :eql,
1248
+ @model.properties[:name],
1249
+ @conditions[:name]
1250
+ )
1251
+ )
1252
+ end
1253
+ end
1254
+
1255
+ describe 'that are invalid' do
1256
+ it 'should raise an exception' do
1257
+ lambda {
1258
+ DataMapper::Query.new(@repository, @model, 'invalid')
1259
+ }.should raise_error(ArgumentError, '+options+ should be Hash, but was String')
1260
+ end
1261
+ end
1262
+ end
1263
+
1264
+ describe 'with no options' do
1265
+ before :all do
1266
+ @return = DataMapper::Query.new(@repository, @model)
1267
+ end
1268
+
1269
+ it { @return.should be_kind_of(DataMapper::Query) }
1270
+
1271
+ it 'should set options to an empty Hash' do
1272
+ @return.options.should == {}
1273
+ end
1274
+ end
1275
+ end
1276
+ end
1277
+
1278
+ # instance methods
1279
+ describe DataMapper::Query do
1280
+ before :all do
1281
+ class ::User
1282
+ include DataMapper::Resource
1283
+
1284
+ property :name, String, :key => true
1285
+ property :citizenship, String
1286
+
1287
+ belongs_to :referrer, self, :nullable => true
1288
+ has n, :referrals, self, :inverse => :referrer
1289
+
1290
+ # TODO: figure out a way to remove this
1291
+ assert_valid
1292
+ end
1293
+
1294
+ @repository = DataMapper::Repository.new(:default)
1295
+ @model = User
1296
+ @options = { :limit => 3 }
1297
+ @query = DataMapper::Query.new(@repository, @model, @options)
1298
+ @original = @query
1299
+ end
1300
+
1301
+ before :all do
1302
+ @other_options = {
1303
+ :fields => [ @model.properties[:name] ].freeze,
1304
+ :links => [ @model.relationships[:referrer] ].freeze,
1305
+ :conditions => [ 'name = ?', 'Dan Kubb' ].freeze,
1306
+ :offset => 1,
1307
+ :limit => 2,
1308
+ :order => [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ].freeze,
1309
+ :unique => true,
1310
+ :add_reversed => true,
1311
+ :reload => true,
1312
+ }
1313
+ end
1314
+
1315
+ it { @query.should respond_to(:==) }
1316
+
1317
+ describe '#==' do
1318
+ describe 'when other is equal' do
1319
+ before :all do
1320
+ @return = @query == @query
1321
+ end
1322
+
1323
+ it { @return.should be_true }
1324
+ end
1325
+
1326
+ describe 'when other is equivalent' do
1327
+ before :all do
1328
+ @return = @query == @query.dup
1329
+ end
1330
+
1331
+ it { @return.should be_true }
1332
+ end
1333
+
1334
+ DataMapper::Query::OPTIONS.each do |name|
1335
+ describe "when other has an inequalvalent #{name}" do
1336
+ before :all do
1337
+ @return = @query == @query.merge(name => @other_options[name])
1338
+ end
1339
+
1340
+ it { @return.should be_false }
1341
+ end
1342
+ end
1343
+
1344
+ describe 'when other is a different type of object that can be compared, and is equivalent' do
1345
+ before :all do
1346
+ @other = OpenStruct.new(
1347
+ :repository => @query.repository,
1348
+ :model => @query.model,
1349
+ :fields => @query.fields,
1350
+ :links => @query.links,
1351
+ :conditions => @query.conditions,
1352
+ :order => @query.order,
1353
+ :limit => @query.limit,
1354
+ :offset => @query.offset,
1355
+ :reload? => @query.reload?,
1356
+ :unique? => @query.unique?,
1357
+ :add_reversed? => @query.add_reversed?
1358
+ )
1359
+
1360
+ @return = @query == @other
1361
+ end
1362
+
1363
+ it { @return.should be_true }
1364
+ end
1365
+
1366
+ describe 'when other is a different type of object that can be compared, and is not equivalent' do
1367
+ before :all do
1368
+ @other = OpenStruct.new(
1369
+ :repository => @query.repository,
1370
+ :model => @query.model,
1371
+ :fields => @query.fields,
1372
+ :links => @query.links,
1373
+ :conditions => @query.conditions,
1374
+ :order => @query.order,
1375
+ :limit => @query.limit,
1376
+ :offset => @query.offset,
1377
+ :reload? => true,
1378
+ :unique? => @query.unique?,
1379
+ :add_reversed? => @query.add_reversed?
1380
+ )
1381
+
1382
+ @return = @query == @other
1383
+ end
1384
+
1385
+ it { @return.should be_false }
1386
+ end
1387
+
1388
+ describe 'when other is a different type of object that cannot be compared' do
1389
+ before :all do
1390
+ @return = @query == 'invalid'
1391
+ end
1392
+
1393
+ it { @return.should be_false }
1394
+ end
1395
+ end
1396
+
1397
+ it { @query.should respond_to(:conditions) }
1398
+
1399
+ describe '#conditions' do
1400
+ before :all do
1401
+ @query.update(:name => 'Dan Kubb')
1402
+
1403
+ @return = @query.conditions
1404
+ end
1405
+
1406
+ it { @return.should be_kind_of(DataMapper::Query::Conditions::AndOperation) }
1407
+
1408
+ it 'should return expected value' do
1409
+ @return.should ==
1410
+ DataMapper::Query::Conditions::Operation.new(
1411
+ :and,
1412
+ DataMapper::Query::Conditions::Comparison.new(
1413
+ :eql,
1414
+ @model.properties[:name],
1415
+ 'Dan Kubb'
1416
+ )
1417
+ )
1418
+ end
1419
+ end
1420
+
1421
+ it { @query.should respond_to(:dup) }
1422
+
1423
+ describe '#dup' do
1424
+ it 'should be awesome'
1425
+ end
1426
+
1427
+ it { @query.should respond_to(:eql?) }
1428
+
1429
+ describe '#eql?' do
1430
+ describe 'when other is equal' do
1431
+ before :all do
1432
+ @return = @query.eql?(@query)
1433
+ end
1434
+
1435
+ it { @return.should be_true }
1436
+ end
1437
+
1438
+ describe 'when other is eql' do
1439
+ before :all do
1440
+ @return = @query.eql?(@query.dup)
1441
+ end
1442
+
1443
+ it { @return.should be_true }
1444
+ end
1445
+
1446
+ DataMapper::Query::OPTIONS.each do |name|
1447
+ describe "when other has an not eql #{name}" do
1448
+ before :all do
1449
+ @return = @query.eql?(@query.merge(name => @other_options[name]))
1450
+ end
1451
+
1452
+ it { @return.should be_false }
1453
+ end
1454
+ end
1455
+
1456
+ describe 'when other is a different type of object' do
1457
+ before :all do
1458
+ @other = OpenStruct.new(
1459
+ :repository => @query.repository,
1460
+ :model => @query.model,
1461
+ :fields => @query.fields,
1462
+ :links => @query.links,
1463
+ :conditions => @query.conditions,
1464
+ :order => @query.order,
1465
+ :limit => @query.limit,
1466
+ :offset => @query.offset,
1467
+ :reload? => @query.reload?,
1468
+ :unique? => @query.unique?,
1469
+ :add_reversed? => @query.add_reversed?
1470
+ )
1471
+
1472
+ @return = @query.eql?(@other)
1473
+ end
1474
+
1475
+ it { @return.should be_false }
1476
+ end
1477
+ end
1478
+
1479
+ it { @query.should respond_to(:fields) }
1480
+
1481
+ describe '#fields' do
1482
+ before :all do
1483
+ @return = @query.fields
1484
+ end
1485
+
1486
+ it { @return.should be_kind_of(Array) }
1487
+
1488
+ it 'should return expected value' do
1489
+ @return.should == [ @model.properties[:name], @model.properties[:citizenship], @model.properties[:referrer_name] ]
1490
+ end
1491
+ end
1492
+
1493
+ it { @query.should respond_to(:filter_records) }
1494
+
1495
+ supported_by :all do
1496
+ describe '#filter_records' do
1497
+ before :all do
1498
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
1499
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
1500
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
1501
+
1502
+ @records = [ @john, @sam, @dan ]
1503
+
1504
+ @query.update(:name.not => @sam['name'])
1505
+
1506
+ @return = @query.filter_records(@records)
1507
+ end
1508
+
1509
+ it 'should return Enumerable' do
1510
+ @return.should be_kind_of(Enumerable)
1511
+ end
1512
+
1513
+ it 'should not be the records provided' do
1514
+ @return.should_not equal(@records)
1515
+ end
1516
+
1517
+ it 'should return expected values' do
1518
+ @return.should == [ @dan, @john ]
1519
+ end
1520
+ end
1521
+ end
1522
+
1523
+ it { @query.should respond_to(:inspect) }
1524
+
1525
+ describe '#inspect' do
1526
+ before :all do
1527
+ @return = @query.inspect
1528
+ end
1529
+
1530
+ it 'should return expected value' do
1531
+ @return.should == <<-INSPECT.compress_lines
1532
+ #<DataMapper::Query
1533
+ @repository=:default
1534
+ @model=User
1535
+ @fields=[#<DataMapper::Property @model=User @name=:name>, #<DataMapper::Property @model=User @name=:citizenship>, #<DataMapper::Property @model=User @name=:referrer_name>]
1536
+ @links=[]
1537
+ @conditions=#<DataMapper::Query::Conditions::AndOperation @operands=[]>
1538
+ @order=[#<DataMapper::Query::Direction @target=#<DataMapper::Property @model=User @name=:name> @operator=:asc>]
1539
+ @limit=3
1540
+ @offset=0
1541
+ @reload=false
1542
+ @unique=false>
1543
+ INSPECT
1544
+ end
1545
+ end
1546
+
1547
+ it { @query.should respond_to(:limit) }
1548
+
1549
+ describe '#limit' do
1550
+ before :all do
1551
+ @return = @query.limit
1552
+ end
1553
+
1554
+ it { @return.should be_kind_of(Integer) }
1555
+
1556
+ it 'should return expected value' do
1557
+ @return.should == 3
1558
+ end
1559
+ end
1560
+
1561
+ it { @query.should respond_to(:limit_records) }
1562
+
1563
+ supported_by :all do
1564
+ describe '#limit_records' do
1565
+ before :all do
1566
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
1567
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
1568
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
1569
+
1570
+ @records = [ @john, @sam, @dan ]
1571
+
1572
+ @query.update(:limit => 1, :offset => 1)
1573
+
1574
+ @return = @query.limit_records(@records)
1575
+ end
1576
+
1577
+ it 'should return Enumerable' do
1578
+ @return.should be_kind_of(Enumerable)
1579
+ end
1580
+
1581
+ it 'should not be the records provided' do
1582
+ @return.should_not equal(@records)
1583
+ end
1584
+
1585
+ it 'should return expected values' do
1586
+ @return.should == [ @sam ]
1587
+ end
1588
+ end
1589
+ end
1590
+
1591
+ it { @query.should respond_to(:links) }
1592
+
1593
+ describe '#links' do
1594
+ before :all do
1595
+ @return = @query.links
1596
+ end
1597
+
1598
+ it { @return.should be_kind_of(Array) }
1599
+
1600
+ it { @return.should be_empty }
1601
+ end
1602
+
1603
+ it { @query.should respond_to(:match_records) }
1604
+
1605
+ supported_by :all do
1606
+ describe '#match_records' do
1607
+ before :all do
1608
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
1609
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
1610
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
1611
+
1612
+ @records = [ @john, @sam, @dan ]
1613
+
1614
+ @query.update(:name.not => @sam['name'])
1615
+
1616
+ @return = @query.match_records(@records)
1617
+ end
1618
+
1619
+ it 'should return Enumerable' do
1620
+ @return.should be_kind_of(Enumerable)
1621
+ end
1622
+
1623
+ it 'should not be the records provided' do
1624
+ @return.should_not equal(@records)
1625
+ end
1626
+
1627
+ it 'should return expected values' do
1628
+ @return.should == [ @john, @dan ]
1629
+ end
1630
+ end
1631
+ end
1632
+
1633
+ it { @query.should respond_to(:merge) }
1634
+
1635
+ describe '#merge' do
1636
+ describe "with a Hash" do
1637
+ before(:each) do
1638
+ @return = @query.merge({ :limit => 202 })
1639
+ end
1640
+
1641
+ it "does not affect the receiver" do
1642
+ @query.options[:limit].should == 3
1643
+ end
1644
+ end
1645
+
1646
+ describe "with a Query" do
1647
+ before(:each) do
1648
+ @other = DataMapper::Query.new(@repository, @model, @options.update(@other_options))
1649
+ @return = @query.merge(@other)
1650
+ end
1651
+
1652
+ it "does not affect the receiver" do
1653
+ @query.options[:limit].should == 3
1654
+ end
1655
+ end
1656
+ end
1657
+
1658
+ it { @query.should respond_to(:model) }
1659
+
1660
+ describe '#model' do
1661
+ before :all do
1662
+ @return = @query.model
1663
+ end
1664
+
1665
+ it { @return.should be_kind_of(Class) }
1666
+
1667
+ it 'should return expected value' do
1668
+ @return.should == @model
1669
+ end
1670
+ end
1671
+
1672
+ it { @query.should respond_to(:offset) }
1673
+
1674
+ describe '#offset' do
1675
+ before :all do
1676
+ @return = @query.offset
1677
+ end
1678
+
1679
+ it { @return.should be_kind_of(Integer) }
1680
+
1681
+ it 'should return expected value' do
1682
+ @return.should == 0
1683
+ end
1684
+ end
1685
+
1686
+ it { @query.should respond_to(:order) }
1687
+
1688
+ describe '#order' do
1689
+ before :all do
1690
+ @return = @query.order
1691
+ end
1692
+
1693
+ it { @return.should be_kind_of(Array) }
1694
+
1695
+ it 'should return expected value' do
1696
+ @return.should == [ DataMapper::Query::Direction.new(@model.properties[:name]) ]
1697
+ end
1698
+ end
1699
+
1700
+ it { @query.should respond_to(:raw?) }
1701
+
1702
+ describe '#raw?' do
1703
+ describe 'when the query contains raw conditions' do
1704
+ before :all do
1705
+ @query.update(:conditions => [ 'name = ?', 'Dan Kubb' ])
1706
+ end
1707
+
1708
+ it { @query.should be_raw }
1709
+ end
1710
+
1711
+ describe 'when the query does not contain raw conditions' do
1712
+ it { @query.should_not be_raw }
1713
+ end
1714
+ end
1715
+
1716
+ it { @query.should respond_to(:relative) }
1717
+
1718
+ describe '#relative' do
1719
+ describe 'with a Hash' do
1720
+ describe 'that is empty' do
1721
+ before :all do
1722
+ @return = @query.relative({})
1723
+ end
1724
+
1725
+ it { @return.should be_kind_of(DataMapper::Query) }
1726
+
1727
+ it 'should not return self' do
1728
+ @return.should_not equal(@query)
1729
+ end
1730
+
1731
+ it 'should return a copy' do
1732
+ @return.should be_eql(@query)
1733
+ end
1734
+ end
1735
+
1736
+ describe 'using a different repository as a Repository' do
1737
+ before :all do
1738
+ @repository = DataMapper::Repository.new(:other)
1739
+ @return = @query.relative(:repository => @repository)
1740
+ end
1741
+
1742
+ it { @return.should be_kind_of(DataMapper::Query) }
1743
+
1744
+ it 'should not return self' do
1745
+ @return.should_not equal(@original)
1746
+ end
1747
+
1748
+ it 'should set the repository' do
1749
+ @return.repository.should equal(@repository)
1750
+ end
1751
+ end
1752
+
1753
+ describe 'using a different repository as a Symbol' do
1754
+ before :all do
1755
+ @other_adapter = DataMapper.setup(:other, :adapter => :in_memory)
1756
+
1757
+ @return = @query.relative(:repository => :other)
1758
+ end
1759
+
1760
+ after :all do
1761
+ DataMapper::Repository.adapters.delete(@other_adapter.name)
1762
+ end
1763
+
1764
+ it { @return.should be_kind_of(DataMapper::Query) }
1765
+
1766
+ it 'should not return self' do
1767
+ @return.should_not equal(@original)
1768
+ end
1769
+
1770
+ it 'should set the repository' do
1771
+ @return.repository.should == DataMapper::Repository.new(:other)
1772
+ end
1773
+ end
1774
+
1775
+ describe 'using different options' do
1776
+ before :all do
1777
+ @return = @query.relative(@other_options)
1778
+ end
1779
+
1780
+ it { @return.should be_kind_of(DataMapper::Query) }
1781
+
1782
+ it 'should not return self' do
1783
+ @return.should_not equal(@original)
1784
+ end
1785
+
1786
+ it 'should update the fields' do
1787
+ @return.fields.should == @other_options[:fields]
1788
+ end
1789
+
1790
+ it 'should update the links' do
1791
+ @return.links.should == @other_options[:links]
1792
+ end
1793
+
1794
+ it 'should update the conditions' do
1795
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
1796
+ end
1797
+
1798
+ it 'should update the offset' do
1799
+ @return.offset.should == @other_options[:offset]
1800
+ end
1801
+
1802
+ it 'should update the limit' do
1803
+ @return.limit.should == @other_options[:limit]
1804
+ end
1805
+
1806
+ it 'should update the order' do
1807
+ @return.order.should == @other_options[:order]
1808
+ end
1809
+
1810
+ it 'should update the unique' do
1811
+ @return.unique?.should == @other_options[:unique]
1812
+ end
1813
+
1814
+ it 'should update the add_reversed' do
1815
+ @return.add_reversed?.should == @other_options[:add_reversed]
1816
+ end
1817
+
1818
+ it 'should update the reload' do
1819
+ @return.reload?.should == @other_options[:reload]
1820
+ end
1821
+ end
1822
+
1823
+ describe 'using extra options' do
1824
+ before :all do
1825
+ @options = { :name => 'Dan Kubb' }
1826
+
1827
+ @return = @query.relative(@options)
1828
+ end
1829
+
1830
+ it { @return.should be_kind_of(DataMapper::Query) }
1831
+
1832
+ it 'should not return self' do
1833
+ @return.should_not equal(@original)
1834
+ end
1835
+
1836
+ it 'should update the conditions' do
1837
+ @return.conditions.should ==
1838
+ DataMapper::Query::Conditions::Operation.new(
1839
+ :and,
1840
+ DataMapper::Query::Conditions::Comparison.new(
1841
+ :eql,
1842
+ @model.properties[:name],
1843
+ @options[:name]
1844
+ )
1845
+ )
1846
+ end
1847
+ end
1848
+
1849
+ describe 'using an offset when query offset is greater than 0' do
1850
+ before :all do
1851
+ @query = @query.update(:offset => 1, :limit => 2)
1852
+
1853
+ @return = @query.relative(:offset => 1)
1854
+ end
1855
+
1856
+ it { @return.should be_kind_of(DataMapper::Query) }
1857
+
1858
+ it 'should not return self' do
1859
+ @return.should_not equal(@original)
1860
+ end
1861
+
1862
+ it 'should update the offset to be relative to the original offset' do
1863
+ @return.offset.should == 2
1864
+ end
1865
+ end
1866
+
1867
+ describe 'using an limit when query limit specified' do
1868
+ before :all do
1869
+ @query = @query.update(:offset => 1, :limit => 2)
1870
+
1871
+ @return = @query.relative(:limit => 1)
1872
+ end
1873
+
1874
+ it { @return.should be_kind_of(DataMapper::Query) }
1875
+
1876
+ it 'should not return self' do
1877
+ @return.should_not equal(@original)
1878
+ end
1879
+
1880
+ it 'should update the limit' do
1881
+ @return.limit.should == 1
1882
+ end
1883
+ end
1884
+ end
1885
+ end
1886
+
1887
+ it { @query.should respond_to(:reload?) }
1888
+
1889
+ describe '#reload?' do
1890
+ describe 'when the query should reload' do
1891
+ before :all do
1892
+ @query.update(:reload => true)
1893
+ end
1894
+
1895
+ it { @query.should be_reload }
1896
+ end
1897
+
1898
+ describe 'when the query should not reload' do
1899
+ it { @query.should_not be_reload }
1900
+ end
1901
+ end
1902
+
1903
+ it { @query.should respond_to(:repository) }
1904
+
1905
+ describe '#repository' do
1906
+ before :all do
1907
+ @return = @query.repository
1908
+ end
1909
+
1910
+ it { @return.should be_kind_of(DataMapper::Repository) }
1911
+
1912
+ it 'should return expected value' do
1913
+ @return.should == @repository
1914
+ end
1915
+ end
1916
+
1917
+ it { @query.should respond_to(:reverse) }
1918
+
1919
+ describe '#reverse' do
1920
+ before :all do
1921
+ @return = @query.reverse
1922
+ end
1923
+
1924
+ it { @return.should be_kind_of(DataMapper::Query) }
1925
+
1926
+ it 'should copy the Query' do
1927
+ @return.should_not equal(@original)
1928
+ end
1929
+
1930
+ # TODO: push this into dup spec
1931
+ it 'should not reference original order' do
1932
+ @return.order.should_not equal(@original.order)
1933
+ end
1934
+
1935
+ it 'should have a reversed order' do
1936
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ]
1937
+ end
1938
+
1939
+ [ :repository, :model, :fields, :links, :conditions, :offset, :limit, :unique?, :add_reversed?, :reload? ].each do |attribute|
1940
+ it "should have an equivalent #{attribute}" do
1941
+ @return.send(attribute).should == @original.send(attribute)
1942
+ end
1943
+ end
1944
+ end
1945
+
1946
+ it { @query.should respond_to(:reverse!) }
1947
+
1948
+ describe '#reverse!' do
1949
+ before :all do
1950
+ @return = @query.reverse!
1951
+ end
1952
+
1953
+ it { @return.should be_kind_of(DataMapper::Query) }
1954
+
1955
+ it { @return.should equal(@original) }
1956
+
1957
+ it 'should have a reversed order' do
1958
+ @return.order.should == [ DataMapper::Query::Direction.new(@model.properties[:name], :desc) ]
1959
+ end
1960
+ end
1961
+
1962
+ [ :slice, :[] ].each do |method|
1963
+ it { @query.should respond_to(method) }
1964
+
1965
+ describe "##{method}" do
1966
+ describe 'with a positive offset' do
1967
+ before :all do
1968
+ @query = @query.update(:offset => 1, :limit => 2)
1969
+
1970
+ @return = @query.send(method, 1)
1971
+ end
1972
+
1973
+ it { @return.should be_kind_of(DataMapper::Query) }
1974
+
1975
+ it 'should not return self' do
1976
+ @return.should_not equal(@original)
1977
+ end
1978
+
1979
+ it 'should update the offset to be relative to the original offset' do
1980
+ @return.offset.should == 2
1981
+ end
1982
+
1983
+ it 'should update the limit to 1' do
1984
+ @return.limit.should == 1
1985
+ end
1986
+ end
1987
+
1988
+ describe 'with a positive offset and length' do
1989
+ before :all do
1990
+ @query = @query.update(:offset => 1, :limit => 2)
1991
+
1992
+ @return = @query.send(method, 1, 1)
1993
+ end
1994
+
1995
+ it { @return.should be_kind_of(DataMapper::Query) }
1996
+
1997
+ it 'should not return self' do
1998
+ @return.should_not equal(@original)
1999
+ end
2000
+
2001
+ it 'should update the offset to be relative to the original offset' do
2002
+ @return.offset.should == 2
2003
+ end
2004
+
2005
+ it 'should update the limit' do
2006
+ @return.limit.should == 1
2007
+ end
2008
+ end
2009
+
2010
+ describe 'with a positive range' do
2011
+ before :all do
2012
+ @query = @query.update(:offset => 1, :limit => 3)
2013
+
2014
+ @return = @query.send(method, 1..2)
2015
+ end
2016
+
2017
+ it { @return.should be_kind_of(DataMapper::Query) }
2018
+
2019
+ it 'should not return self' do
2020
+ @return.should_not equal(@original)
2021
+ end
2022
+
2023
+ it 'should update the offset to be relative to the original offset' do
2024
+ @return.offset.should == 2
2025
+ end
2026
+
2027
+ it 'should update the limit' do
2028
+ @return.limit.should == 2
2029
+ end
2030
+ end
2031
+
2032
+ describe 'with a negative offset' do
2033
+ before :all do
2034
+ @query = @query.update(:offset => 1, :limit => 2)
2035
+
2036
+ @return = @query.send(method, -1)
2037
+ end
2038
+
2039
+ it { @return.should be_kind_of(DataMapper::Query) }
2040
+
2041
+ it 'should not return self' do
2042
+ @return.should_not equal(@original)
2043
+ end
2044
+
2045
+ it 'should update the offset to be relative to the original offset' do
2046
+ pending "TODO: update Query##{method} handle negative offset" do
2047
+ @return.offset.should == 2
2048
+ end
2049
+ end
2050
+
2051
+ it 'should update the limit to 1' do
2052
+ @return.limit.should == 1
2053
+ end
2054
+ end
2055
+
2056
+ describe 'with a negative offset and length' do
2057
+ before :all do
2058
+ @query = @query.update(:offset => 1, :limit => 2)
2059
+
2060
+ @return = @query.send(method, -1, 1)
2061
+ end
2062
+
2063
+ it { @return.should be_kind_of(DataMapper::Query) }
2064
+
2065
+ it 'should not return self' do
2066
+ @return.should_not equal(@original)
2067
+ end
2068
+
2069
+ it 'should update the offset to be relative to the original offset' do
2070
+ pending "TODO: update Query##{method} handle negative offset and length" do
2071
+ @return.offset.should == 2
2072
+ end
2073
+ end
2074
+
2075
+ it 'should update the limit to 1' do
2076
+ @return.limit.should == 1
2077
+ end
2078
+ end
2079
+
2080
+ describe 'with a negative range' do
2081
+ before :all do
2082
+ @query = @query.update(:offset => 1, :limit => 3)
2083
+
2084
+ rescue_if "TODO: update Query##{method} handle negative range" do
2085
+ @return = @query.send(method, -2..-1)
2086
+ end
2087
+ end
2088
+
2089
+ before do
2090
+ pending_if "TODO: update Query##{method} handle negative range", !defined?(@return)
2091
+ end
2092
+
2093
+ it { @return.should be_kind_of(DataMapper::Query) }
2094
+
2095
+ it 'should not return self' do
2096
+ @return.should_not equal(@original)
2097
+ end
2098
+
2099
+ it 'should update the offset to be relative to the original offset' do
2100
+ @return.offset.should == 2
2101
+ end
2102
+
2103
+ it 'should update the limit to 1' do
2104
+ @return.limit.should == 2
2105
+ end
2106
+ end
2107
+
2108
+ describe 'with an offset not within range' do
2109
+ before :all do
2110
+ @query = @query.update(:offset => 1, :limit => 3)
2111
+ end
2112
+
2113
+ it 'should raise an exception' do
2114
+ lambda {
2115
+ @query.send(method, 12)
2116
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2117
+ end
2118
+ end
2119
+
2120
+ describe 'with an offset and length not within range' do
2121
+ before :all do
2122
+ @query = @query.update(:offset => 1, :limit => 3)
2123
+ end
2124
+
2125
+ it 'should raise an exception' do
2126
+ lambda {
2127
+ @query.send(method, 12, 1)
2128
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2129
+ end
2130
+ end
2131
+
2132
+ describe 'with a range not within range' do
2133
+ before :all do
2134
+ @query = @query.update(:offset => 1, :limit => 3)
2135
+ end
2136
+
2137
+ it 'should raise an exception' do
2138
+ lambda {
2139
+ @query.send(method, 12..12)
2140
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2141
+ end
2142
+ end
2143
+
2144
+ describe 'with invalid arguments' do
2145
+ it 'should raise an exception' do
2146
+ lambda {
2147
+ @query.send(method, 'invalid')
2148
+ }.should raise_error(ArgumentError, 'arguments may be 1 or 2 Integers, or 1 Range object, was: ["invalid"]')
2149
+ end
2150
+ end
2151
+ end
2152
+ end
2153
+
2154
+ it { @query.should respond_to(:slice!) }
2155
+
2156
+ describe '#slice!' do
2157
+ describe 'with a positive offset' do
2158
+ before :all do
2159
+ @query = @query.update(:offset => 1, :limit => 2)
2160
+
2161
+ @return = @query.slice!(1)
2162
+ end
2163
+
2164
+ it { @return.should be_kind_of(DataMapper::Query) }
2165
+
2166
+ it 'should return self' do
2167
+ @return.should equal(@original)
2168
+ end
2169
+
2170
+ it 'should update the offset to be relative to the original offset' do
2171
+ @return.offset.should == 2
2172
+ end
2173
+
2174
+ it 'should update the limit to 1' do
2175
+ @return.limit.should == 1
2176
+ end
2177
+ end
2178
+
2179
+ describe 'with a positive offset and length' do
2180
+ before :all do
2181
+ @query = @query.update(:offset => 1, :limit => 2)
2182
+
2183
+ @return = @query.slice!(1, 1)
2184
+ end
2185
+
2186
+ it { @return.should be_kind_of(DataMapper::Query) }
2187
+
2188
+ it 'should return self' do
2189
+ @return.should equal(@original)
2190
+ end
2191
+
2192
+ it 'should update the offset to be relative to the original offset' do
2193
+ @return.offset.should == 2
2194
+ end
2195
+
2196
+ it 'should update the limit' do
2197
+ @return.limit.should == 1
2198
+ end
2199
+ end
2200
+
2201
+ describe 'with a positive range' do
2202
+ before :all do
2203
+ @query = @query.update(:offset => 1, :limit => 3)
2204
+
2205
+ @return = @query.slice!(1..2)
2206
+ end
2207
+
2208
+ it { @return.should be_kind_of(DataMapper::Query) }
2209
+
2210
+ it 'should return self' do
2211
+ @return.should equal(@original)
2212
+ end
2213
+
2214
+ it 'should update the offset to be relative to the original offset' do
2215
+ @return.offset.should == 2
2216
+ end
2217
+
2218
+ it 'should update the limit' do
2219
+ @return.limit.should == 2
2220
+ end
2221
+ end
2222
+
2223
+ describe 'with a negative offset' do
2224
+ before :all do
2225
+ @query = @query.update(:offset => 1, :limit => 2)
2226
+
2227
+ @return = @query.slice!(-1)
2228
+ end
2229
+
2230
+ it { @return.should be_kind_of(DataMapper::Query) }
2231
+
2232
+ it 'should return self' do
2233
+ @return.should equal(@original)
2234
+ end
2235
+
2236
+ it 'should update the offset to be relative to the original offset' do
2237
+ pending 'TODO: update Query#slice! handle negative offset' do
2238
+ @return.offset.should == 2
2239
+ end
2240
+ end
2241
+
2242
+ it 'should update the limit to 1' do
2243
+ @return.limit.should == 1
2244
+ end
2245
+ end
2246
+
2247
+ describe 'with a negative offset and length' do
2248
+ before :all do
2249
+ @query = @query.update(:offset => 1, :limit => 2)
2250
+
2251
+ @return = @query.slice!(-1, 1)
2252
+ end
2253
+
2254
+ it { @return.should be_kind_of(DataMapper::Query) }
2255
+
2256
+ it 'should return self' do
2257
+ @return.should equal(@original)
2258
+ end
2259
+
2260
+ it 'should update the offset to be relative to the original offset' do
2261
+ pending 'TODO: update Query#slice! handle negative offset and length' do
2262
+ @return.offset.should == 2
2263
+ end
2264
+ end
2265
+
2266
+ it 'should update the limit to 1' do
2267
+ @return.limit.should == 1
2268
+ end
2269
+ end
2270
+
2271
+ describe 'with a negative range' do
2272
+ before :all do
2273
+ @query = @query.update(:offset => 1, :limit => 3)
2274
+
2275
+ rescue_if 'TODO: update Query#slice! handle negative range' do
2276
+ @return = @query.slice!(-2..-1)
2277
+ end
2278
+ end
2279
+
2280
+ before do
2281
+ pending_if 'TODO: update Query#slice! handle negative range', !defined?(@return)
2282
+ end
2283
+
2284
+ it { @return.should be_kind_of(DataMapper::Query) }
2285
+
2286
+ it 'should return self' do
2287
+ @return.should equal(@original)
2288
+ end
2289
+
2290
+ it 'should update the offset to be relative to the original offset' do
2291
+ @return.offset.should == 2
2292
+ end
2293
+
2294
+ it 'should update the limit to 1' do
2295
+ @return.limit.should == 2
2296
+ end
2297
+ end
2298
+
2299
+ describe 'with an offset not within range' do
2300
+ before :all do
2301
+ @query = @query.update(:offset => 1, :limit => 3)
2302
+ end
2303
+
2304
+ it 'should raise an exception' do
2305
+ lambda {
2306
+ @query.slice!(12)
2307
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2308
+ end
2309
+ end
2310
+
2311
+ describe 'with an offset and length not within range' do
2312
+ before :all do
2313
+ @query = @query.update(:offset => 1, :limit => 3)
2314
+ end
2315
+
2316
+ it 'should raise an exception' do
2317
+ lambda {
2318
+ @query.slice!(12, 1)
2319
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2320
+ end
2321
+ end
2322
+
2323
+ describe 'with a range not within range' do
2324
+ before :all do
2325
+ @query = @query.update(:offset => 1, :limit => 3)
2326
+ end
2327
+
2328
+ it 'should raise an exception' do
2329
+ lambda {
2330
+ @query.slice!(12..12)
2331
+ }.should raise_error(RangeError, 'offset 12 and limit 1 are outside allowed range')
2332
+ end
2333
+ end
2334
+
2335
+ describe 'with invalid arguments' do
2336
+ it 'should raise an exception' do
2337
+ lambda {
2338
+ @query.slice!('invalid')
2339
+ }.should raise_error(ArgumentError, 'arguments may be 1 or 2 Integers, or 1 Range object, was: ["invalid"]')
2340
+ end
2341
+ end
2342
+ end
2343
+
2344
+ it { @query.should respond_to(:sort_records) }
2345
+
2346
+ supported_by :all do
2347
+ describe '#sort_records' do
2348
+ before :all do
2349
+ @john = { 'name' => 'John Doe', 'referrer_name' => nil }
2350
+ @sam = { 'name' => 'Sam Smoot', 'referrer_name' => nil }
2351
+ @dan = { 'name' => 'Dan Kubb', 'referrer_name' => 'Sam Smoot' }
2352
+
2353
+ @records = [ @john, @sam, @dan ]
2354
+
2355
+ @query.update(:order => [ :name ])
2356
+
2357
+ @return = @query.sort_records(@records)
2358
+ end
2359
+
2360
+ it 'should return Enumerable' do
2361
+ @return.should be_kind_of(Enumerable)
2362
+ end
2363
+
2364
+ it 'should not be the records provided' do
2365
+ @return.should_not equal(@records)
2366
+ end
2367
+
2368
+ it 'should return expected values' do
2369
+ @return.should == [ @dan, @john, @sam ]
2370
+ end
2371
+ end
2372
+ end
2373
+
2374
+ it { @query.should respond_to(:unique?) }
2375
+
2376
+ describe '#unique?' do
2377
+ describe 'when the query is unique' do
2378
+ before :all do
2379
+ @query.update(:unique => true)
2380
+ end
2381
+
2382
+ it { @query.should be_unique }
2383
+ end
2384
+
2385
+ describe 'when the query is not unique' do
2386
+ it { @query.should_not be_unique }
2387
+ end
2388
+ end
2389
+
2390
+ it { @query.should respond_to(:update) }
2391
+
2392
+ describe '#update' do
2393
+ describe 'with a Query' do
2394
+ describe 'that is equivalent' do
2395
+ before :all do
2396
+ @other = DataMapper::Query.new(@repository, @model, @options)
2397
+
2398
+ @return = @query.update(@other)
2399
+ end
2400
+
2401
+ it { @return.should be_kind_of(DataMapper::Query) }
2402
+
2403
+ it { @return.should equal(@original) }
2404
+ end
2405
+
2406
+ describe 'that has conditions set' do
2407
+ before :all do
2408
+ @and_operation = DataMapper::Query::Conditions::Operation.new(:and)
2409
+ @or_operation = DataMapper::Query::Conditions::Operation.new(:or)
2410
+
2411
+ @and_operation << DataMapper::Query::Conditions::Comparison.new(:eql,User.name,"Dan Kubb")
2412
+ @and_operation << DataMapper::Query::Conditions::Comparison.new(:eql,User.citizenship,"Canada")
2413
+
2414
+ @or_operation << DataMapper::Query::Conditions::Comparison.new(:eql,User.name,"Ted Han")
2415
+ @or_operation << DataMapper::Query::Conditions::Comparison.new(:eql,User.citizenship,"USA")
2416
+ @query_one = DataMapper::Query.new(@repository, @model, {:conditions=>@and_operation})
2417
+ @query_two = DataMapper::Query.new(@repository, @model, {:conditions=>@or_operation})
2418
+
2419
+ @conditions = @query_one.merge(@query_two).conditions
2420
+ end
2421
+
2422
+ it { @conditions.should == (@and_operation << @or_operation) }
2423
+ end
2424
+
2425
+ describe 'that is for an ancestor model' do
2426
+ before :all do
2427
+ class ::Contact < User; end
2428
+
2429
+ @query = DataMapper::Query.new(@repository, Contact, @options)
2430
+ @original = @query
2431
+
2432
+ @other = DataMapper::Query.new(@repository, User, @options)
2433
+
2434
+ @return = @query.update(@other)
2435
+ end
2436
+
2437
+ it { @return.should be_kind_of(DataMapper::Query) }
2438
+
2439
+ it { @return.should equal(@original) }
2440
+ end
2441
+
2442
+ describe 'using a different repository' do
2443
+ it 'should raise an exception' do
2444
+ lambda {
2445
+ @query.update(DataMapper::Query.new(DataMapper::Repository.new(:other), User))
2446
+ }.should raise_error(ArgumentError, '+other+ DataMapper::Query must be for the default repository, not other')
2447
+ end
2448
+ end
2449
+
2450
+ describe 'using a different model' do
2451
+ before :all do
2452
+ class ::Clone
2453
+ include DataMapper::Resource
2454
+
2455
+ property :name, String, :key => true
2456
+ end
2457
+ end
2458
+
2459
+ it 'should raise an exception' do
2460
+ lambda {
2461
+ @query.update(DataMapper::Query.new(@repository, Clone))
2462
+ }.should raise_error(ArgumentError, '+other+ DataMapper::Query must be for the User model, not Clone')
2463
+ end
2464
+ end
2465
+
2466
+ describe 'using different options' do
2467
+ before :all do
2468
+ @other = DataMapper::Query.new(@repository, @model, @options.update(@other_options))
2469
+
2470
+ @return = @query.update(@other)
2471
+ end
2472
+
2473
+ it { @return.should be_kind_of(DataMapper::Query) }
2474
+
2475
+ it { @return.should equal(@original) }
2476
+
2477
+ it 'should update the fields' do
2478
+ @return.fields.should == @options[:fields]
2479
+ end
2480
+
2481
+ it 'should update the links' do
2482
+ @return.links.should == @options[:links]
2483
+ end
2484
+
2485
+ it 'should update the conditions' do
2486
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
2487
+ end
2488
+
2489
+ it 'should update the offset' do
2490
+ @return.offset.should == @options[:offset]
2491
+ end
2492
+
2493
+ it 'should update the limit' do
2494
+ @return.limit.should == @options[:limit]
2495
+ end
2496
+
2497
+ it 'should update the order' do
2498
+ @return.order.should == @options[:order]
2499
+ end
2500
+
2501
+ it 'should update the unique' do
2502
+ @return.unique?.should == @options[:unique]
2503
+ end
2504
+
2505
+ it 'should update the add_reversed' do
2506
+ @return.add_reversed?.should == @options[:add_reversed]
2507
+ end
2508
+
2509
+ it 'should update the reload' do
2510
+ @return.reload?.should == @options[:reload]
2511
+ end
2512
+ end
2513
+
2514
+ describe 'using extra options' do
2515
+ before :all do
2516
+ @options.update(:name => 'Dan Kubb')
2517
+ @other = DataMapper::Query.new(@repository, @model, @options)
2518
+
2519
+ @return = @query.update(@other)
2520
+ end
2521
+
2522
+ it { @return.should be_kind_of(DataMapper::Query) }
2523
+
2524
+ it { @return.should equal(@original) }
2525
+
2526
+ it 'should update the conditions' do
2527
+ @return.conditions.should ==
2528
+ DataMapper::Query::Conditions::Operation.new(
2529
+ :and,
2530
+ DataMapper::Query::Conditions::Comparison.new(
2531
+ :eql,
2532
+ @model.properties[:name],
2533
+ @options[:name]
2534
+ )
2535
+ )
2536
+ end
2537
+ end
2538
+ end
2539
+
2540
+ describe 'with a Hash' do
2541
+ describe 'that is empty' do
2542
+ before :all do
2543
+ @copy = @query.dup
2544
+ @return = @query.update({})
2545
+ end
2546
+
2547
+ it { @return.should be_kind_of(DataMapper::Query) }
2548
+
2549
+ it { @return.should equal(@original) }
2550
+
2551
+ it 'should not change the Query' do
2552
+ @return.should == @copy
2553
+ end
2554
+ end
2555
+
2556
+ describe 'using different options' do
2557
+ before :all do
2558
+ @return = @query.update(@other_options)
2559
+ end
2560
+
2561
+ it { @return.should be_kind_of(DataMapper::Query) }
2562
+
2563
+ it { @return.should equal(@original) }
2564
+
2565
+ it 'should update the fields' do
2566
+ @return.fields.should == @other_options[:fields]
2567
+ end
2568
+
2569
+ it 'should update the links' do
2570
+ @return.links.should == @other_options[:links]
2571
+ end
2572
+
2573
+ it 'should update the conditions' do
2574
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(:and, [ 'name = ?', [ 'Dan Kubb' ] ])
2575
+ end
2576
+
2577
+ it 'should update the offset' do
2578
+ @return.offset.should == @other_options[:offset]
2579
+ end
2580
+
2581
+ it 'should update the limit' do
2582
+ @return.limit.should == @other_options[:limit]
2583
+ end
2584
+
2585
+ it 'should update the order' do
2586
+ @return.order.should == @other_options[:order]
2587
+ end
2588
+
2589
+ it 'should update the unique' do
2590
+ @return.unique?.should == @other_options[:unique]
2591
+ end
2592
+
2593
+ it 'should update the add_reversed' do
2594
+ @return.add_reversed?.should == @other_options[:add_reversed]
2595
+ end
2596
+
2597
+ it 'should update the reload' do
2598
+ @return.reload?.should == @other_options[:reload]
2599
+ end
2600
+ end
2601
+
2602
+ describe 'using extra options' do
2603
+ before :all do
2604
+ @options = { :name => 'Dan Kubb' }
2605
+
2606
+ @return = @query.update(@options)
2607
+ end
2608
+
2609
+ it { @return.should be_kind_of(DataMapper::Query) }
2610
+
2611
+ it { @return.should equal(@original) }
2612
+
2613
+ it 'should update the conditions' do
2614
+ @return.conditions.should == DataMapper::Query::Conditions::Operation.new(
2615
+ :and,
2616
+ DataMapper::Query::Conditions::Comparison.new(:eql,
2617
+ @model.properties[:name],
2618
+ @options[:name]
2619
+ )
2620
+ )
2621
+ #@return.conditions.should == [ [ :eql, @model.properties[:name], @options[:name] ] ]
2622
+ end
2623
+ end
2624
+ end
2625
+ end
2626
+ end