datamapper-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 (192) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -39
  5. data/Manifest.txt +67 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +16 -15
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/dm-core.gemspec +11 -15
  12. data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
  13. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  14. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  15. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  16. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  19. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  20. data/lib/dm-core/adapters.rb +135 -16
  21. data/lib/dm-core/associations/many_to_many.rb +372 -90
  22. data/lib/dm-core/associations/many_to_one.rb +220 -73
  23. data/lib/dm-core/associations/one_to_many.rb +319 -255
  24. data/lib/dm-core/associations/one_to_one.rb +66 -53
  25. data/lib/dm-core/associations/relationship.rb +560 -158
  26. data/lib/dm-core/collection.rb +1104 -381
  27. data/lib/dm-core/core_ext/kernel.rb +12 -0
  28. data/lib/dm-core/core_ext/symbol.rb +10 -0
  29. data/lib/dm-core/identity_map.rb +4 -34
  30. data/lib/dm-core/migrations.rb +1283 -0
  31. data/lib/dm-core/model/descendant_set.rb +81 -0
  32. data/lib/dm-core/model/hook.rb +45 -0
  33. data/lib/dm-core/model/is.rb +32 -0
  34. data/lib/dm-core/model/property.rb +248 -0
  35. data/lib/dm-core/model/relationship.rb +335 -0
  36. data/lib/dm-core/model/scope.rb +90 -0
  37. data/lib/dm-core/model.rb +570 -369
  38. data/lib/dm-core/property.rb +753 -280
  39. data/lib/dm-core/property_set.rb +141 -98
  40. data/lib/dm-core/query/conditions/comparison.rb +814 -0
  41. data/lib/dm-core/query/conditions/operation.rb +247 -0
  42. data/lib/dm-core/query/direction.rb +43 -0
  43. data/lib/dm-core/query/operator.rb +42 -0
  44. data/lib/dm-core/query/path.rb +102 -0
  45. data/lib/dm-core/query/sort.rb +45 -0
  46. data/lib/dm-core/query.rb +974 -492
  47. data/lib/dm-core/repository.rb +147 -107
  48. data/lib/dm-core/resource.rb +644 -429
  49. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  50. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  51. data/lib/dm-core/support/chainable.rb +20 -0
  52. data/lib/dm-core/support/deprecate.rb +12 -0
  53. data/lib/dm-core/support/equalizer.rb +23 -0
  54. data/lib/dm-core/support/logger.rb +13 -0
  55. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  56. data/lib/dm-core/transaction.rb +333 -92
  57. data/lib/dm-core/type.rb +98 -60
  58. data/lib/dm-core/types/boolean.rb +1 -1
  59. data/lib/dm-core/types/discriminator.rb +34 -20
  60. data/lib/dm-core/types/object.rb +7 -4
  61. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  62. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  63. data/lib/dm-core/types/serial.rb +3 -3
  64. data/lib/dm-core/types/text.rb +3 -4
  65. data/lib/dm-core/version.rb +1 -1
  66. data/lib/dm-core.rb +106 -110
  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/model/relationship_spec.rb +924 -0
  80. data/spec/public/model_spec.rb +159 -0
  81. data/spec/public/property_spec.rb +829 -0
  82. data/spec/public/resource_spec.rb +71 -0
  83. data/spec/public/sel_spec.rb +44 -0
  84. data/spec/public/setup_spec.rb +145 -0
  85. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  86. data/spec/public/shared/collection_shared_spec.rb +1723 -0
  87. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  88. data/spec/public/shared/resource_shared_spec.rb +924 -0
  89. data/spec/public/shared/sel_shared_spec.rb +112 -0
  90. data/spec/public/transaction_spec.rb +129 -0
  91. data/spec/public/types/discriminator_spec.rb +130 -0
  92. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  93. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  94. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  95. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  96. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  97. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  99. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  100. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  101. data/spec/semipublic/associations_spec.rb +177 -0
  102. data/spec/semipublic/collection_spec.rb +142 -0
  103. data/spec/semipublic/property_spec.rb +61 -0
  104. data/spec/semipublic/query/conditions_spec.rb +528 -0
  105. data/spec/semipublic/query/path_spec.rb +443 -0
  106. data/spec/semipublic/query_spec.rb +2626 -0
  107. data/spec/semipublic/resource_spec.rb +47 -0
  108. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  109. data/spec/spec.opts +3 -1
  110. data/spec/spec_helper.rb +80 -57
  111. data/tasks/ci.rb +19 -31
  112. data/tasks/dm.rb +43 -48
  113. data/tasks/doc.rb +8 -11
  114. data/tasks/gemspec.rb +5 -5
  115. data/tasks/hoe.rb +15 -16
  116. data/tasks/install.rb +8 -10
  117. metadata +72 -93
  118. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  119. data/lib/dm-core/associations.rb +0 -207
  120. data/lib/dm-core/auto_migrations.rb +0 -105
  121. data/lib/dm-core/dependency_queue.rb +0 -32
  122. data/lib/dm-core/hook.rb +0 -11
  123. data/lib/dm-core/is.rb +0 -16
  124. data/lib/dm-core/logger.rb +0 -232
  125. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  126. data/lib/dm-core/migrator.rb +0 -29
  127. data/lib/dm-core/scope.rb +0 -58
  128. data/lib/dm-core/support/array.rb +0 -13
  129. data/lib/dm-core/support/assertions.rb +0 -8
  130. data/lib/dm-core/support/errors.rb +0 -23
  131. data/lib/dm-core/support/kernel.rb +0 -11
  132. data/lib/dm-core/support/symbol.rb +0 -41
  133. data/lib/dm-core/support.rb +0 -7
  134. data/lib/dm-core/type_map.rb +0 -80
  135. data/lib/dm-core/types.rb +0 -19
  136. data/script/all +0 -4
  137. data/spec/integration/association_spec.rb +0 -1382
  138. data/spec/integration/association_through_spec.rb +0 -203
  139. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  140. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  141. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  142. data/spec/integration/auto_migrations_spec.rb +0 -413
  143. data/spec/integration/collection_spec.rb +0 -1073
  144. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  145. data/spec/integration/dependency_queue_spec.rb +0 -46
  146. data/spec/integration/model_spec.rb +0 -197
  147. data/spec/integration/mysql_adapter_spec.rb +0 -85
  148. data/spec/integration/postgres_adapter_spec.rb +0 -731
  149. data/spec/integration/property_spec.rb +0 -253
  150. data/spec/integration/query_spec.rb +0 -514
  151. data/spec/integration/repository_spec.rb +0 -61
  152. data/spec/integration/resource_spec.rb +0 -513
  153. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  154. data/spec/integration/sti_spec.rb +0 -273
  155. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  156. data/spec/integration/transaction_spec.rb +0 -75
  157. data/spec/integration/type_spec.rb +0 -275
  158. data/spec/lib/logging_helper.rb +0 -18
  159. data/spec/lib/mock_adapter.rb +0 -27
  160. data/spec/lib/model_loader.rb +0 -100
  161. data/spec/lib/publicize_methods.rb +0 -28
  162. data/spec/models/content.rb +0 -16
  163. data/spec/models/vehicles.rb +0 -34
  164. data/spec/models/zoo.rb +0 -48
  165. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  166. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  167. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  168. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  169. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  170. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  171. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  172. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  173. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  174. data/spec/unit/associations/relationship_spec.rb +0 -71
  175. data/spec/unit/associations_spec.rb +0 -242
  176. data/spec/unit/auto_migrations_spec.rb +0 -111
  177. data/spec/unit/collection_spec.rb +0 -182
  178. data/spec/unit/data_mapper_spec.rb +0 -35
  179. data/spec/unit/identity_map_spec.rb +0 -126
  180. data/spec/unit/is_spec.rb +0 -80
  181. data/spec/unit/migrator_spec.rb +0 -33
  182. data/spec/unit/model_spec.rb +0 -321
  183. data/spec/unit/naming_conventions_spec.rb +0 -36
  184. data/spec/unit/property_set_spec.rb +0 -90
  185. data/spec/unit/property_spec.rb +0 -753
  186. data/spec/unit/query_spec.rb +0 -571
  187. data/spec/unit/repository_spec.rb +0 -93
  188. data/spec/unit/resource_spec.rb +0 -649
  189. data/spec/unit/scope_spec.rb +0 -142
  190. data/spec/unit/transaction_spec.rb +0 -493
  191. data/spec/unit/type_map_spec.rb +0 -114
  192. data/spec/unit/type_spec.rb +0 -119
@@ -0,0 +1,1723 @@
1
+ share_examples_for 'A public Collection' do
2
+ before :all do
3
+ %w[ @article_model @article @other @original @articles @other_articles ].each do |ivar|
4
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
5
+ raise "+#{ivar}+ should not be nil in before block" unless instance_variable_get(ivar)
6
+ end
7
+
8
+ @articles.loaded?.should == loaded
9
+ end
10
+
11
+ before :all do
12
+ @no_join = defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.kind_of?(DataMapper::Adapters::InMemoryAdapter) ||
13
+ defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
14
+
15
+ @one_to_many = @articles.kind_of?(DataMapper::Associations::OneToMany::Collection)
16
+ @many_to_many = @articles.kind_of?(DataMapper::Associations::ManyToMany::Collection)
17
+
18
+ @skip = @no_join && @many_to_many
19
+ end
20
+
21
+ before do
22
+ pending if @skip
23
+ end
24
+
25
+ it { @articles.should respond_to(:<<) }
26
+
27
+ describe '#<<' do
28
+ before :all do
29
+ @resource = @article_model.new(:title => 'Title')
30
+
31
+ @return = @articles << @resource
32
+ end
33
+
34
+ it 'should return a Collection' do
35
+ @return.should be_kind_of(DataMapper::Collection)
36
+ end
37
+
38
+ it 'should return self' do
39
+ @return.should equal(@articles)
40
+ end
41
+
42
+ it 'should append one Resource to the Collection' do
43
+ @articles.last.should equal(@resource)
44
+ end
45
+
46
+ it 'should not relate the Resource to the Collection' do
47
+ @resource.collection.should_not equal(@articles)
48
+ end
49
+ end
50
+
51
+ it { @articles.should respond_to(:blank?) }
52
+
53
+ describe '#blank?' do
54
+ describe 'when the collection is empty' do
55
+ it 'should be true' do
56
+ @articles.clear.blank?.should be_true
57
+ end
58
+ end
59
+
60
+ describe 'when the collection is not empty' do
61
+ it 'should be false' do
62
+ @articles.blank?.should be_false
63
+ end
64
+ end
65
+ end
66
+
67
+ it { @articles.should respond_to(:clean?) }
68
+
69
+ describe '#clean?' do
70
+ describe 'with all clean resources in the collection' do
71
+ it 'should return true' do
72
+ @articles.clean?.should be_true
73
+ end
74
+ end
75
+
76
+ describe 'with a dirty resource in the collection' do
77
+ before :all do
78
+ @articles.each { |r| r.content = 'Changed' }
79
+ end
80
+
81
+ it 'should return true' do
82
+ @articles.clean?.should be_false
83
+ end
84
+ end
85
+ end
86
+
87
+ it { @articles.should respond_to(:clear) }
88
+
89
+ describe '#clear' do
90
+ before :all do
91
+ @resources = @articles.entries
92
+
93
+ @return = @articles.clear
94
+ end
95
+
96
+ it 'should return a Collection' do
97
+ @return.should be_kind_of(DataMapper::Collection)
98
+ end
99
+
100
+ it 'should return self' do
101
+ @return.should equal(@articles)
102
+ end
103
+
104
+ it 'should make the Collection empty' do
105
+ @articles.should be_empty
106
+ end
107
+
108
+ it 'should orphan the Resources' do
109
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
110
+ end
111
+ end
112
+
113
+ [ :collect!, :map! ].each do |method|
114
+ it { @articles.should respond_to(method) }
115
+
116
+ describe "##{method}" do
117
+ before :all do
118
+ @resources = @articles.dup.entries
119
+
120
+ @return = @articles.send(method) { |resource| @article_model.new(:title => 'Ignored Title', :content => 'New Content') }
121
+ end
122
+
123
+ it 'should return a Collection' do
124
+ @return.should be_kind_of(DataMapper::Collection)
125
+ end
126
+
127
+ it 'should return self' do
128
+ @return.should equal(@articles)
129
+ end
130
+
131
+ it 'should update the Collection inline' do
132
+ @articles.each { |resource| resource.attributes.only(:title, :content).should == { :title => 'Sample Article', :content => 'New Content' } }
133
+ end
134
+
135
+ it 'should orphan each replaced Resource in the Collection' do
136
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
137
+ end
138
+ end
139
+ end
140
+
141
+ it { @articles.should respond_to(:concat) }
142
+
143
+ describe '#concat' do
144
+ before :all do
145
+ @return = @articles.concat(@other_articles)
146
+ end
147
+
148
+ it 'should return a Collection' do
149
+ @return.should be_kind_of(DataMapper::Collection)
150
+ end
151
+
152
+ it 'should return self' do
153
+ @return.should equal(@articles)
154
+ end
155
+
156
+ it 'should concatenate the two collections' do
157
+ @return.should == [ @article, @other ]
158
+ end
159
+
160
+ it 'should relate each Resource to the Collection' do
161
+ @other_articles.each { |resource| resource.collection.should equal(@articles) }
162
+ end
163
+ end
164
+
165
+ [ :create, :create! ].each do |method|
166
+ it { @articles.should respond_to(method) }
167
+
168
+ describe "##{method}" do
169
+ describe 'when scoped to a property' do
170
+ before :all do
171
+ @return = @resource = @articles.send(method)
172
+ end
173
+
174
+ it 'should return a Resource' do
175
+ @return.should be_kind_of(DataMapper::Resource)
176
+ end
177
+
178
+ it 'should be a saved Resource' do
179
+ @resource.should be_saved
180
+ end
181
+
182
+ it 'should append the Resource to the Collection' do
183
+ @articles.last.should equal(@resource)
184
+ end
185
+
186
+ it 'should use the query conditions to set default values' do
187
+ @resource.title.should == 'Sample Article'
188
+ end
189
+
190
+ it 'should not append a Resource if create fails' do
191
+ pending 'TODO: not sure how to best spec this'
192
+ end
193
+ end
194
+
195
+ describe 'when scoped to the key' do
196
+ before :all do
197
+ @articles = @articles.all(:id => 1)
198
+
199
+ @return = @resource = @articles.send(method)
200
+ end
201
+
202
+ it 'should return a Resource' do
203
+ @return.should be_kind_of(DataMapper::Resource)
204
+ end
205
+
206
+ it 'should be a saved Resource' do
207
+ @resource.should be_saved
208
+ end
209
+
210
+ it 'should append the Resource to the Collection' do
211
+ @articles.last.should equal(@resource)
212
+ end
213
+
214
+ it 'should not use the query conditions to set default values' do
215
+ @resource.id.should_not == 1
216
+ end
217
+
218
+ it 'should not append a Resource if create fails' do
219
+ pending 'TODO: not sure how to best spec this'
220
+ end
221
+ end
222
+
223
+ describe 'when scoped to a property with multiple values' do
224
+ before :all do
225
+ @articles = @articles.all(:content => %w[ Sample Other ])
226
+
227
+ @return = @resource = @articles.send(method)
228
+ end
229
+
230
+ it 'should return a Resource' do
231
+ @return.should be_kind_of(DataMapper::Resource)
232
+ end
233
+
234
+ it 'should be a saved Resource' do
235
+ @resource.should be_saved
236
+ end
237
+
238
+ it 'should append the Resource to the Collection' do
239
+ @articles.last.should equal(@resource)
240
+ end
241
+
242
+ it 'should not use the query conditions to set default values' do
243
+ @resource.content.should be_nil
244
+ end
245
+
246
+ it 'should not append a Resource if create fails' do
247
+ pending 'TODO: not sure how to best spec this'
248
+ end
249
+ end
250
+
251
+ describe 'when scoped with a condition other than eql' do
252
+ before :all do
253
+ @articles = @articles.all(:content.not => 'Sample')
254
+
255
+ @return = @resource = @articles.send(method)
256
+ end
257
+
258
+ it 'should return a Resource' do
259
+ @return.should be_kind_of(DataMapper::Resource)
260
+ end
261
+
262
+ it 'should be a saved Resource' do
263
+ @resource.should be_saved
264
+ end
265
+
266
+ it 'should append the Resource to the Collection' do
267
+ @articles.last.should equal(@resource)
268
+ end
269
+
270
+ it 'should not use the query conditions to set default values' do
271
+ @resource.content.should be_nil
272
+ end
273
+
274
+ it 'should not append a Resource if create fails' do
275
+ pending 'TODO: not sure how to best spec this'
276
+ end
277
+ end
278
+ end
279
+ end
280
+
281
+ it { @articles.should respond_to(:delete) }
282
+
283
+ describe '#delete' do
284
+ describe 'with a Resource within the Collection' do
285
+ before :all do
286
+ @return = @resource = @articles.delete(@article)
287
+ end
288
+
289
+ it 'should return a DataMapper::Resource' do
290
+ @return.should be_kind_of(DataMapper::Resource)
291
+ end
292
+
293
+ it 'should be the expected Resource' do
294
+ # compare keys because FK attributes may have been altered
295
+ # when removing from the Collection
296
+ @resource.key.should == @article.key
297
+ end
298
+
299
+ it 'should remove the Resource from the Collection' do
300
+ @articles.should_not be_include(@resource)
301
+ end
302
+
303
+ it 'should orphan the Resource' do
304
+ @resource.collection.should_not equal(@articles)
305
+ end
306
+ end
307
+
308
+ describe 'with a Resource not within the Collection' do
309
+ before :all do
310
+ @return = @articles.delete(@other)
311
+ end
312
+
313
+ it 'should return nil' do
314
+ @return.should be_nil
315
+ end
316
+ end
317
+ end
318
+
319
+ it { @articles.should respond_to(:delete_at) }
320
+
321
+ describe '#delete_at' do
322
+ describe 'with an offset within the Collection' do
323
+ before :all do
324
+ @return = @resource = @articles.delete_at(0)
325
+ end
326
+
327
+ it 'should return a DataMapper::Resource' do
328
+ @return.should be_kind_of(DataMapper::Resource)
329
+ end
330
+
331
+ it 'should be the expected Resource' do
332
+ @resource.key.should == @article.key
333
+ end
334
+
335
+ it 'should remove the Resource from the Collection' do
336
+ @articles.should_not be_include(@resource)
337
+ end
338
+
339
+ it 'should orphan the Resource' do
340
+ @resource.collection.should_not equal(@articles)
341
+ end
342
+ end
343
+
344
+ describe 'with an offset not within the Collection' do
345
+ before :all do
346
+ @return = @articles.delete_at(1)
347
+ end
348
+
349
+ it 'should return nil' do
350
+ @return.should be_nil
351
+ end
352
+ end
353
+ end
354
+
355
+ it { @articles.should respond_to(:delete_if) }
356
+
357
+ describe '#delete_if' do
358
+ describe 'with a block that matches a Resource in the Collection' do
359
+ before :all do
360
+ @resources = @articles.dup.entries
361
+
362
+ @return = @articles.delete_if { true }
363
+ end
364
+
365
+ it 'should return a Collection' do
366
+ @return.should be_kind_of(DataMapper::Collection)
367
+ end
368
+
369
+ it 'should return self' do
370
+ @return.should equal(@articles)
371
+ end
372
+
373
+ it 'should remove the Resources from the Collection' do
374
+ @resources.each { |resource| @articles.should_not be_include(resource) }
375
+ end
376
+
377
+ it 'should orphan the Resources' do
378
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
379
+ end
380
+ end
381
+
382
+ describe 'with a block that does not match a Resource in the Collection' do
383
+ before :all do
384
+ @resources = @articles.dup.entries
385
+
386
+ @return = @articles.delete_if { false }
387
+ end
388
+
389
+ it 'should return a Collection' do
390
+ @return.should be_kind_of(DataMapper::Collection)
391
+ end
392
+
393
+ it 'should return self' do
394
+ @return.should equal(@articles)
395
+ end
396
+
397
+ it 'should not modify the Collection' do
398
+ @articles.should == @resources
399
+ end
400
+ end
401
+ end
402
+
403
+ [ :destroy, :destroy! ].each do |method|
404
+ it { @articles.should respond_to(method) }
405
+
406
+ describe "##{method}" do
407
+ describe 'on a normal collection' do
408
+ before :all do
409
+ @return = @articles.send(method)
410
+ end
411
+
412
+ it 'should return true' do
413
+ @return.should be_true
414
+ end
415
+
416
+ it 'should remove the Resources from the datasource' do
417
+ @article_model.all(:title => 'Sample Article').should be_empty
418
+ end
419
+
420
+ it 'should clear the collection' do
421
+ @articles.should be_empty
422
+ end
423
+ end
424
+
425
+ describe 'on a limited collection' do
426
+ before :all do
427
+ @other = @articles.create.freeze
428
+ @limited = @articles.all(:limit => 1)
429
+
430
+ @return = @limited.send(method)
431
+ end
432
+
433
+ it 'should return true' do
434
+ @return.should be_true
435
+ end
436
+
437
+ it 'should remove the Resources from the datasource' do
438
+ @article_model.all(:title => 'Sample Article').should == [ @other ]
439
+ end
440
+
441
+ it 'should clear the collection' do
442
+ @limited.should be_empty
443
+ end
444
+
445
+ it 'should not destroy the other Resource' do
446
+ @article_model.get(*@other.key).should_not be_nil
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ it { @articles.should respond_to(:dirty?) }
453
+
454
+ describe '#dirty?' do
455
+ describe 'with all clean resources in the collection' do
456
+ it 'should return false' do
457
+ @articles.dirty?.should be_false
458
+ end
459
+ end
460
+
461
+ describe 'with a dirty resource in the collection' do
462
+ before :all do
463
+ @articles.each { |r| r.content = 'Changed' }
464
+ end
465
+
466
+ it 'should return true' do
467
+ @articles.dirty?.should be_true
468
+ end
469
+ end
470
+ end
471
+
472
+ it { @articles.should respond_to(:insert) }
473
+
474
+ describe '#insert' do
475
+ before :all do
476
+ @resources = @other_articles
477
+
478
+ @return = @articles.insert(0, *@resources)
479
+ end
480
+
481
+ it 'should return a Collection' do
482
+ @return.should be_kind_of(DataMapper::Collection)
483
+ end
484
+
485
+ it 'should return self' do
486
+ @return.should equal(@articles)
487
+ end
488
+
489
+ it 'should insert one or more Resources at a given offset' do
490
+ @articles.should == @resources + [ @article ]
491
+ end
492
+
493
+ it 'should relate the Resources to the Collection' do
494
+ @resources.each { |resource| resource.collection.should equal(@articles) }
495
+ end
496
+ end
497
+
498
+ it { @articles.should respond_to(:inspect) }
499
+
500
+ describe '#inspect' do
501
+ before :all do
502
+ @copy = @articles.dup
503
+ @copy << @article_model.new(:title => 'Ignored Title', :content => 'Other Article')
504
+
505
+ @return = @copy.inspect
506
+ end
507
+
508
+ it { @return.should match(/\A\[.*\]\z/) }
509
+
510
+ it { @return.should match(/\bid=#{@article.id}\b/) }
511
+ it { @return.should match(/\bid=nil\b/) }
512
+
513
+ it { @return.should match(/\btitle=\"Sample Article\"\s/) }
514
+ it { @return.should_not match(/\btitle=\"Ignored Title\"\s/) }
515
+ it { @return.should match(/\bcontent=\"Other Article\"\s/) }
516
+ end
517
+
518
+ it { @articles.should respond_to(:new) }
519
+
520
+ describe '#new' do
521
+ describe 'when scoped to a property' do
522
+ before :all do
523
+ @return = @resource = @articles.new
524
+ end
525
+
526
+ it 'should return a Resource' do
527
+ @return.should be_kind_of(DataMapper::Resource)
528
+ end
529
+
530
+ it 'should be a new Resource' do
531
+ @resource.should be_new
532
+ end
533
+
534
+ it 'should append the Resource to the Collection' do
535
+ @articles.last.should equal(@resource)
536
+ end
537
+
538
+ it 'should use the query conditions to set default values' do
539
+ @resource.title.should == 'Sample Article'
540
+ end
541
+ end
542
+
543
+ describe 'when scoped to the key' do
544
+ before :all do
545
+ @articles = @articles.all(:id => 1)
546
+
547
+ @return = @resource = @articles.new
548
+ end
549
+
550
+ it 'should return a Resource' do
551
+ @return.should be_kind_of(DataMapper::Resource)
552
+ end
553
+
554
+ it 'should be a new Resource' do
555
+ @resource.should be_new
556
+ end
557
+
558
+ it 'should append the Resource to the Collection' do
559
+ @articles.last.should equal(@resource)
560
+ end
561
+
562
+ it 'should not use the query conditions to set default values' do
563
+ @resource.id.should be_nil
564
+ end
565
+ end
566
+
567
+ describe 'when scoped to a property with multiple values' do
568
+ before :all do
569
+ @articles = @articles.all(:content => %w[ Sample Other ])
570
+
571
+ @return = @resource = @articles.new
572
+ end
573
+
574
+ it 'should return a Resource' do
575
+ @return.should be_kind_of(DataMapper::Resource)
576
+ end
577
+
578
+ it 'should be a new Resource' do
579
+ @resource.should be_new
580
+ end
581
+
582
+ it 'should append the Resource to the Collection' do
583
+ @articles.last.should equal(@resource)
584
+ end
585
+
586
+ it 'should not use the query conditions to set default values' do
587
+ @resource.content.should be_nil
588
+ end
589
+ end
590
+
591
+ describe 'when scoped with a condition other than eql' do
592
+ before :all do
593
+ @articles = @articles.all(:content.not => 'Sample')
594
+
595
+ @return = @resource = @articles.new
596
+ end
597
+
598
+ it 'should return a Resource' do
599
+ @return.should be_kind_of(DataMapper::Resource)
600
+ end
601
+
602
+ it 'should be a new Resource' do
603
+ @resource.should be_new
604
+ end
605
+
606
+ it 'should append the Resource to the Collection' do
607
+ @articles.last.should equal(@resource)
608
+ end
609
+
610
+ it 'should not use the query conditions to set default values' do
611
+ @resource.content.should be_nil
612
+ end
613
+ end
614
+ end
615
+
616
+ it { @articles.should respond_to(:pop) }
617
+
618
+ describe '#pop' do
619
+ before :all do
620
+ @new = @articles.create(:title => 'Sample Article') # TODO: freeze @new
621
+ end
622
+
623
+ describe 'with no arguments' do
624
+ before :all do
625
+ @return = @articles.pop
626
+ end
627
+
628
+ it 'should return a Resource' do
629
+ @return.should be_kind_of(DataMapper::Resource)
630
+ end
631
+
632
+ it 'should be the last Resource in the Collection' do
633
+ @return.should == @new
634
+ end
635
+
636
+ it 'should remove the Resource from the Collection' do
637
+ @articles.should_not be_include(@new)
638
+ end
639
+
640
+ it 'should orphan the Resource' do
641
+ @return.collection.should_not equal(@articles)
642
+ end
643
+ end
644
+
645
+ if RUBY_VERSION >= '1.8.7'
646
+ describe 'with a limit specified' do
647
+ before :all do
648
+ @return = @articles.pop(1)
649
+ end
650
+
651
+ it 'should return an Array' do
652
+ @return.should be_kind_of(Array)
653
+ end
654
+
655
+ it 'should return the expected Resources' do
656
+ @return.should == [ @new ]
657
+ end
658
+
659
+ it 'should remove the Resource from the Collection' do
660
+ @articles.should_not be_include(@new)
661
+ end
662
+
663
+ it 'should orphan the Resource' do
664
+ @return.each { |resource| resource.collection.should_not equal(@articles) }
665
+ end
666
+ end
667
+ end
668
+ end
669
+
670
+ it { @articles.should respond_to(:push) }
671
+
672
+ describe '#push' do
673
+ before :all do
674
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
675
+
676
+ @return = @articles.push(*@resources)
677
+ end
678
+
679
+ it 'should return a Collection' do
680
+ @return.should be_kind_of(DataMapper::Collection)
681
+ end
682
+
683
+ it 'should return self' do
684
+ @return.should equal(@articles)
685
+ end
686
+
687
+ it 'should append the Resources to the Collection' do
688
+ @articles.should == [ @article ] + @resources
689
+ end
690
+
691
+ it 'should relate the Resources to the Collection' do
692
+ @resources.each { |resource| resource.collection.should equal(@articles) }
693
+ end
694
+ end
695
+
696
+ it { @articles.should respond_to(:reject!) }
697
+
698
+ describe '#reject!' do
699
+ describe 'with a block that matches a Resource in the Collection' do
700
+ before :all do
701
+ @resources = @articles.dup.entries
702
+
703
+ @return = @articles.reject! { true }
704
+ end
705
+
706
+ it 'should return a Collection' do
707
+ @return.should be_kind_of(DataMapper::Collection)
708
+ end
709
+
710
+ it 'should return self' do
711
+ @return.should equal(@articles)
712
+ end
713
+
714
+ it 'should remove the Resources from the Collection' do
715
+ @resources.each { |resource| @articles.should_not be_include(resource) }
716
+ end
717
+
718
+ it 'should orphan the Resources' do
719
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
720
+ end
721
+ end
722
+
723
+ describe 'with a block that does not match a Resource in the Collection' do
724
+ before :all do
725
+ @resources = @articles.dup.entries
726
+
727
+ @return = @articles.reject! { false }
728
+ end
729
+
730
+ it 'should return nil' do
731
+ @return.should be_nil
732
+ end
733
+
734
+ it 'should not modify the Collection' do
735
+ @articles.should == @resources
736
+ end
737
+ end
738
+ end
739
+
740
+ it { @articles.should respond_to(:reload) }
741
+
742
+ describe '#reload' do
743
+ describe 'with no arguments' do
744
+ before :all do
745
+ @resources = @articles.dup.entries
746
+
747
+ @return = @collection = @articles.reload
748
+ end
749
+
750
+ # FIXME: this is spec order dependent, move this into a helper method
751
+ # and execute in the before :all block
752
+ unless loaded
753
+ it 'should not be a kicker' do
754
+ pending do
755
+ @articles.should_not be_loaded
756
+ end
757
+ end
758
+ end
759
+
760
+ it 'should return a Collection' do
761
+ @return.should be_kind_of(DataMapper::Collection)
762
+ end
763
+
764
+ it 'should return self' do
765
+ @return.should equal(@articles)
766
+ end
767
+
768
+ { :title => true, :content => false }.each do |attribute, expected|
769
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
770
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
771
+ end
772
+ end
773
+ end
774
+
775
+ describe 'with a Hash query' do
776
+ before :all do
777
+ @resources = @articles.dup.entries
778
+
779
+ @return = @collection = @articles.reload(:fields => [ :content ]) # :title is a default field
780
+ end
781
+
782
+ # FIXME: this is spec order dependent, move this into a helper method
783
+ # and execute in the before :all block
784
+ unless loaded
785
+ it 'should not be a kicker' do
786
+ pending do
787
+ @articles.should_not be_loaded
788
+ end
789
+ end
790
+ end
791
+
792
+ it 'should return a Collection' do
793
+ @return.should be_kind_of(DataMapper::Collection)
794
+ end
795
+
796
+ it 'should return self' do
797
+ @return.should equal(@articles)
798
+ end
799
+
800
+ { :id => true, :content => true, :title => true }.each do |attribute, expected|
801
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
802
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
803
+ end
804
+ end
805
+ end
806
+
807
+ describe 'with a Query' do
808
+ before :all do
809
+ @query = DataMapper::Query.new(@repository, @article_model, :fields => [ :content ]) # :title is an original field
810
+
811
+ @return = @collection = @articles.reload(@query)
812
+ end
813
+
814
+ # FIXME: this is spec order dependent, move this into a helper method
815
+ # and execute in the before :all block
816
+ unless loaded
817
+ it 'should not be a kicker' do
818
+ pending do
819
+ @articles.should_not be_loaded
820
+ end
821
+ end
822
+ end
823
+
824
+ it 'should return a Collection' do
825
+ @return.should be_kind_of(DataMapper::Collection)
826
+ end
827
+
828
+ it 'should return self' do
829
+ @return.should equal(@articles)
830
+ end
831
+
832
+ { :id => true, :content => true, :title => loaded }.each do |attribute, expected|
833
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
834
+ pending_if "TODO: #{@articles.class}#reload should not be a kicker", @one_to_many && loaded == false && attribute == :title do
835
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
836
+ end
837
+ end
838
+ end
839
+ end
840
+ end
841
+
842
+ it { @articles.should respond_to(:replace) }
843
+
844
+ describe '#replace' do
845
+ describe 'when provided an Array of Resources' do
846
+ before :all do
847
+ @resources = @articles.dup.entries
848
+
849
+ @return = @articles.replace(@other_articles)
850
+ end
851
+
852
+ it 'should return a Collection' do
853
+ @return.should be_kind_of(DataMapper::Collection)
854
+ end
855
+
856
+ it 'should return self' do
857
+ @return.should equal(@articles)
858
+ end
859
+
860
+ it 'should update the Collection with new Resources' do
861
+ @articles.should == @other_articles
862
+ end
863
+
864
+ it 'should relate each Resource added to the Collection' do
865
+ @articles.each { |resource| resource.collection.should equal(@articles) }
866
+ end
867
+
868
+ it 'should orphan each Resource removed from the Collection' do
869
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
870
+ end
871
+ end
872
+
873
+ describe 'when provided an Array of Hashes' do
874
+ before :all do
875
+ @array = [ { :content => 'From Hash' } ].freeze
876
+
877
+ @return = @articles.replace(@array)
878
+ end
879
+
880
+ it 'should return a Collection' do
881
+ @return.should be_kind_of(DataMapper::Collection)
882
+ end
883
+
884
+ it 'should return self' do
885
+ @return.should equal(@articles)
886
+ end
887
+
888
+ it 'should initialize a Resource' do
889
+ @return.first.should be_kind_of(DataMapper::Resource)
890
+ end
891
+
892
+ it 'should be a new Resource' do
893
+ @return.first.should be_new
894
+ end
895
+
896
+ it 'should be a Resource with attributes matching the Hash' do
897
+ @return.first.attributes.only(*@array.first.keys).should == @array.first
898
+ end
899
+ end
900
+ end
901
+
902
+ it { @articles.should respond_to(:reverse!) }
903
+
904
+ describe '#reverse!' do
905
+ before :all do
906
+ @query = @articles.query
907
+
908
+ @new = @articles.create(:title => 'Sample Article')
909
+
910
+ @return = @articles.reverse!
911
+ end
912
+
913
+ it 'should return a Collection' do
914
+ @return.should be_kind_of(DataMapper::Collection)
915
+ end
916
+
917
+ it 'should return self' do
918
+ @return.should equal(@articles)
919
+ end
920
+
921
+ it 'should return a Collection with reversed entries' do
922
+ @return.should == [ @new, @article ]
923
+ end
924
+
925
+ it 'should return a Query that equal to the original' do
926
+ @return.query.should equal(@query)
927
+ end
928
+ end
929
+
930
+ [ :save, :save! ].each do |method|
931
+ it { @articles.should respond_to(method) }
932
+
933
+ describe "##{method}" do
934
+ describe 'when Resources are not saved' do
935
+ before :all do
936
+ @articles.new(:title => 'New Article', :content => 'New Article')
937
+
938
+ @return = @articles.send(method)
939
+ end
940
+
941
+ it 'should return true' do
942
+ @return.should be_true
943
+ end
944
+
945
+ it 'should save each Resource' do
946
+ @articles.each { |resource| resource.should be_saved }
947
+ end
948
+ end
949
+
950
+ describe 'when Resources have been orphaned' do
951
+ before :all do
952
+ @resources = @articles.entries
953
+ @articles.replace([])
954
+
955
+ @return = @articles.send(method)
956
+ end
957
+
958
+ it 'should return true' do
959
+ @return.should be_true
960
+ end
961
+
962
+ it 'should orphan the Resources' do
963
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
964
+ end
965
+ end
966
+ end
967
+ end
968
+
969
+ it { @articles.should respond_to(:shift) }
970
+
971
+ describe '#shift' do
972
+ describe 'with no arguments' do
973
+ before :all do
974
+ @return = @articles.shift
975
+ end
976
+
977
+ it 'should return a Resource' do
978
+ @return.should be_kind_of(DataMapper::Resource)
979
+ end
980
+
981
+ it 'should be the first Resource in the Collection' do
982
+ @return.key.should == @article.key
983
+ end
984
+
985
+ it 'should remove the Resource from the Collection' do
986
+ @articles.should_not be_include(@return)
987
+ end
988
+
989
+ it 'should orphan the Resource' do
990
+ @return.collection.should_not equal(@articles)
991
+ end
992
+ end
993
+
994
+ if RUBY_VERSION >= '1.8.7'
995
+ describe 'with a limit specified' do
996
+ before :all do
997
+ @return = @articles.shift(1)
998
+ end
999
+
1000
+ it 'should return an Array' do
1001
+ @return.should be_kind_of(Array)
1002
+ end
1003
+
1004
+ it 'should return the expected Resources' do
1005
+ @return.size.should == 1
1006
+ @return.first.key.should == @article.key
1007
+ end
1008
+
1009
+ it 'should remove the Resource from the Collection' do
1010
+ @articles.should_not be_include(@article)
1011
+ end
1012
+
1013
+ it 'should orphan the Resource' do
1014
+ @return.each { |resource| resource.collection.should_not equal(@articles) }
1015
+ end
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ it { @articles.should respond_to(:slice!) }
1021
+
1022
+ describe '#slice!' do
1023
+ before :all do
1024
+ 1.upto(10) { |number| @articles.create(:content => "Article #{number}") }
1025
+
1026
+ @copy = @articles.dup
1027
+ end
1028
+
1029
+ describe 'with a positive offset' do
1030
+ before :all do
1031
+ unless @skip
1032
+ @return = @resource = @articles.slice!(0)
1033
+ end
1034
+ end
1035
+
1036
+ it 'should return a Resource' do
1037
+ @return.should be_kind_of(DataMapper::Resource)
1038
+ end
1039
+
1040
+ it 'should return expected Resource' do
1041
+ @return.key.should == @article.key
1042
+ end
1043
+
1044
+ it 'should return the same as Array#slice!' do
1045
+ @return.should == @copy.entries.slice!(0)
1046
+ end
1047
+
1048
+ it 'should remove the Resource from the Collection' do
1049
+ @articles.should_not be_include(@resource)
1050
+ end
1051
+
1052
+ it 'should orphan the Resource' do
1053
+ @resource.collection.should_not equal(@articles)
1054
+ end
1055
+ end
1056
+
1057
+ describe 'with a positive offset and length' do
1058
+ before :all do
1059
+ unless @skip
1060
+ @return = @resources = @articles.slice!(5, 5)
1061
+ end
1062
+ end
1063
+
1064
+ it 'should return a Collection' do
1065
+ @return.should be_kind_of(DataMapper::Collection)
1066
+ end
1067
+
1068
+ it 'should return the expected Resource' do
1069
+ @return.should == @copy.entries.slice!(5, 5)
1070
+ end
1071
+
1072
+ it 'should remove the Resources from the Collection' do
1073
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1074
+ end
1075
+
1076
+ it 'should orphan the Resources' do
1077
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1078
+ end
1079
+
1080
+ it 'should scope the Collection' do
1081
+ @resources.reload.should == @copy.entries.slice!(5, 5)
1082
+ end
1083
+ end
1084
+
1085
+ describe 'with a positive range' do
1086
+ before :all do
1087
+ unless @skip
1088
+ @return = @resources = @articles.slice!(5..10)
1089
+ end
1090
+ end
1091
+
1092
+ it 'should return a Collection' do
1093
+ @return.should be_kind_of(DataMapper::Collection)
1094
+ end
1095
+
1096
+ it 'should return the expected Resources' do
1097
+ @return.should == @copy.entries.slice!(5..10)
1098
+ end
1099
+
1100
+ it 'should remove the Resources from the Collection' do
1101
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1102
+ end
1103
+
1104
+ it 'should orphan the Resources' do
1105
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1106
+ end
1107
+
1108
+ it 'should scope the Collection' do
1109
+ @resources.reload.should == @copy.entries.slice!(5..10)
1110
+ end
1111
+ end
1112
+
1113
+ describe 'with a negative offset' do
1114
+ before :all do
1115
+ unless @skip
1116
+ @return = @resource = @articles.slice!(-1)
1117
+ end
1118
+ end
1119
+
1120
+ it 'should return a Resource' do
1121
+ @return.should be_kind_of(DataMapper::Resource)
1122
+ end
1123
+
1124
+ it 'should return expected Resource' do
1125
+ @return.should == @copy.entries.slice!(-1)
1126
+ end
1127
+
1128
+ it 'should remove the Resource from the Collection' do
1129
+ @articles.should_not be_include(@resource)
1130
+ end
1131
+
1132
+ it 'should orphan the Resource' do
1133
+ @resource.collection.should_not equal(@articles)
1134
+ end
1135
+ end
1136
+
1137
+ describe 'with a negative offset and length' do
1138
+ before :all do
1139
+ unless @skip
1140
+ @return = @resources = @articles.slice!(-5, 5)
1141
+ end
1142
+ end
1143
+
1144
+ it 'should return a Collection' do
1145
+ @return.should be_kind_of(DataMapper::Collection)
1146
+ end
1147
+
1148
+ it 'should return the expected Resources' do
1149
+ @return.should == @copy.entries.slice!(-5, 5)
1150
+ end
1151
+
1152
+ it 'should remove the Resources from the Collection' do
1153
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1154
+ end
1155
+
1156
+ it 'should orphan the Resources' do
1157
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1158
+ end
1159
+
1160
+ it 'should scope the Collection' do
1161
+ @resources.reload.should == @copy.entries.slice!(-5, 5)
1162
+ end
1163
+ end
1164
+
1165
+ describe 'with a negative range' do
1166
+ before :all do
1167
+ unless @skip
1168
+ @return = @resources = @articles.slice!(-3..-2)
1169
+ end
1170
+ end
1171
+
1172
+ it 'should return a Collection' do
1173
+ @return.should be_kind_of(DataMapper::Collection)
1174
+ end
1175
+
1176
+ it 'should return the expected Resources' do
1177
+ @return.should == @copy.entries.slice!(-3..-2)
1178
+ end
1179
+
1180
+ it 'should remove the Resources from the Collection' do
1181
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1182
+ end
1183
+
1184
+ it 'should orphan the Resources' do
1185
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1186
+ end
1187
+
1188
+ it 'should scope the Collection' do
1189
+ @resources.reload.should == @copy.entries.slice!(-3..-2)
1190
+ end
1191
+ end
1192
+
1193
+ describe 'with an offset not within the Collection' do
1194
+ before :all do
1195
+ unless @skip
1196
+ @return = @articles.slice!(12)
1197
+ end
1198
+ end
1199
+
1200
+ it 'should return nil' do
1201
+ @return.should be_nil
1202
+ end
1203
+ end
1204
+
1205
+ describe 'with an offset and length not within the Collection' do
1206
+ before :all do
1207
+ unless @skip
1208
+ @return = @articles.slice!(12, 1)
1209
+ end
1210
+ end
1211
+
1212
+ it 'should return nil' do
1213
+ @return.should be_nil
1214
+ end
1215
+ end
1216
+
1217
+ describe 'with a range not within the Collection' do
1218
+ before :all do
1219
+ unless @skip
1220
+ @return = @articles.slice!(12..13)
1221
+ end
1222
+ end
1223
+
1224
+ it 'should return nil' do
1225
+ @return.should be_nil
1226
+ end
1227
+ end
1228
+ end
1229
+
1230
+ it { @articles.should respond_to(:sort!) }
1231
+
1232
+ describe '#sort!' do
1233
+ describe 'without a block' do
1234
+ before :all do
1235
+ @return = @articles.unshift(@other).sort!
1236
+ end
1237
+
1238
+ it 'should return a Collection' do
1239
+ @return.should be_kind_of(DataMapper::Collection)
1240
+ end
1241
+
1242
+ it 'should return self' do
1243
+ @return.should equal(@articles)
1244
+ end
1245
+
1246
+ it 'should modify and sort the Collection using default sort order' do
1247
+ @articles.should == [ @article, @other ]
1248
+ end
1249
+ end
1250
+
1251
+ describe 'with a block' do
1252
+ before :all do
1253
+ @return = @articles.unshift(@other).sort! { |a_resource, b_resource| b_resource.id <=> a_resource.id }
1254
+ end
1255
+
1256
+ it 'should return a Collection' do
1257
+ @return.should be_kind_of(DataMapper::Collection)
1258
+ end
1259
+
1260
+ it 'should return self' do
1261
+ @return.should equal(@articles)
1262
+ end
1263
+
1264
+ it 'should modify and sort the Collection using supplied block' do
1265
+ @articles.should == [ @other, @article ]
1266
+ end
1267
+ end
1268
+ end
1269
+
1270
+ [ :splice, :[]= ].each do |method|
1271
+ it { @articles.should respond_to(method) }
1272
+
1273
+ describe "##{method}" do
1274
+ before :all do
1275
+ unless @skip
1276
+ orphans = (1..10).map do |number|
1277
+ articles = @articles.dup
1278
+ articles.create(:content => "Article #{number}")
1279
+ articles.pop # remove the article from the tail
1280
+ end
1281
+
1282
+ @articles.unshift(*orphans.first(5))
1283
+ @articles.concat(orphans.last(5))
1284
+
1285
+ unless loaded
1286
+ @articles.should_not be_loaded
1287
+ end
1288
+
1289
+ @copy = @articles.dup
1290
+ @new = @article_model.new(:content => 'New Article')
1291
+ end
1292
+ end
1293
+
1294
+ describe 'with a positive offset and a Resource' do
1295
+ before :all do
1296
+ rescue_if 'TODO', @skip do
1297
+ @original = @copy[1]
1298
+ @original.collection.should equal(@articles)
1299
+
1300
+ @return = @resource = @articles.send(method, 1, @new)
1301
+ end
1302
+ end
1303
+
1304
+ should_not_be_a_kicker
1305
+
1306
+ it 'should return a Resource' do
1307
+ @return.should be_kind_of(DataMapper::Resource)
1308
+ end
1309
+
1310
+ it 'should return expected Resource' do
1311
+ @return.should equal(@new)
1312
+ end
1313
+
1314
+ it 'should return the same as Array#[]=' do
1315
+ @return.should == @copy.entries[1] = @new
1316
+ end
1317
+
1318
+ it 'should include the Resource in the Collection' do
1319
+ @articles.should be_include(@resource)
1320
+ end
1321
+
1322
+ it 'should relate the Resource to the Collection' do
1323
+ @resource.collection.should equal(@articles)
1324
+ end
1325
+
1326
+ it 'should orphan the original Resource' do
1327
+ @original.collection.should_not equal(@articles)
1328
+ end
1329
+ end
1330
+
1331
+ describe 'with a positive offset and length and a Resource' do
1332
+ before :all do
1333
+ rescue_if 'TODO', @skip do
1334
+ @original = @copy[2]
1335
+ @original.collection.should equal(@articles)
1336
+
1337
+ @return = @resource = @articles.send(method, 2, 1, @new)
1338
+ end
1339
+ end
1340
+
1341
+ should_not_be_a_kicker
1342
+
1343
+ it 'should return a Resource' do
1344
+ @return.should be_kind_of(DataMapper::Resource)
1345
+ end
1346
+
1347
+ it 'should return the expected Resource' do
1348
+ @return.should equal(@new)
1349
+ end
1350
+
1351
+ it 'should return the same as Array#[]=' do
1352
+ @return.should == @copy.entries[2, 1] = @new
1353
+ end
1354
+
1355
+ it 'should include the Resource in the Collection' do
1356
+ @articles.should be_include(@resource)
1357
+ end
1358
+
1359
+ it 'should orphan the original Resource' do
1360
+ @original.collection.should_not equal(@articles)
1361
+ end
1362
+ end
1363
+
1364
+ describe 'with a positive range and a Resource' do
1365
+ before :all do
1366
+ rescue_if 'TODO', @skip do
1367
+ @originals = @copy.values_at(2..3)
1368
+ @originals.each { |resource| resource.collection.should equal(@articles) }
1369
+
1370
+ @return = @resource = @articles.send(method, 2..3, @new)
1371
+ end
1372
+ end
1373
+
1374
+ should_not_be_a_kicker
1375
+
1376
+ it 'should return a Resource' do
1377
+ @return.should be_kind_of(DataMapper::Resource)
1378
+ end
1379
+
1380
+ it 'should return the expected Resources' do
1381
+ @return.should equal(@new)
1382
+ end
1383
+
1384
+ it 'should return the same as Array#[]=' do
1385
+ @return.should == @copy.entries[2..3] = @new
1386
+ end
1387
+
1388
+ it 'should include the Resource in the Collection' do
1389
+ @articles.should be_include(@resource)
1390
+ end
1391
+
1392
+ it 'should orphan the original Resources' do
1393
+ @originals.each { |resource| resource.collection.should_not equal(@articles) }
1394
+ end
1395
+ end
1396
+
1397
+ describe 'with a negative offset and a Resource' do
1398
+ before :all do
1399
+ rescue_if 'TODO', @skip do
1400
+ @original = @copy[-1]
1401
+ @original.collection.should equal(@articles)
1402
+
1403
+ @return = @resource = @articles.send(method, -1, @new)
1404
+ end
1405
+ end
1406
+
1407
+ should_not_be_a_kicker
1408
+
1409
+ it 'should return a Resource' do
1410
+ @return.should be_kind_of(DataMapper::Resource)
1411
+ end
1412
+
1413
+ it 'should return expected Resource' do
1414
+ @return.should equal(@new)
1415
+ end
1416
+
1417
+ it 'should return the same as Array#[]=' do
1418
+ @return.should == @copy.entries[-1] = @new
1419
+ end
1420
+
1421
+ it 'should include the Resource in the Collection' do
1422
+ @articles.should be_include(@resource)
1423
+ end
1424
+
1425
+ it 'should relate the Resource to the Collection' do
1426
+ @resource.collection.should equal(@articles)
1427
+ end
1428
+
1429
+ it 'should orphan the original Resource' do
1430
+ @original.collection.should_not equal(@articles)
1431
+ end
1432
+ end
1433
+
1434
+ describe 'with a negative offset and length and a Resource' do
1435
+ before :all do
1436
+ rescue_if 'TODO', @skip do
1437
+ @original = @copy[-2]
1438
+ @original.collection.should equal(@articles)
1439
+
1440
+ @return = @resource = @articles.send(method, -2, 1, @new)
1441
+ end
1442
+ end
1443
+
1444
+ should_not_be_a_kicker
1445
+
1446
+ it 'should return a Resource' do
1447
+ @return.should be_kind_of(DataMapper::Resource)
1448
+ end
1449
+
1450
+ it 'should return the expected Resource' do
1451
+ @return.should equal(@new)
1452
+ end
1453
+
1454
+ it 'should return the same as Array#[]=' do
1455
+ @return.should == @copy.entries[-2, 1] = @new
1456
+ end
1457
+
1458
+ it 'should include the Resource in the Collection' do
1459
+ @articles.should be_include(@resource)
1460
+ end
1461
+
1462
+ it 'should orphan the original Resource' do
1463
+ @original.collection.should_not equal(@articles)
1464
+ end
1465
+ end
1466
+
1467
+ describe 'with a negative range and a Resource' do
1468
+ before :all do
1469
+ rescue_if 'TODO', @skip do
1470
+ @originals = @articles.values_at(-3..-2)
1471
+ @originals.each { |resource| resource.collection.should equal(@articles) }
1472
+
1473
+ @return = @resource = @articles.send(method, -3..-2, @new)
1474
+ end
1475
+ end
1476
+
1477
+ should_not_be_a_kicker
1478
+
1479
+ it 'should return a Resource' do
1480
+ @return.should be_kind_of(DataMapper::Resource)
1481
+ end
1482
+
1483
+ it 'should return the expected Resources' do
1484
+ @return.should equal(@new)
1485
+ end
1486
+
1487
+ it 'should return the same as Array#[]=' do
1488
+ @return.should == @copy.entries[-3..-2] = @new
1489
+ end
1490
+
1491
+ it 'should include the Resource in the Collection' do
1492
+ @articles.should be_include(@resource)
1493
+ end
1494
+
1495
+ it 'should orphan the original Resources' do
1496
+ @originals.each { |resource| resource.collection.should_not equal(@articles) }
1497
+ end
1498
+ end
1499
+ end
1500
+ end
1501
+
1502
+ describe '#[]=' do
1503
+ describe 'when swapping resources' do
1504
+ before :all do
1505
+ rescue_if 'TODO', @skip do
1506
+ @articles.create(:content => 'Another Article')
1507
+
1508
+ @entries = @articles.entries
1509
+
1510
+ @articles[0], @articles[1] = @articles[1], @articles[0]
1511
+ end
1512
+ end
1513
+
1514
+ it 'should include the Resource in the Collection' do
1515
+ @articles.should == @entries.reverse
1516
+ end
1517
+
1518
+ it 'should relate the Resource to the Collection' do
1519
+ @articles.each { |resource| resource.collection.should equal(@articles) }
1520
+ end
1521
+ end
1522
+ end
1523
+
1524
+ it { @articles.should respond_to(:unshift) }
1525
+
1526
+ describe '#unshift' do
1527
+ before :all do
1528
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
1529
+
1530
+ @return = @articles.unshift(*@resources)
1531
+ end
1532
+
1533
+ it 'should return a Collection' do
1534
+ @return.should be_kind_of(DataMapper::Collection)
1535
+ end
1536
+
1537
+ it 'should return self' do
1538
+ @return.should equal(@articles)
1539
+ end
1540
+
1541
+ it 'should prepend the Resources to the Collection' do
1542
+ @articles.should == @resources + [ @article ]
1543
+ end
1544
+
1545
+ it 'should relate the Resources to the Collection' do
1546
+ @resources.each { |resource| resource.collection.should equal(@articles) }
1547
+ end
1548
+ end
1549
+
1550
+ [ :update, :update! ].each do |method|
1551
+ it { @articles.should respond_to(method) }
1552
+
1553
+ describe "##{method}" do
1554
+ describe 'with no arguments' do
1555
+ before :all do
1556
+ @return = @articles.send(method)
1557
+ end
1558
+
1559
+ if method == :update!
1560
+ should_not_be_a_kicker
1561
+ end
1562
+
1563
+ it 'should return true' do
1564
+ @return.should be_true
1565
+ end
1566
+ end
1567
+
1568
+ describe 'with attributes' do
1569
+ before :all do
1570
+ @attributes = { :title => 'Updated Title' }
1571
+
1572
+ @return = @articles.send(method, @attributes)
1573
+ end
1574
+
1575
+ if method == :update!
1576
+ # FIXME: this is spec order dependent, move this into a helper method
1577
+ # and execute in the before :all block
1578
+ unless loaded
1579
+ it 'should not be a kicker' do
1580
+ pending_if 'TODO', @many_to_many do
1581
+ @articles.should_not be_loaded
1582
+ end
1583
+ end
1584
+ end
1585
+ end
1586
+
1587
+ it 'should return true' do
1588
+ @return.should be_true
1589
+ end
1590
+
1591
+ it 'should update attributes of all Resources' do
1592
+ @articles.each { |resource| @attributes.each { |key, value| resource.send(key).should == value } }
1593
+ end
1594
+
1595
+ it 'should persist the changes' do
1596
+ resource = @article_model.get(*@article.key)
1597
+ @attributes.each { |key, value| resource.send(key).should == value }
1598
+ end
1599
+ end
1600
+
1601
+ describe 'with attributes where one is a parent association' do
1602
+ before :all do
1603
+ @attributes = { :original => @other }
1604
+
1605
+ @return = @articles.send(method, @attributes)
1606
+ end
1607
+
1608
+ if method == :update!
1609
+ # FIXME: this is spec order dependent, move this into a helper method
1610
+ # and execute in the before :all block
1611
+ unless loaded
1612
+ it 'should not be a kicker' do
1613
+ pending_if 'TODO', @many_to_many do
1614
+ @articles.should_not be_loaded
1615
+ end
1616
+ end
1617
+ end
1618
+ end
1619
+
1620
+ it 'should return true' do
1621
+ @return.should be_true
1622
+ end
1623
+
1624
+ it 'should update attributes of all Resources' do
1625
+ @articles.each { |resource| @attributes.each { |key, value| resource.send(key).should == value } }
1626
+ end
1627
+
1628
+ it 'should persist the changes' do
1629
+ resource = @article_model.get(*@article.key)
1630
+ @attributes.each { |key, value| resource.send(key).should == value }
1631
+ end
1632
+ end
1633
+
1634
+ describe 'with attributes where a not-nullable property is nil' do
1635
+ before :all do
1636
+ @return = @articles.send(method, :title => nil)
1637
+ end
1638
+
1639
+ if method == :update!
1640
+ should_not_be_a_kicker
1641
+ end
1642
+
1643
+ it 'should return false' do
1644
+ @return.should be_false
1645
+ end
1646
+ end
1647
+
1648
+ describe 'on a limited collection' do
1649
+ before :all do
1650
+ @other = @articles.create
1651
+ @limited = @articles.all(:limit => 1)
1652
+ @attributes = { :content => 'Updated Content' }
1653
+
1654
+ @return = @limited.send(method, @attributes)
1655
+ end
1656
+
1657
+ if method == :update!
1658
+ # FIXME: this is spec order dependent, move this into a helper method
1659
+ # and execute in the before :all block
1660
+ unless loaded
1661
+ it 'should not be a kicker' do
1662
+ pending "Update Collection##{method} to use a subquery" do
1663
+ @limited.should_not be_loaded
1664
+ end
1665
+ end
1666
+ end
1667
+ end
1668
+
1669
+ it 'should return true' do
1670
+ @return.should be_true
1671
+ end
1672
+
1673
+ it 'should bypass validation' do
1674
+ pending 'TODO: not sure how to best spec this'
1675
+ end
1676
+
1677
+ it 'should update attributes of all Resources' do
1678
+ @limited.each { |resource| @attributes.each { |key, value| resource.send(key).should == value } }
1679
+ end
1680
+
1681
+ it 'should persist the changes' do
1682
+ resource = @article_model.get(*@article.key)
1683
+ @attributes.each { |key, value| resource.send(key).should == value }
1684
+ end
1685
+
1686
+ it 'should not update the other Resource' do
1687
+ @other.reload
1688
+ @attributes.each { |key, value| @other.send(key).should_not == value }
1689
+ end
1690
+ end
1691
+
1692
+ describe 'on a dirty collection' do
1693
+ before :all do
1694
+ @articles.each { |r| r.content = 'Changed' }
1695
+ end
1696
+
1697
+ it 'should raise an exception' do
1698
+ lambda {
1699
+ @articles.send(method, :content => 'New Content')
1700
+ }.should raise_error(DataMapper::UpdateConflictError, "##{method} cannot be called on a dirty collection")
1701
+ end
1702
+ end
1703
+ end
1704
+ end
1705
+
1706
+ it 'should respond to a public model method with #method_missing' do
1707
+ @articles.should respond_to(:base_model)
1708
+ end
1709
+
1710
+ describe '#method_missing' do
1711
+ describe 'with a public model method' do
1712
+ before :all do
1713
+ @return = @articles.model.base_model
1714
+ end
1715
+
1716
+ should_not_be_a_kicker
1717
+
1718
+ it 'should return expected object' do
1719
+ @return.should == @article_model
1720
+ end
1721
+ end
1722
+ end
1723
+ end