datamapper-dm-core 0.9.11 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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