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,1670 @@
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
+
622
+ @return = @resource = @articles.pop
623
+ end
624
+
625
+ it 'should return a Resource' do
626
+ @return.should be_kind_of(DataMapper::Resource)
627
+ end
628
+
629
+ it 'should be the last Resource in the Collection' do
630
+ @resource.should == @new
631
+ end
632
+
633
+ it 'should remove the Resource from the Collection' do
634
+ @articles.should_not be_include(@resource)
635
+ end
636
+
637
+ it 'should orphan the Resource' do
638
+ @resource.collection.should_not equal(@articles)
639
+ end
640
+ end
641
+
642
+ it { @articles.should respond_to(:push) }
643
+
644
+ describe '#push' do
645
+ before :all do
646
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
647
+
648
+ @return = @articles.push(*@resources)
649
+ end
650
+
651
+ it 'should return a Collection' do
652
+ @return.should be_kind_of(DataMapper::Collection)
653
+ end
654
+
655
+ it 'should return self' do
656
+ @return.should equal(@articles)
657
+ end
658
+
659
+ it 'should append the Resources to the Collection' do
660
+ @articles.should == [ @article ] + @resources
661
+ end
662
+
663
+ it 'should relate the Resources to the Collection' do
664
+ @resources.each { |resource| resource.collection.should equal(@articles) }
665
+ end
666
+ end
667
+
668
+ it { @articles.should respond_to(:reject!) }
669
+
670
+ describe '#reject!' do
671
+ describe 'with a block that matches a Resource in the Collection' do
672
+ before :all do
673
+ @resources = @articles.dup.entries
674
+
675
+ @return = @articles.reject! { true }
676
+ end
677
+
678
+ it 'should return a Collection' do
679
+ @return.should be_kind_of(DataMapper::Collection)
680
+ end
681
+
682
+ it 'should return self' do
683
+ @return.should equal(@articles)
684
+ end
685
+
686
+ it 'should remove the Resources from the Collection' do
687
+ @resources.each { |resource| @articles.should_not be_include(resource) }
688
+ end
689
+
690
+ it 'should orphan the Resources' do
691
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
692
+ end
693
+ end
694
+
695
+ describe 'with a block that does not match a Resource in the Collection' do
696
+ before :all do
697
+ @resources = @articles.dup.entries
698
+
699
+ @return = @articles.reject! { false }
700
+ end
701
+
702
+ it 'should return nil' do
703
+ @return.should be_nil
704
+ end
705
+
706
+ it 'should not modify the Collection' do
707
+ @articles.should == @resources
708
+ end
709
+ end
710
+ end
711
+
712
+ it { @articles.should respond_to(:reload) }
713
+
714
+ describe '#reload' do
715
+ describe 'with no arguments' do
716
+ before :all do
717
+ @resources = @articles.dup.entries
718
+
719
+ @return = @collection = @articles.reload
720
+ end
721
+
722
+ # FIXME: this is spec order dependent, move this into a helper method
723
+ # and execute in the before :all block
724
+ unless loaded
725
+ it 'should not be a kicker' do
726
+ pending do
727
+ @articles.should_not be_loaded
728
+ end
729
+ end
730
+ end
731
+
732
+ it 'should return a Collection' do
733
+ @return.should be_kind_of(DataMapper::Collection)
734
+ end
735
+
736
+ it 'should return self' do
737
+ @return.should equal(@articles)
738
+ end
739
+
740
+ { :title => true, :content => false }.each do |attribute, expected|
741
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
742
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
743
+ end
744
+ end
745
+ end
746
+
747
+ describe 'with a Hash query' do
748
+ before :all do
749
+ @resources = @articles.dup.entries
750
+
751
+ @return = @collection = @articles.reload(:fields => [ :content ]) # :title is a default field
752
+ end
753
+
754
+ # FIXME: this is spec order dependent, move this into a helper method
755
+ # and execute in the before :all block
756
+ unless loaded
757
+ it 'should not be a kicker' do
758
+ pending do
759
+ @articles.should_not be_loaded
760
+ end
761
+ end
762
+ end
763
+
764
+ it 'should return a Collection' do
765
+ @return.should be_kind_of(DataMapper::Collection)
766
+ end
767
+
768
+ it 'should return self' do
769
+ @return.should equal(@articles)
770
+ end
771
+
772
+ { :id => true, :content => true, :title => true }.each do |attribute, expected|
773
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
774
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
775
+ end
776
+ end
777
+ end
778
+
779
+ describe 'with a Query' do
780
+ before :all do
781
+ @query = DataMapper::Query.new(@repository, @article_model, :fields => [ :content ]) # :title is an original field
782
+
783
+ @return = @collection = @articles.reload(@query)
784
+ end
785
+
786
+ # FIXME: this is spec order dependent, move this into a helper method
787
+ # and execute in the before :all block
788
+ unless loaded
789
+ it 'should not be a kicker' do
790
+ pending do
791
+ @articles.should_not be_loaded
792
+ end
793
+ end
794
+ end
795
+
796
+ it 'should return a Collection' do
797
+ @return.should be_kind_of(DataMapper::Collection)
798
+ end
799
+
800
+ it 'should return self' do
801
+ @return.should equal(@articles)
802
+ end
803
+
804
+ { :id => true, :content => true, :title => loaded }.each do |attribute, expected|
805
+ it "should have query field #{attribute.inspect} #{'not' unless expected} loaded".squeeze(' ') do
806
+ pending_if "TODO: #{@articles.class}#reload should not be a kicker", @one_to_many && loaded == false && attribute == :title do
807
+ @collection.each { |resource| resource.attribute_loaded?(attribute).should == expected }
808
+ end
809
+ end
810
+ end
811
+ end
812
+ end
813
+
814
+ it { @articles.should respond_to(:replace) }
815
+
816
+ describe '#replace' do
817
+ describe 'when provided an Array of Resources' do
818
+ before :all do
819
+ @resources = @articles.dup.entries
820
+
821
+ @return = @articles.replace(@other_articles)
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
+ it 'should update the Collection with new Resources' do
833
+ @articles.should == @other_articles
834
+ end
835
+
836
+ it 'should relate each Resource added to the Collection' do
837
+ @articles.each { |resource| resource.collection.should equal(@articles) }
838
+ end
839
+
840
+ it 'should orphan each Resource removed from the Collection' do
841
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
842
+ end
843
+ end
844
+
845
+ describe 'when provided an Array of Hashes' do
846
+ before :all do
847
+ @array = [ { :content => 'From Hash' } ].freeze
848
+
849
+ @return = @articles.replace(@array)
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 initialize a Resource' do
861
+ @return.first.should be_kind_of(DataMapper::Resource)
862
+ end
863
+
864
+ it 'should be a new Resource' do
865
+ @return.first.should be_new
866
+ end
867
+
868
+ it 'should be a Resource with attributes matching the Hash' do
869
+ @return.first.attributes.only(*@array.first.keys).should == @array.first
870
+ end
871
+ end
872
+ end
873
+
874
+ it { @articles.should respond_to(:reverse!) }
875
+
876
+ describe '#reverse!' do
877
+ before :all do
878
+ @query = @articles.query
879
+
880
+ @new = @articles.create(:title => 'Sample Article')
881
+
882
+ @return = @articles.reverse!
883
+ end
884
+
885
+ it 'should return a Collection' do
886
+ @return.should be_kind_of(DataMapper::Collection)
887
+ end
888
+
889
+ it 'should return self' do
890
+ @return.should equal(@articles)
891
+ end
892
+
893
+ it 'should return a Collection with reversed entries' do
894
+ @return.should == [ @new, @article ]
895
+ end
896
+
897
+ it 'should return a Query that equal to the original' do
898
+ @return.query.should equal(@query)
899
+ end
900
+ end
901
+
902
+ [ :save, :save! ].each do |method|
903
+ it { @articles.should respond_to(method) }
904
+
905
+ describe "##{method}" do
906
+ describe 'when Resources are not saved' do
907
+ before :all do
908
+ @articles.new(:title => 'New Article', :content => 'New Article')
909
+
910
+ @return = @articles.send(method)
911
+ end
912
+
913
+ it 'should return true' do
914
+ @return.should be_true
915
+ end
916
+
917
+ it 'should save each Resource' do
918
+ @articles.each { |resource| resource.should be_saved }
919
+ end
920
+ end
921
+
922
+ describe 'when Resources have been orphaned' do
923
+ before :all do
924
+ @resources = @articles.entries
925
+ @articles.replace([])
926
+
927
+ @return = @articles.send(method)
928
+ end
929
+
930
+ it 'should return true' do
931
+ @return.should be_true
932
+ end
933
+
934
+ it 'should orphan the Resources' do
935
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
936
+ end
937
+ end
938
+ end
939
+ end
940
+
941
+ it { @articles.should respond_to(:shift) }
942
+
943
+ describe '#shift' do
944
+ before :all do
945
+ @new = @articles.create(:title => 'Sample Article')
946
+
947
+ @return = @resource = @articles.shift
948
+ end
949
+
950
+ it 'should return a Resource' do
951
+ @return.should be_kind_of(DataMapper::Resource)
952
+ end
953
+
954
+ it 'should be the first Resource in the Collection' do
955
+ @resource.key.should == @article.key
956
+ end
957
+
958
+ it 'should remove the Resource from the Collection' do
959
+ @articles.should_not be_include(@resource)
960
+ end
961
+
962
+ it 'should orphan the Resource' do
963
+ @resource.collection.should_not equal(@articles)
964
+ end
965
+ end
966
+
967
+ it { @articles.should respond_to(:slice!) }
968
+
969
+ describe '#slice!' do
970
+ before :all do
971
+ 1.upto(10) { |number| @articles.create(:content => "Article #{number}") }
972
+
973
+ @copy = @articles.dup
974
+ end
975
+
976
+ describe 'with a positive offset' do
977
+ before :all do
978
+ unless @skip
979
+ @return = @resource = @articles.slice!(0)
980
+ end
981
+ end
982
+
983
+ it 'should return a Resource' do
984
+ @return.should be_kind_of(DataMapper::Resource)
985
+ end
986
+
987
+ it 'should return expected Resource' do
988
+ @return.key.should == @article.key
989
+ end
990
+
991
+ it 'should return the same as Array#slice!' do
992
+ @return.should == @copy.entries.slice!(0)
993
+ end
994
+
995
+ it 'should remove the Resource from the Collection' do
996
+ @articles.should_not be_include(@resource)
997
+ end
998
+
999
+ it 'should orphan the Resource' do
1000
+ @resource.collection.should_not equal(@articles)
1001
+ end
1002
+ end
1003
+
1004
+ describe 'with a positive offset and length' do
1005
+ before :all do
1006
+ unless @skip
1007
+ @return = @resources = @articles.slice!(5, 5)
1008
+ end
1009
+ end
1010
+
1011
+ it 'should return a Collection' do
1012
+ @return.should be_kind_of(DataMapper::Collection)
1013
+ end
1014
+
1015
+ it 'should return the expected Resource' do
1016
+ @return.should == @copy.entries.slice!(5, 5)
1017
+ end
1018
+
1019
+ it 'should remove the Resources from the Collection' do
1020
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1021
+ end
1022
+
1023
+ it 'should orphan the Resources' do
1024
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1025
+ end
1026
+
1027
+ it 'should scope the Collection' do
1028
+ @resources.reload.should == @copy.entries.slice!(5, 5)
1029
+ end
1030
+ end
1031
+
1032
+ describe 'with a positive range' do
1033
+ before :all do
1034
+ unless @skip
1035
+ @return = @resources = @articles.slice!(5..10)
1036
+ end
1037
+ end
1038
+
1039
+ it 'should return a Collection' do
1040
+ @return.should be_kind_of(DataMapper::Collection)
1041
+ end
1042
+
1043
+ it 'should return the expected Resources' do
1044
+ @return.should == @copy.entries.slice!(5..10)
1045
+ end
1046
+
1047
+ it 'should remove the Resources from the Collection' do
1048
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1049
+ end
1050
+
1051
+ it 'should orphan the Resources' do
1052
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1053
+ end
1054
+
1055
+ it 'should scope the Collection' do
1056
+ @resources.reload.should == @copy.entries.slice!(5..10)
1057
+ end
1058
+ end
1059
+
1060
+ describe 'with a negative offset' do
1061
+ before :all do
1062
+ unless @skip
1063
+ @return = @resource = @articles.slice!(-1)
1064
+ end
1065
+ end
1066
+
1067
+ it 'should return a Resource' do
1068
+ @return.should be_kind_of(DataMapper::Resource)
1069
+ end
1070
+
1071
+ it 'should return expected Resource' do
1072
+ @return.should == @copy.entries.slice!(-1)
1073
+ end
1074
+
1075
+ it 'should remove the Resource from the Collection' do
1076
+ @articles.should_not be_include(@resource)
1077
+ end
1078
+
1079
+ it 'should orphan the Resource' do
1080
+ @resource.collection.should_not equal(@articles)
1081
+ end
1082
+ end
1083
+
1084
+ describe 'with a negative offset and length' do
1085
+ before :all do
1086
+ unless @skip
1087
+ @return = @resources = @articles.slice!(-5, 5)
1088
+ end
1089
+ end
1090
+
1091
+ it 'should return a Collection' do
1092
+ @return.should be_kind_of(DataMapper::Collection)
1093
+ end
1094
+
1095
+ it 'should return the expected Resources' do
1096
+ @return.should == @copy.entries.slice!(-5, 5)
1097
+ end
1098
+
1099
+ it 'should remove the Resources from the Collection' do
1100
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1101
+ end
1102
+
1103
+ it 'should orphan the Resources' do
1104
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1105
+ end
1106
+
1107
+ it 'should scope the Collection' do
1108
+ @resources.reload.should == @copy.entries.slice!(-5, 5)
1109
+ end
1110
+ end
1111
+
1112
+ describe 'with a negative range' do
1113
+ before :all do
1114
+ unless @skip
1115
+ @return = @resources = @articles.slice!(-3..-2)
1116
+ end
1117
+ end
1118
+
1119
+ it 'should return a Collection' do
1120
+ @return.should be_kind_of(DataMapper::Collection)
1121
+ end
1122
+
1123
+ it 'should return the expected Resources' do
1124
+ @return.should == @copy.entries.slice!(-3..-2)
1125
+ end
1126
+
1127
+ it 'should remove the Resources from the Collection' do
1128
+ @resources.each { |resource| @articles.should_not be_include(resource) }
1129
+ end
1130
+
1131
+ it 'should orphan the Resources' do
1132
+ @resources.each { |resource| resource.collection.should_not equal(@articles) }
1133
+ end
1134
+
1135
+ it 'should scope the Collection' do
1136
+ @resources.reload.should == @copy.entries.slice!(-3..-2)
1137
+ end
1138
+ end
1139
+
1140
+ describe 'with an offset not within the Collection' do
1141
+ before :all do
1142
+ unless @skip
1143
+ @return = @articles.slice!(12)
1144
+ end
1145
+ end
1146
+
1147
+ it 'should return nil' do
1148
+ @return.should be_nil
1149
+ end
1150
+ end
1151
+
1152
+ describe 'with an offset and length not within the Collection' do
1153
+ before :all do
1154
+ unless @skip
1155
+ @return = @articles.slice!(12, 1)
1156
+ end
1157
+ end
1158
+
1159
+ it 'should return nil' do
1160
+ @return.should be_nil
1161
+ end
1162
+ end
1163
+
1164
+ describe 'with a range not within the Collection' do
1165
+ before :all do
1166
+ unless @skip
1167
+ @return = @articles.slice!(12..13)
1168
+ end
1169
+ end
1170
+
1171
+ it 'should return nil' do
1172
+ @return.should be_nil
1173
+ end
1174
+ end
1175
+ end
1176
+
1177
+ it { @articles.should respond_to(:sort!) }
1178
+
1179
+ describe '#sort!' do
1180
+ describe 'without a block' do
1181
+ before :all do
1182
+ @return = @articles.unshift(@other).sort!
1183
+ end
1184
+
1185
+ it 'should return a Collection' do
1186
+ @return.should be_kind_of(DataMapper::Collection)
1187
+ end
1188
+
1189
+ it 'should return self' do
1190
+ @return.should equal(@articles)
1191
+ end
1192
+
1193
+ it 'should modify and sort the Collection using default sort order' do
1194
+ @articles.should == [ @article, @other ]
1195
+ end
1196
+ end
1197
+
1198
+ describe 'with a block' do
1199
+ before :all do
1200
+ @return = @articles.unshift(@other).sort! { |a_resource, b_resource| b_resource.id <=> a_resource.id }
1201
+ end
1202
+
1203
+ it 'should return a Collection' do
1204
+ @return.should be_kind_of(DataMapper::Collection)
1205
+ end
1206
+
1207
+ it 'should return self' do
1208
+ @return.should equal(@articles)
1209
+ end
1210
+
1211
+ it 'should modify and sort the Collection using supplied block' do
1212
+ @articles.should == [ @other, @article ]
1213
+ end
1214
+ end
1215
+ end
1216
+
1217
+ [ :splice, :[]= ].each do |method|
1218
+ it { @articles.should respond_to(method) }
1219
+
1220
+ describe "##{method}" do
1221
+ before :all do
1222
+ unless @skip
1223
+ orphans = (1..10).map do |number|
1224
+ articles = @articles.dup
1225
+ articles.create(:content => "Article #{number}")
1226
+ articles.pop # remove the article from the tail
1227
+ end
1228
+
1229
+ @articles.unshift(*orphans.first(5))
1230
+ @articles.concat(orphans.last(5))
1231
+
1232
+ unless loaded
1233
+ @articles.should_not be_loaded
1234
+ end
1235
+
1236
+ @copy = @articles.dup
1237
+ @new = @article_model.new(:content => 'New Article')
1238
+ end
1239
+ end
1240
+
1241
+ describe 'with a positive offset and a Resource' do
1242
+ before :all do
1243
+ rescue_if 'TODO', @skip do
1244
+ @original = @copy[1]
1245
+ @original.collection.should equal(@articles)
1246
+
1247
+ @return = @resource = @articles.send(method, 1, @new)
1248
+ end
1249
+ end
1250
+
1251
+ should_not_be_a_kicker
1252
+
1253
+ it 'should return a Resource' do
1254
+ @return.should be_kind_of(DataMapper::Resource)
1255
+ end
1256
+
1257
+ it 'should return expected Resource' do
1258
+ @return.should equal(@new)
1259
+ end
1260
+
1261
+ it 'should return the same as Array#[]=' do
1262
+ @return.should == @copy.entries[1] = @new
1263
+ end
1264
+
1265
+ it 'should include the Resource in the Collection' do
1266
+ @articles.should be_include(@resource)
1267
+ end
1268
+
1269
+ it 'should relate the Resource to the Collection' do
1270
+ @resource.collection.should equal(@articles)
1271
+ end
1272
+
1273
+ it 'should orphan the original Resource' do
1274
+ @original.collection.should_not equal(@articles)
1275
+ end
1276
+ end
1277
+
1278
+ describe 'with a positive offset and length and a Resource' do
1279
+ before :all do
1280
+ rescue_if 'TODO', @skip do
1281
+ @original = @copy[2]
1282
+ @original.collection.should equal(@articles)
1283
+
1284
+ @return = @resource = @articles.send(method, 2, 1, @new)
1285
+ end
1286
+ end
1287
+
1288
+ should_not_be_a_kicker
1289
+
1290
+ it 'should return a Resource' do
1291
+ @return.should be_kind_of(DataMapper::Resource)
1292
+ end
1293
+
1294
+ it 'should return the expected Resource' do
1295
+ @return.should equal(@new)
1296
+ end
1297
+
1298
+ it 'should return the same as Array#[]=' do
1299
+ @return.should == @copy.entries[2, 1] = @new
1300
+ end
1301
+
1302
+ it 'should include the Resource in the Collection' do
1303
+ @articles.should be_include(@resource)
1304
+ end
1305
+
1306
+ it 'should orphan the original Resource' do
1307
+ @original.collection.should_not equal(@articles)
1308
+ end
1309
+ end
1310
+
1311
+ describe 'with a positive range and a Resource' do
1312
+ before :all do
1313
+ rescue_if 'TODO', @skip do
1314
+ @originals = @copy.values_at(2..3)
1315
+ @originals.each { |resource| resource.collection.should equal(@articles) }
1316
+
1317
+ @return = @resource = @articles.send(method, 2..3, @new)
1318
+ end
1319
+ end
1320
+
1321
+ should_not_be_a_kicker
1322
+
1323
+ it 'should return a Resource' do
1324
+ @return.should be_kind_of(DataMapper::Resource)
1325
+ end
1326
+
1327
+ it 'should return the expected Resources' do
1328
+ @return.should equal(@new)
1329
+ end
1330
+
1331
+ it 'should return the same as Array#[]=' do
1332
+ @return.should == @copy.entries[2..3] = @new
1333
+ end
1334
+
1335
+ it 'should include the Resource in the Collection' do
1336
+ @articles.should be_include(@resource)
1337
+ end
1338
+
1339
+ it 'should orphan the original Resources' do
1340
+ @originals.each { |resource| resource.collection.should_not equal(@articles) }
1341
+ end
1342
+ end
1343
+
1344
+ describe 'with a negative offset and a Resource' do
1345
+ before :all do
1346
+ rescue_if 'TODO', @skip do
1347
+ @original = @copy[-1]
1348
+ @original.collection.should equal(@articles)
1349
+
1350
+ @return = @resource = @articles.send(method, -1, @new)
1351
+ end
1352
+ end
1353
+
1354
+ should_not_be_a_kicker
1355
+
1356
+ it 'should return a Resource' do
1357
+ @return.should be_kind_of(DataMapper::Resource)
1358
+ end
1359
+
1360
+ it 'should return expected Resource' do
1361
+ @return.should equal(@new)
1362
+ end
1363
+
1364
+ it 'should return the same as Array#[]=' do
1365
+ @return.should == @copy.entries[-1] = @new
1366
+ end
1367
+
1368
+ it 'should include the Resource in the Collection' do
1369
+ @articles.should be_include(@resource)
1370
+ end
1371
+
1372
+ it 'should relate the Resource to the Collection' do
1373
+ @resource.collection.should equal(@articles)
1374
+ end
1375
+
1376
+ it 'should orphan the original Resource' do
1377
+ @original.collection.should_not equal(@articles)
1378
+ end
1379
+ end
1380
+
1381
+ describe 'with a negative offset and length and a Resource' do
1382
+ before :all do
1383
+ rescue_if 'TODO', @skip do
1384
+ @original = @copy[-2]
1385
+ @original.collection.should equal(@articles)
1386
+
1387
+ @return = @resource = @articles.send(method, -2, 1, @new)
1388
+ end
1389
+ end
1390
+
1391
+ should_not_be_a_kicker
1392
+
1393
+ it 'should return a Resource' do
1394
+ @return.should be_kind_of(DataMapper::Resource)
1395
+ end
1396
+
1397
+ it 'should return the expected Resource' do
1398
+ @return.should equal(@new)
1399
+ end
1400
+
1401
+ it 'should return the same as Array#[]=' do
1402
+ @return.should == @copy.entries[-2, 1] = @new
1403
+ end
1404
+
1405
+ it 'should include the Resource in the Collection' do
1406
+ @articles.should be_include(@resource)
1407
+ end
1408
+
1409
+ it 'should orphan the original Resource' do
1410
+ @original.collection.should_not equal(@articles)
1411
+ end
1412
+ end
1413
+
1414
+ describe 'with a negative range and a Resource' do
1415
+ before :all do
1416
+ rescue_if 'TODO', @skip do
1417
+ @originals = @articles.values_at(-3..-2)
1418
+ @originals.each { |resource| resource.collection.should equal(@articles) }
1419
+
1420
+ @return = @resource = @articles.send(method, -3..-2, @new)
1421
+ end
1422
+ end
1423
+
1424
+ should_not_be_a_kicker
1425
+
1426
+ it 'should return a Resource' do
1427
+ @return.should be_kind_of(DataMapper::Resource)
1428
+ end
1429
+
1430
+ it 'should return the expected Resources' do
1431
+ @return.should equal(@new)
1432
+ end
1433
+
1434
+ it 'should return the same as Array#[]=' do
1435
+ @return.should == @copy.entries[-3..-2] = @new
1436
+ end
1437
+
1438
+ it 'should include the Resource in the Collection' do
1439
+ @articles.should be_include(@resource)
1440
+ end
1441
+
1442
+ it 'should orphan the original Resources' do
1443
+ @originals.each { |resource| resource.collection.should_not equal(@articles) }
1444
+ end
1445
+ end
1446
+ end
1447
+ end
1448
+
1449
+ describe '#[]=' do
1450
+ describe 'when swapping resources' do
1451
+ before :all do
1452
+ rescue_if 'TODO', @skip do
1453
+ @articles.create(:content => 'Another Article')
1454
+
1455
+ @entries = @articles.entries
1456
+
1457
+ @articles[0], @articles[1] = @articles[1], @articles[0]
1458
+ end
1459
+ end
1460
+
1461
+ it 'should include the Resource in the Collection' do
1462
+ @articles.should == @entries.reverse
1463
+ end
1464
+
1465
+ it 'should relate the Resource to the Collection' do
1466
+ @articles.each { |resource| resource.collection.should equal(@articles) }
1467
+ end
1468
+ end
1469
+ end
1470
+
1471
+ it { @articles.should respond_to(:unshift) }
1472
+
1473
+ describe '#unshift' do
1474
+ before :all do
1475
+ @resources = [ @article_model.new(:title => 'Title 1'), @article_model.new(:title => 'Title 2') ]
1476
+
1477
+ @return = @articles.unshift(*@resources)
1478
+ end
1479
+
1480
+ it 'should return a Collection' do
1481
+ @return.should be_kind_of(DataMapper::Collection)
1482
+ end
1483
+
1484
+ it 'should return self' do
1485
+ @return.should equal(@articles)
1486
+ end
1487
+
1488
+ it 'should prepend the Resources to the Collection' do
1489
+ @articles.should == @resources + [ @article ]
1490
+ end
1491
+
1492
+ it 'should relate the Resources to the Collection' do
1493
+ @resources.each { |resource| resource.collection.should equal(@articles) }
1494
+ end
1495
+ end
1496
+
1497
+ [ :update, :update! ].each do |method|
1498
+ it { @articles.should respond_to(method) }
1499
+
1500
+ describe "##{method}" do
1501
+ describe 'with no arguments' do
1502
+ before :all do
1503
+ @return = @articles.send(method)
1504
+ end
1505
+
1506
+ if method == :update!
1507
+ should_not_be_a_kicker
1508
+ end
1509
+
1510
+ it 'should return true' do
1511
+ @return.should be_true
1512
+ end
1513
+ end
1514
+
1515
+ describe 'with attributes' do
1516
+ before :all do
1517
+ @attributes = { :title => 'Updated Title' }
1518
+
1519
+ @return = @articles.send(method, @attributes)
1520
+ end
1521
+
1522
+ if method == :update!
1523
+ # FIXME: this is spec order dependent, move this into a helper method
1524
+ # and execute in the before :all block
1525
+ unless loaded
1526
+ it 'should not be a kicker' do
1527
+ pending_if 'TODO', @many_to_many do
1528
+ @articles.should_not be_loaded
1529
+ end
1530
+ end
1531
+ end
1532
+ end
1533
+
1534
+ it 'should return true' do
1535
+ @return.should be_true
1536
+ end
1537
+
1538
+ it 'should update attributes of all Resources' do
1539
+ @articles.each { |resource| @attributes.each { |key, value| resource.send(key).should == value } }
1540
+ end
1541
+
1542
+ it 'should persist the changes' do
1543
+ resource = @article_model.get(*@article.key)
1544
+ @attributes.each { |key, value| resource.send(key).should == value }
1545
+ end
1546
+ end
1547
+
1548
+ describe 'with attributes where one is a parent association' do
1549
+ before :all do
1550
+ @attributes = { :original => @other }
1551
+
1552
+ @return = @articles.send(method, @attributes)
1553
+ end
1554
+
1555
+ if method == :update!
1556
+ # FIXME: this is spec order dependent, move this into a helper method
1557
+ # and execute in the before :all block
1558
+ unless loaded
1559
+ it 'should not be a kicker' do
1560
+ pending_if 'TODO', @many_to_many do
1561
+ @articles.should_not be_loaded
1562
+ end
1563
+ end
1564
+ end
1565
+ end
1566
+
1567
+ it 'should return true' do
1568
+ @return.should be_true
1569
+ end
1570
+
1571
+ it 'should update attributes of all Resources' do
1572
+ @articles.each { |resource| @attributes.each { |key, value| resource.send(key).should == value } }
1573
+ end
1574
+
1575
+ it 'should persist the changes' do
1576
+ resource = @article_model.get(*@article.key)
1577
+ @attributes.each { |key, value| resource.send(key).should == value }
1578
+ end
1579
+ end
1580
+
1581
+ describe 'with attributes where a not-nullable property is nil' do
1582
+ before :all do
1583
+ @return = @articles.send(method, :title => nil)
1584
+ end
1585
+
1586
+ if method == :update!
1587
+ should_not_be_a_kicker
1588
+ end
1589
+
1590
+ it 'should return false' do
1591
+ @return.should be_false
1592
+ end
1593
+ end
1594
+
1595
+ describe 'on a limited collection' do
1596
+ before :all do
1597
+ @other = @articles.create
1598
+ @limited = @articles.all(:limit => 1)
1599
+ @attributes = { :content => 'Updated Content' }
1600
+
1601
+ @return = @limited.send(method, @attributes)
1602
+ end
1603
+
1604
+ if method == :update!
1605
+ # FIXME: this is spec order dependent, move this into a helper method
1606
+ # and execute in the before :all block
1607
+ unless loaded
1608
+ it 'should not be a kicker' do
1609
+ pending "Update Collection##{method} to use a subquery" do
1610
+ @limited.should_not be_loaded
1611
+ end
1612
+ end
1613
+ end
1614
+ end
1615
+
1616
+ it 'should return true' do
1617
+ @return.should be_true
1618
+ end
1619
+
1620
+ it 'should bypass validation' do
1621
+ pending 'TODO: not sure how to best spec this'
1622
+ end
1623
+
1624
+ it 'should update attributes of all Resources' do
1625
+ @limited.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
+
1633
+ it 'should not update the other Resource' do
1634
+ @other.reload
1635
+ @attributes.each { |key, value| @other.send(key).should_not == value }
1636
+ end
1637
+ end
1638
+
1639
+ describe 'on a dirty collection' do
1640
+ before :all do
1641
+ @articles.each { |r| r.content = 'Changed' }
1642
+ end
1643
+
1644
+ it 'should raise an exception' do
1645
+ lambda {
1646
+ @articles.send(method, :content => 'New Content')
1647
+ }.should raise_error(DataMapper::UpdateConflictError, "##{method} cannot be called on a dirty collection")
1648
+ end
1649
+ end
1650
+ end
1651
+ end
1652
+
1653
+ it 'should respond to a public model method with #method_missing' do
1654
+ @articles.should respond_to(:base_model)
1655
+ end
1656
+
1657
+ describe '#method_missing' do
1658
+ describe 'with a public model method' do
1659
+ before :all do
1660
+ @return = @articles.model.base_model
1661
+ end
1662
+
1663
+ should_not_be_a_kicker
1664
+
1665
+ it 'should return expected object' do
1666
+ @return.should == @article_model
1667
+ end
1668
+ end
1669
+ end
1670
+ end