thorsson-mongo_mapper 0.8.2

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 (134) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/bin/mmconsole +60 -0
  4. data/examples/keys.rb +40 -0
  5. data/examples/modifiers/set.rb +25 -0
  6. data/examples/plugins.rb +41 -0
  7. data/examples/querying.rb +35 -0
  8. data/examples/scopes.rb +52 -0
  9. data/lib/mongo_mapper.rb +79 -0
  10. data/lib/mongo_mapper/connection.rb +83 -0
  11. data/lib/mongo_mapper/document.rb +41 -0
  12. data/lib/mongo_mapper/embedded_document.rb +31 -0
  13. data/lib/mongo_mapper/exceptions.rb +27 -0
  14. data/lib/mongo_mapper/extensions/array.rb +19 -0
  15. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  16. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  17. data/lib/mongo_mapper/extensions/date.rb +25 -0
  18. data/lib/mongo_mapper/extensions/float.rb +14 -0
  19. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  20. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  21. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  22. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  23. data/lib/mongo_mapper/extensions/object.rb +27 -0
  24. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  25. data/lib/mongo_mapper/extensions/set.rb +20 -0
  26. data/lib/mongo_mapper/extensions/string.rb +18 -0
  27. data/lib/mongo_mapper/extensions/time.rb +29 -0
  28. data/lib/mongo_mapper/plugins.rb +16 -0
  29. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  30. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  31. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  32. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  33. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  34. data/lib/mongo_mapper/plugins/associations/collection.rb +22 -0
  35. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  36. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  37. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  38. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +110 -0
  39. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  40. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  41. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  42. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  43. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  44. data/lib/mongo_mapper/plugins/associations/proxy.rb +126 -0
  45. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  46. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  47. data/lib/mongo_mapper/plugins/clone.rb +19 -0
  48. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  49. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  50. data/lib/mongo_mapper/plugins/document.rb +41 -0
  51. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  52. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  53. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  54. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  55. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  56. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  57. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  58. data/lib/mongo_mapper/plugins/keys.rb +309 -0
  59. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  60. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  61. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  62. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  63. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  64. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  65. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  66. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  67. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  68. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  69. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  70. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  71. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  72. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  73. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  74. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  75. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  76. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  77. data/lib/mongo_mapper/version.rb +4 -0
  78. data/test/_NOTE_ON_TESTING +1 -0
  79. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  80. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  81. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  82. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  83. data/test/functional/associations/test_many_documents_proxy.rb +575 -0
  84. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  85. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  86. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  87. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  88. data/test/functional/associations/test_one_proxy.rb +182 -0
  89. data/test/functional/test_accessible.rb +168 -0
  90. data/test/functional/test_associations.rb +44 -0
  91. data/test/functional/test_binary.rb +27 -0
  92. data/test/functional/test_caching.rb +76 -0
  93. data/test/functional/test_callbacks.rb +151 -0
  94. data/test/functional/test_dirty.rb +163 -0
  95. data/test/functional/test_document.rb +253 -0
  96. data/test/functional/test_dynamic_querying.rb +75 -0
  97. data/test/functional/test_embedded_document.rb +210 -0
  98. data/test/functional/test_identity_map.rb +506 -0
  99. data/test/functional/test_indexes.rb +42 -0
  100. data/test/functional/test_logger.rb +20 -0
  101. data/test/functional/test_modifiers.rb +416 -0
  102. data/test/functional/test_pagination.rb +91 -0
  103. data/test/functional/test_protected.rb +175 -0
  104. data/test/functional/test_querying.rb +873 -0
  105. data/test/functional/test_safe.rb +76 -0
  106. data/test/functional/test_sci.rb +230 -0
  107. data/test/functional/test_scopes.rb +171 -0
  108. data/test/functional/test_string_id_compatibility.rb +67 -0
  109. data/test/functional/test_timestamps.rb +62 -0
  110. data/test/functional/test_userstamps.rb +27 -0
  111. data/test/functional/test_validations.rb +342 -0
  112. data/test/models.rb +233 -0
  113. data/test/test_active_model_lint.rb +13 -0
  114. data/test/test_helper.rb +104 -0
  115. data/test/unit/associations/test_base.rb +212 -0
  116. data/test/unit/associations/test_proxy.rb +105 -0
  117. data/test/unit/serializers/test_json_serializer.rb +202 -0
  118. data/test/unit/test_clone.rb +69 -0
  119. data/test/unit/test_descendant_appends.rb +71 -0
  120. data/test/unit/test_document.rb +213 -0
  121. data/test/unit/test_dynamic_finder.rb +125 -0
  122. data/test/unit/test_embedded_document.rb +644 -0
  123. data/test/unit/test_extensions.rb +380 -0
  124. data/test/unit/test_key.rb +185 -0
  125. data/test/unit/test_keys.rb +55 -0
  126. data/test/unit/test_mongo_mapper.rb +110 -0
  127. data/test/unit/test_pagination.rb +11 -0
  128. data/test/unit/test_plugins.rb +50 -0
  129. data/test/unit/test_rails.rb +181 -0
  130. data/test/unit/test_rails_compatibility.rb +52 -0
  131. data/test/unit/test_serialization.rb +51 -0
  132. data/test/unit/test_time_zones.rb +39 -0
  133. data/test/unit/test_validations.rb +544 -0
  134. metadata +316 -0
@@ -0,0 +1,575 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyDocumentsProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Project.collection.remove
7
+ Status.collection.remove
8
+
9
+ @pet_class = Doc do
10
+ key :name, String
11
+ key :owner_id, ObjectId
12
+ end
13
+
14
+ @owner_class = Doc do
15
+ key :name, String
16
+ end
17
+ @owner_class.many :pets, :class => @pet_class, :foreign_key => :owner_id, :order => 'name'
18
+ end
19
+
20
+ should "default reader to empty array" do
21
+ project = Project.new
22
+ project.statuses.should == []
23
+ end
24
+
25
+ should "allow assignment of many associated documents using a hash" do
26
+ person_attributes = {
27
+ 'name' => 'Mr. Pet Lover',
28
+ 'pets' => [
29
+ {'name' => 'Jimmy', 'species' => 'Cocker Spainel'},
30
+ {'name' => 'Sasha', 'species' => 'Siberian Husky'},
31
+ ]
32
+ }
33
+
34
+ owner = @owner_class.new(person_attributes)
35
+ owner.name.should == 'Mr. Pet Lover'
36
+ owner.pets[0].name.should == 'Jimmy'
37
+ owner.pets[0].species.should == 'Cocker Spainel'
38
+ owner.pets[1].name.should == 'Sasha'
39
+ owner.pets[1].species.should == 'Siberian Husky'
40
+
41
+ owner.save.should be_true
42
+ owner.reload
43
+
44
+ owner.name.should == 'Mr. Pet Lover'
45
+ owner.pets[0].name.should == 'Jimmy'
46
+ owner.pets[0].species.should == 'Cocker Spainel'
47
+ owner.pets[1].name.should == 'Sasha'
48
+ owner.pets[1].species.should == 'Siberian Husky'
49
+ end
50
+
51
+ should "allow adding to association like it was an array" do
52
+ project = Project.new
53
+ project.statuses << Status.new(:name => 'Foo1!')
54
+ project.statuses.push Status.new(:name => 'Foo2!')
55
+ project.statuses.concat Status.new(:name => 'Foo3!')
56
+ project.statuses.size.should == 3
57
+ end
58
+
59
+ context "replacing the association" do
60
+ context "with objects of the class" do
61
+ should "work" do
62
+ project = Project.new
63
+ project.statuses = [Status.new(:name => "ready")]
64
+ project.save.should be_true
65
+
66
+ project.reload
67
+ project.statuses.size.should == 1
68
+ project.statuses[0].name.should == "ready"
69
+ end
70
+ end
71
+
72
+ context "with Hashes" do
73
+ should "convert to objects of the class and work" do
74
+ project = Project.new
75
+ project.statuses = [{ 'name' => 'ready' }]
76
+ project.save.should be_true
77
+
78
+ project.reload
79
+ project.statuses.size.should == 1
80
+ project.statuses[0].name.should == "ready"
81
+ end
82
+ end
83
+ end
84
+
85
+ context "using <<, push and concat" do
86
+ context "with objects of the class" do
87
+ should "correctly assign foreign key" do
88
+ project = Project.new
89
+ project.statuses << Status.new(:name => '<<')
90
+ project.statuses.push Status.new(:name => 'push')
91
+ project.statuses.concat Status.new(:name => 'concat')
92
+
93
+ project.reload
94
+ project.statuses[0].project_id.should == project.id
95
+ project.statuses[1].project_id.should == project.id
96
+ project.statuses[2].project_id.should == project.id
97
+ end
98
+ end
99
+
100
+ context "with Hashes" do
101
+ should "correctly convert to objects and assign foreign key" do
102
+ project = Project.new
103
+ project.statuses << { 'name' => '<<' }
104
+ project.statuses.push( { 'name' => 'push' })
105
+ project.statuses.concat({ 'name' => 'concat' })
106
+
107
+ project.reload
108
+ project.statuses[0].project_id.should == project.id
109
+ project.statuses[1].project_id.should == project.id
110
+ project.statuses[2].project_id.should == project.id
111
+ end
112
+ end
113
+ end
114
+
115
+ context "build" do
116
+ should "assign foreign key" do
117
+ project = Project.create
118
+ status = project.statuses.build
119
+ status.project_id.should == project.id
120
+ end
121
+
122
+ should "allow assigning attributes" do
123
+ project = Project.create
124
+ status = project.statuses.build(:name => 'Foo')
125
+ status.name.should == 'Foo'
126
+ end
127
+
128
+ should "reset cache" do
129
+ project = Project.create
130
+ project.statuses.size.should == 0
131
+ status = project.statuses.build(:name => 'Foo')
132
+ status.save!
133
+ project.statuses.size.should == 1
134
+ end
135
+
136
+ should "update collection without save" do
137
+ project = Project.create
138
+ project.statuses.build(:name => 'Foo')
139
+ project.statuses.size.should == 1
140
+ end
141
+
142
+ should "save built document when saving parent" do
143
+ project = Project.create
144
+ status = project.statuses.build(:name => 'Foo')
145
+ project.save!
146
+ status.should_not be_new
147
+ end
148
+ end
149
+
150
+ context "create" do
151
+ should "assign foreign key" do
152
+ project = Project.create
153
+ status = project.statuses.create(:name => 'Foo!')
154
+ status.project_id.should == project.id
155
+ end
156
+
157
+ should "save record" do
158
+ project = Project.create
159
+ lambda {
160
+ project.statuses.create(:name => 'Foo!')
161
+ }.should change { Status.count }
162
+ end
163
+
164
+ should "allow passing attributes" do
165
+ project = Project.create
166
+ status = project.statuses.create(:name => 'Foo!')
167
+ status.name.should == 'Foo!'
168
+ end
169
+
170
+ should "reset cache" do
171
+ project = Project.create
172
+ project.statuses.size.should == 0
173
+ project.statuses.create(:name => 'Foo!')
174
+ project.statuses.size.should == 1
175
+ end
176
+ end
177
+
178
+ context "create!" do
179
+ should "assign foreign key" do
180
+ project = Project.create
181
+ status = project.statuses.create!(:name => 'Foo!')
182
+ status.project_id.should == project.id
183
+ end
184
+
185
+ should "save record" do
186
+ project = Project.create
187
+ lambda {
188
+ project.statuses.create!(:name => 'Foo!')
189
+ }.should change { Status.count }
190
+ end
191
+
192
+ should "allow passing attributes" do
193
+ project = Project.create
194
+ status = project.statuses.create!(:name => 'Foo!')
195
+ status.name.should == 'Foo!'
196
+ end
197
+
198
+ should "raise exception if not valid" do
199
+ project = Project.create
200
+ lambda {
201
+ project.statuses.create!(:name => nil)
202
+ }.should raise_error(MongoMapper::DocumentNotValid)
203
+ end
204
+
205
+ should "reset cache" do
206
+ project = Project.create
207
+ project.statuses.size.should == 0
208
+ project.statuses.create!(:name => 'Foo!')
209
+ project.statuses.size.should == 1
210
+ end
211
+ end
212
+
213
+ context "count" do
214
+ should "work scoped to association" do
215
+ project = Project.create
216
+ 3.times { project.statuses.create(:name => 'Foo!') }
217
+
218
+ other_project = Project.create
219
+ 2.times { other_project.statuses.create(:name => 'Foo!') }
220
+
221
+ project.statuses.count.should == 3
222
+ other_project.statuses.count.should == 2
223
+ end
224
+
225
+ should "work with conditions" do
226
+ project = Project.create
227
+ project.statuses.create(:name => 'Foo')
228
+ project.statuses.create(:name => 'Other 1')
229
+ project.statuses.create(:name => 'Other 2')
230
+
231
+ project.statuses.count(:name => 'Foo').should == 1
232
+ end
233
+ end
234
+
235
+ context "Unassociating documents" do
236
+ setup do
237
+ @project = Project.create
238
+ @project.statuses << Status.create(:name => '1')
239
+ @project.statuses << Status.create(:name => '2')
240
+
241
+ @project2 = Project.create
242
+ @project2.statuses << Status.create(:name => '1')
243
+ @project2.statuses << Status.create(:name => '2')
244
+ end
245
+
246
+ should "work with destroy all" do
247
+ @project.statuses.count.should == 2
248
+ @project.statuses.destroy_all
249
+ @project.statuses.count.should == 0
250
+
251
+ @project2.statuses.count.should == 2
252
+ Status.count.should == 2
253
+ end
254
+
255
+ should "work with destroy all and conditions" do
256
+ @project.statuses.count.should == 2
257
+ @project.statuses.destroy_all(:name => '1')
258
+ @project.statuses.count.should == 1
259
+
260
+ @project2.statuses.count.should == 2
261
+ Status.count.should == 3
262
+ end
263
+
264
+ should "work with delete all" do
265
+ @project.statuses.count.should == 2
266
+ @project.statuses.delete_all
267
+ @project.statuses.count.should == 0
268
+
269
+ @project2.statuses.count.should == 2
270
+ Status.count.should == 2
271
+ end
272
+
273
+ should "work with delete all and conditions" do
274
+ @project.statuses.count.should == 2
275
+ @project.statuses.delete_all(:name => '1')
276
+ @project.statuses.count.should == 1
277
+
278
+ @project2.statuses.count.should == 2
279
+ Status.count.should == 3
280
+ end
281
+
282
+ should "work with nullify" do
283
+ @project.statuses.count.should == 2
284
+ @project.statuses.nullify
285
+ @project.statuses.count.should == 0
286
+
287
+ @project2.statuses.count.should == 2
288
+ Status.count.should == 4
289
+ Status.count(:name => '1').should == 2
290
+ Status.count(:name => '2').should == 2
291
+ end
292
+ end
293
+
294
+ context "Finding scoped to association" do
295
+ setup do
296
+ @project1 = Project.new(:name => 'Project 1')
297
+ @brand_new = Status.create(:name => 'New', :position => 1 )
298
+ @complete = Status.create(:name => 'Complete', :position => 2)
299
+ @project1.statuses = [@brand_new, @complete]
300
+ @project1.save
301
+
302
+ @project2 = Project.create(:name => 'Project 2')
303
+ @in_progress = Status.create(:name => 'In Progress')
304
+ @archived = Status.create(:name => 'Archived')
305
+ @another_complete = Status.create(:name => 'Complete')
306
+ @project2.statuses = [@in_progress, @archived, @another_complete]
307
+ @project2.save
308
+ end
309
+
310
+ context "dynamic finders" do
311
+ should "work with single key" do
312
+ @project1.statuses.find_by_name('New').should == @brand_new
313
+ @project1.statuses.find_by_name!('New').should == @brand_new
314
+ @project2.statuses.find_by_name('In Progress').should == @in_progress
315
+ @project2.statuses.find_by_name!('In Progress').should == @in_progress
316
+ end
317
+
318
+ should "work with multiple keys" do
319
+ @project1.statuses.find_by_name_and_position('New', 1).should == @brand_new
320
+ @project1.statuses.find_by_name_and_position!('New', 1).should == @brand_new
321
+ @project1.statuses.find_by_name_and_position('New', 2).should be_nil
322
+ end
323
+
324
+ should "raise error when using !" do
325
+ lambda {
326
+ @project1.statuses.find_by_name!('Fake')
327
+ }.should raise_error(MongoMapper::DocumentNotFound)
328
+ end
329
+
330
+ context "find_or_create_by" do
331
+ should "not create document if found" do
332
+ lambda {
333
+ status = @project1.statuses.find_or_create_by_name('New')
334
+ status.project.should == @project1
335
+ status.should == @brand_new
336
+ }.should_not change { Status.count }
337
+ end
338
+
339
+ should "create document if not found" do
340
+ lambda {
341
+ status = @project1.statuses.find_or_create_by_name('Delivered')
342
+ status.project.should == @project1
343
+ }.should change { Status.count }
344
+ end
345
+ end
346
+ end
347
+
348
+ context "sexy querying" do
349
+ should "work with where" do
350
+ @project1.statuses.where(:name => 'New').all.should == [@brand_new]
351
+ end
352
+
353
+ should "work with sort" do
354
+ @project1.statuses.sort(:name).all.should == [@complete, @brand_new]
355
+ end
356
+
357
+ should "work with limit" do
358
+ @project1.statuses.sort(:name).limit(1).all.should == [@complete]
359
+ end
360
+
361
+ should "work with skip" do
362
+ @project1.statuses.sort(:name).skip(1).all.should == [@brand_new]
363
+ end
364
+
365
+ should "work with fields" do
366
+ @project1.statuses.fields(:position).all.each do |status|
367
+ status.position.should_not be_nil
368
+ status.name.should be_nil
369
+ end
370
+ end
371
+
372
+ should "work with scopes" do
373
+ @project1.statuses.complete.all.should == [@complete]
374
+ end
375
+
376
+ should "work with methods on class that return query" do
377
+ @project1.statuses.by_position(1).first.should == @brand_new
378
+ end
379
+
380
+ should "not work with methods on class that do not return query" do
381
+ Status.class_eval { def self.foo; 'foo' end }
382
+ lambda { @project1.statuses.foo }.
383
+ should raise_error(NoMethodError)
384
+ end
385
+ end
386
+
387
+ context "all" do
388
+ should "work" do
389
+ @project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
390
+ end
391
+
392
+ should "work with conditions" do
393
+ @project1.statuses.all(:name => 'Complete').should == [@complete]
394
+ end
395
+ end
396
+
397
+ context "first" do
398
+ should "work" do
399
+ @project1.statuses.first(:order => 'name').should == @complete
400
+ end
401
+
402
+ should "work with conditions" do
403
+ @project1.statuses.first(:name => 'Complete').should == @complete
404
+ end
405
+ end
406
+
407
+ context "last" do
408
+ should "work" do
409
+ @project1.statuses.last(:order => "position asc").should == @complete
410
+ end
411
+
412
+ should "work with conditions" do
413
+ @project1.statuses.last(:order => 'position', :name => 'New').should == @brand_new
414
+ end
415
+ end
416
+
417
+ context "with one id" do
418
+ should "work for id in association" do
419
+ @project1.statuses.find(@complete.id).should == @complete
420
+ end
421
+
422
+ should "not work for id not in association" do
423
+ lambda {
424
+ @project1.statuses.find!(@archived.id)
425
+ }.should raise_error(MongoMapper::DocumentNotFound)
426
+ end
427
+ end
428
+
429
+ context "with multiple ids" do
430
+ should "work for ids in association" do
431
+ statuses = @project1.statuses.find(@brand_new.id, @complete.id)
432
+ statuses.should == [@brand_new, @complete]
433
+ end
434
+
435
+ should "not work for ids not in association" do
436
+ assert_raises(MongoMapper::DocumentNotFound) do
437
+ @project1.statuses.find!(@brand_new.id, @complete.id, @archived.id)
438
+ end
439
+ end
440
+ end
441
+
442
+ context "with #paginate" do
443
+ setup do
444
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
445
+ end
446
+
447
+ should "return total pages" do
448
+ @statuses.total_pages.should == 2
449
+ end
450
+
451
+ should "return total entries" do
452
+ @statuses.total_entries.should == 3
453
+ end
454
+
455
+ should "return the subject" do
456
+ @statuses.collect(&:name).should == %w(Archived Complete)
457
+ end
458
+ end
459
+ end
460
+
461
+ context "extending the association" do
462
+ should "work using a block passed to many" do
463
+ project = Project.new(:name => "Some Project")
464
+ status1 = Status.new(:name => "New")
465
+ status2 = Status.new(:name => "Assigned")
466
+ status3 = Status.new(:name => "Closed")
467
+ project.statuses = [status1, status2, status3]
468
+ project.save
469
+
470
+ open_statuses = project.statuses.open
471
+ open_statuses.should include(status1)
472
+ open_statuses.should include(status2)
473
+ open_statuses.should_not include(status3)
474
+ end
475
+
476
+ should "work using many's :extend option" do
477
+ project = Project.new(:name => "Some Project")
478
+ collaborator1 = Collaborator.new(:name => "zing")
479
+ collaborator2 = Collaborator.new(:name => "zang")
480
+ project.collaborators = [collaborator1, collaborator2]
481
+ project.save
482
+ project.collaborators.top.should == collaborator1
483
+ end
484
+ end
485
+
486
+ context ":dependent" do
487
+ setup do
488
+ # FIXME: make use of already defined models
489
+ class ::Property
490
+ include MongoMapper::Document
491
+ end
492
+ Property.collection.remove
493
+
494
+ class ::Thing
495
+ include MongoMapper::Document
496
+ key :name, String
497
+ end
498
+ Thing.collection.remove
499
+ end
500
+
501
+ teardown do
502
+ Object.send :remove_const, 'Property' if defined?(::Property)
503
+ Object.send :remove_const, 'Thing' if defined?(::Thing)
504
+ end
505
+
506
+ context "=> destroy" do
507
+ setup do
508
+ Property.key :thing_id, ObjectId
509
+ Property.belongs_to :thing, :dependent => :destroy
510
+ Thing.many :properties, :dependent => :destroy
511
+
512
+ @thing = Thing.create(:name => "Tree")
513
+ @property1 = Property.create
514
+ @property2 = Property.create
515
+ @property3 = Property.create
516
+ @thing.properties << @property1
517
+ @thing.properties << @property2
518
+ @thing.properties << @property3
519
+ end
520
+
521
+ should "should destroy the associated documents" do
522
+ @thing.properties.count.should == 3
523
+ @thing.destroy
524
+ @thing.properties.count.should == 0
525
+ Property.count.should == 0
526
+ end
527
+ end
528
+
529
+ context "=> delete_all" do
530
+ setup do
531
+ Property.key :thing_id, ObjectId
532
+ Property.belongs_to :thing
533
+ Thing.has_many :properties, :dependent => :delete_all
534
+
535
+ @thing = Thing.create(:name => "Tree")
536
+ @property1 = Property.create
537
+ @property2 = Property.create
538
+ @property3 = Property.create
539
+ @thing.properties << @property1
540
+ @thing.properties << @property2
541
+ @thing.properties << @property3
542
+ end
543
+
544
+ should "should delete associated documents" do
545
+ @thing.properties.count.should == 3
546
+ @thing.destroy
547
+ @thing.properties.count.should == 0
548
+ Property.count.should == 0
549
+ end
550
+ end
551
+
552
+ context "=> nullify" do
553
+ setup do
554
+ Property.key :thing_id, ObjectId
555
+ Property.belongs_to :thing
556
+ Thing.has_many :properties, :dependent => :nullify
557
+
558
+ @thing = Thing.create(:name => "Tree")
559
+ @property1 = Property.create
560
+ @property2 = Property.create
561
+ @property3 = Property.create
562
+ @thing.properties << @property1
563
+ @thing.properties << @property2
564
+ @thing.properties << @property3
565
+ end
566
+
567
+ should "should nullify relationship but not destroy associated documents" do
568
+ @thing.properties.count.should == 3
569
+ @thing.destroy
570
+ @thing.properties.count.should == 0
571
+ Property.count.should == 3
572
+ end
573
+ end
574
+ end
575
+ end