mongo_mapper-unstable 2009.10.11

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 (73) hide show
  1. data/.gitignore +8 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +50 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper/associations/base.rb +83 -0
  8. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  9. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  10. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
  11. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  12. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  13. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
  14. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  15. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  16. data/lib/mongo_mapper/associations/proxy.rb +74 -0
  17. data/lib/mongo_mapper/associations.rb +86 -0
  18. data/lib/mongo_mapper/callbacks.rb +106 -0
  19. data/lib/mongo_mapper/dirty.rb +137 -0
  20. data/lib/mongo_mapper/document.rb +340 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +355 -0
  23. data/lib/mongo_mapper/finder_options.rb +98 -0
  24. data/lib/mongo_mapper/key.rb +36 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -0
  27. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  29. data/lib/mongo_mapper/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +161 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/lib/mongo_mapper.rb +111 -0
  35. data/mongo_mapper.gemspec +162 -0
  36. data/specs.watchr +32 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/custom_matchers.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  40. data/test/functional/associations/test_belongs_to_proxy.rb +49 -0
  41. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  45. data/test/functional/associations/test_many_proxy.rb +331 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +18 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +138 -0
  50. data/test/functional/test_document.rb +1051 -0
  51. data/test/functional/test_embedded_document.rb +97 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_pagination.rb +87 -0
  54. data/test/functional/test_rails_compatibility.rb +30 -0
  55. data/test/functional/test_validations.rb +279 -0
  56. data/test/models.rb +195 -0
  57. data/test/test_helper.rb +30 -0
  58. data/test/unit/serializers/test_json_serializer.rb +189 -0
  59. data/test/unit/test_association_base.rb +144 -0
  60. data/test/unit/test_document.rb +184 -0
  61. data/test/unit/test_dynamic_finder.rb +125 -0
  62. data/test/unit/test_embedded_document.rb +656 -0
  63. data/test/unit/test_finder_options.rb +261 -0
  64. data/test/unit/test_key.rb +172 -0
  65. data/test/unit/test_mongomapper.rb +28 -0
  66. data/test/unit/test_observing.rb +101 -0
  67. data/test/unit/test_pagination.rb +109 -0
  68. data/test/unit/test_rails_compatibility.rb +39 -0
  69. data/test/unit/test_serializations.rb +52 -0
  70. data/test/unit/test_support.rb +291 -0
  71. data/test/unit/test_time_zones.rb +40 -0
  72. data/test/unit/test_validations.rb +503 -0
  73. metadata +210 -0
@@ -0,0 +1,1051 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class DocumentTest < Test::Unit::TestCase
5
+ def setup
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ set_collection_name 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ key :age, Integer
13
+ key :date, Date
14
+ end
15
+ @document.collection.clear
16
+ end
17
+
18
+ context "Saving a document with a custom id" do
19
+ should "clear custom id flag when saved" do
20
+ doc = @document.new(:id => '1234')
21
+ doc.using_custom_id?.should be_true
22
+ doc.save.should be_true
23
+ doc.using_custom_id?.should be_false
24
+ end
25
+ end
26
+
27
+ context "Saving a document with a blank binary value" do
28
+ setup do
29
+ @document.key :file, Binary
30
+ end
31
+
32
+ should "not fail" do
33
+ doc = @document.new(:file => nil)
34
+ doc.save
35
+ end
36
+ end
37
+
38
+ context "Loading a document from the database with keys that are not defined" do
39
+ setup do
40
+ @id = Mongo::ObjectID.new.to_s
41
+ @document.collection.insert({
42
+ :_id => @id,
43
+ :first_name => 'John',
44
+ :last_name => 'Nunemaker',
45
+ :age => 27,
46
+ :favorite_color => 'red',
47
+ :skills => ['ruby', 'rails', 'javascript', 'xhtml', 'css']
48
+ })
49
+ end
50
+
51
+ should "assign all keys from database" do
52
+ doc = @document.find(@id)
53
+ doc.first_name.should == 'John'
54
+ doc.last_name.should == 'Nunemaker'
55
+ doc.age.should == 27
56
+ doc.favorite_color.should == 'red'
57
+ doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
58
+ end
59
+ end
60
+
61
+ context "Document Class Methods" do
62
+ context "Using key with type Array" do
63
+ setup do
64
+ @document.key :tags, Array
65
+ end
66
+
67
+ should "give correct default" do
68
+ doc = @document.new
69
+ doc.tags.should == []
70
+ end
71
+
72
+ should "work with assignment" do
73
+ doc = @document.new
74
+ doc.tags = %w(foo bar)
75
+ doc.tags.should == %w(foo bar)
76
+ end
77
+
78
+ should "work with assignment after saving" do
79
+ doc = @document.new
80
+ doc.tags = %w(foo bar)
81
+ doc.save
82
+ doc.tags.should == %w(foo bar)
83
+ @document.find(doc.id).tags.should == %w(foo bar)
84
+ end
85
+
86
+ should "work with assignment then <<" do
87
+ doc = @document.new
88
+ doc.tags = []
89
+ doc.tags << "foo"
90
+ doc.tags.should == ["foo"]
91
+ end
92
+
93
+ should "work with <<" do
94
+ doc = @document.new
95
+ doc.tags << "foo"
96
+ doc.tags.should == ["foo"]
97
+ end
98
+
99
+ should "work with << then save" do
100
+ doc = @document.new
101
+ doc.tags << "foo"
102
+ doc.tags << "bar"
103
+ doc.save
104
+ doc.tags.should == %w(foo bar)
105
+ @document.find(doc.id).tags.should == %w(foo bar)
106
+ end
107
+ end
108
+
109
+ context "Using key with type Hash" do
110
+ setup do
111
+ @document.key :foo, Hash
112
+ end
113
+
114
+ should "give correct default" do
115
+ doc = @document.new
116
+ doc.foo.should == {}
117
+ end
118
+
119
+ should "work with []=" do
120
+ doc = @document.new
121
+ doc.foo["quux"] = "bar"
122
+ doc.foo["quux"].should == "bar"
123
+ doc.foo.should == { "quux" => "bar" }
124
+ end
125
+
126
+ should "work with indifferent access" do
127
+ doc = @document.new
128
+ doc.foo = {:baz => 'bar'}
129
+ doc.foo[:baz].should == 'bar'
130
+ doc.foo['baz'].should == 'bar'
131
+ end
132
+
133
+ should "work with indifferent access after save" do
134
+ doc = @document.new
135
+ doc.foo = {:baz => 'bar'}
136
+ doc.save
137
+
138
+ doc = @document.find(doc.id)
139
+ doc.foo[:baz].should == 'bar'
140
+ doc.foo['baz'].should == 'bar'
141
+ end
142
+ end
143
+
144
+ context "Using key with custom type with default" do
145
+ setup do
146
+ @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
147
+ end
148
+
149
+ should "default to default" do
150
+ doc = @document.new
151
+ doc.window.should == WindowSize.new(600, 480)
152
+
153
+ end
154
+
155
+ should "save and load from mongo" do
156
+ doc = @document.new
157
+ doc.save
158
+
159
+ from_db = @document.find(doc.id)
160
+ from_db.window.should == WindowSize.new(600, 480)
161
+ end
162
+ end
163
+
164
+
165
+ context "Creating a single document" do
166
+ setup do
167
+ @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
168
+ end
169
+
170
+ should "create a document in correct collection" do
171
+ @document.count.should == 1
172
+ end
173
+
174
+ should "automatically set id" do
175
+ @doc_instance.id.should_not be_nil
176
+ @doc_instance.id.size.should == 24
177
+ end
178
+
179
+ should "no longer be new?" do
180
+ @doc_instance.new?.should be_false
181
+ end
182
+
183
+ should "return instance of document" do
184
+ @doc_instance.should be_instance_of(@document)
185
+ @doc_instance.first_name.should == 'John'
186
+ @doc_instance.last_name.should == 'Nunemaker'
187
+ @doc_instance.age.should == 27
188
+ end
189
+ end
190
+
191
+ context "Creating a document with no attributes provided" do
192
+ setup do
193
+ @document = Class.new do
194
+ include MongoMapper::Document
195
+ set_collection_name 'test'
196
+ end
197
+ @document.collection.clear
198
+ end
199
+
200
+ should "create the document" do
201
+ lambda {
202
+ @document.create
203
+ }.should change { @document.count }.by(1)
204
+ end
205
+ end
206
+
207
+ context "Creating multiple documents" do
208
+ setup do
209
+ @doc_instances = @document.create([
210
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
211
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
212
+ ])
213
+ end
214
+
215
+ should "create multiple documents" do
216
+ @document.count.should == 2
217
+ end
218
+
219
+ should "return an array of doc instances" do
220
+ @doc_instances.map do |doc_instance|
221
+ doc_instance.should be_instance_of(@document)
222
+ end
223
+ end
224
+ end
225
+
226
+ context "Updating a document" do
227
+ setup do
228
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
229
+ @doc_instance = @document.update(doc.id, {:age => 40})
230
+ end
231
+
232
+ should "update attributes provided" do
233
+ @doc_instance.age.should == 40
234
+ end
235
+
236
+ should "not update existing attributes that were not set to update" do
237
+ @doc_instance.first_name.should == 'John'
238
+ @doc_instance.last_name.should == 'Nunemaker'
239
+ end
240
+
241
+ should "not create new document" do
242
+ @document.count.should == 1
243
+ end
244
+ end
245
+
246
+ should "raise error when updating single doc if not provided id and attributes" do
247
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
248
+ lambda { @document.update }.should raise_error(ArgumentError)
249
+ lambda { @document.update(doc.id) }.should raise_error(ArgumentError)
250
+ lambda { @document.update(doc.id, [1]) }.should raise_error(ArgumentError)
251
+ end
252
+
253
+ context "Updating multiple documents" do
254
+ setup do
255
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
256
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
257
+
258
+ @doc_instances = @document.update({
259
+ @doc1.id => {:age => 30},
260
+ @doc2.id => {:age => 30},
261
+ })
262
+ end
263
+
264
+ should "not create any new documents" do
265
+ @document.count.should == 2
266
+ end
267
+
268
+ should "should return an array of doc instances" do
269
+ @doc_instances.map do |doc_instance|
270
+ doc_instance.should be_instance_of(@document)
271
+ end
272
+ end
273
+
274
+ should "update the documents" do
275
+ @document.find(@doc1.id).age.should == 30
276
+ @document.find(@doc2.id).age.should == 30
277
+ end
278
+ end
279
+
280
+ should "raise error when updating multiple documents if not a hash" do
281
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
282
+ end
283
+
284
+ context "Finding documents" do
285
+ setup do
286
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
287
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
288
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
289
+ end
290
+
291
+ should "raise document not found if nothing provided" do
292
+ lambda { @document.find }.should raise_error(MongoMapper::DocumentNotFound)
293
+ end
294
+
295
+ context "with a single id" do
296
+ should "work" do
297
+ @document.find(@doc1.id).should == @doc1
298
+ end
299
+
300
+ should "raise error if document not found" do
301
+ lambda { @document.find(123) }.should raise_error(MongoMapper::DocumentNotFound)
302
+ end
303
+ end
304
+
305
+ context "with multiple id's" do
306
+ should "work as arguments" do
307
+ @document.find(@doc1.id, @doc2.id).should == [@doc1, @doc2]
308
+ end
309
+
310
+ should "work as array" do
311
+ @document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
312
+ end
313
+
314
+ should "return array if array only has one element" do
315
+ @document.find([@doc1.id]).should == [@doc1]
316
+ end
317
+ end
318
+
319
+ should "be able to find using condition auto-detection" do
320
+ @document.first(:first_name => 'John').should == @doc1
321
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
322
+ end
323
+
324
+ context "with :all" do
325
+ should "find all documents" do
326
+ @document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
327
+ end
328
+
329
+ should "be able to add conditions" do
330
+ @document.find(:all, :conditions => {:first_name => 'John'}).should == [@doc1]
331
+ end
332
+ end
333
+
334
+ context "with #all" do
335
+ should "find all documents based on criteria" do
336
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
337
+ @document.all(:conditions => {:last_name => 'Nunemaker'}, :order => 'age desc').should == [@doc1, @doc3]
338
+ end
339
+ end
340
+
341
+ context "with :first" do
342
+ should "find first document" do
343
+ @document.find(:first, :order => 'first_name').should == @doc1
344
+ end
345
+ end
346
+
347
+ context "with #first" do
348
+ should "find first document based on criteria" do
349
+ @document.first(:order => 'first_name').should == @doc1
350
+ @document.first(:conditions => {:age => 28}).should == @doc2
351
+ end
352
+ end
353
+
354
+ context "with :last" do
355
+ should "find last document" do
356
+ @document.find(:last, :order => 'age').should == @doc2
357
+ end
358
+ end
359
+
360
+ context "with #last" do
361
+ should "find last document based on criteria" do
362
+ @document.last(:order => 'age').should == @doc2
363
+ @document.last(:order => 'age', :conditions => {:age => 28}).should == @doc2
364
+ end
365
+
366
+ should "raise error if no order provided" do
367
+ lambda { @document.last() }.should raise_error
368
+ end
369
+ end
370
+
371
+ context "with :find_by" do
372
+ should "find document based on argument" do
373
+ @document.find_by_first_name('John').should == @doc1
374
+ @document.find_by_last_name('Nunemaker', :order => 'age desc').should == @doc1
375
+ @document.find_by_age(27).should == @doc1
376
+ end
377
+
378
+ should "not raise error" do
379
+ @document.find_by_first_name('Mongo').should be_nil
380
+ end
381
+
382
+ should "define a method for each key" do
383
+ @document.methods(false).select { |e| e =~ /^find_by_/ }.size == @document.keys.size
384
+ end
385
+ end
386
+
387
+ context "with dynamic finders" do
388
+ should "find document based on all arguments" do
389
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 27).should == @doc1
390
+ end
391
+
392
+ should "not find the document if an argument is wrong" do
393
+ @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 28).should be_nil
394
+ end
395
+
396
+ should "find all documents based on arguments" do
397
+ docs = @document.find_all_by_last_name('Nunemaker')
398
+ docs.should be_kind_of(Array)
399
+ docs.should include(@doc1)
400
+ docs.should include(@doc3)
401
+ end
402
+
403
+ should "initialize document with given arguments" do
404
+ doc = @document.find_or_initialize_by_first_name_and_last_name('David', 'Cuadrado')
405
+ doc.should be_new
406
+ doc.first_name.should == 'David'
407
+ end
408
+
409
+ should "not initialize document if document is found" do
410
+ doc = @document.find_or_initialize_by_first_name('John')
411
+ doc.should_not be_new
412
+ end
413
+
414
+ should "create document with given arguments" do
415
+ doc = @document.find_or_create_by_first_name_and_last_name('David', 'Cuadrado')
416
+ doc.should_not be_new
417
+ doc.first_name.should == 'David'
418
+ end
419
+
420
+ should "raise error if document is not found when using !" do
421
+ lambda {
422
+ @document.find_by_first_name_and_last_name!(1,2)
423
+ }.should raise_error(MongoMapper::DocumentNotFound)
424
+ end
425
+ end
426
+ end # finding documents
427
+
428
+ context "Finding document by id" do
429
+ setup do
430
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
431
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
432
+ end
433
+
434
+ should "be able to find by id" do
435
+ @document.find_by_id(@doc1.id).should == @doc1
436
+ @document.find_by_id(@doc2.id).should == @doc2
437
+ end
438
+
439
+ should "return nil if document not found" do
440
+ @document.find_by_id(1234).should be(nil)
441
+ end
442
+ end
443
+
444
+ context "Deleting a document" do
445
+ setup do
446
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
447
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
448
+ @document.delete(@doc1.id)
449
+ end
450
+
451
+ should "remove document from collection" do
452
+ @document.count.should == 1
453
+ end
454
+
455
+ should "not remove other documents" do
456
+ @document.find(@doc2.id).should_not be(nil)
457
+ end
458
+ end
459
+
460
+ context "Deleting multiple documents" do
461
+ should "work with multiple arguments" do
462
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
463
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
464
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
465
+ @document.delete(@doc1.id, @doc2.id)
466
+
467
+ @document.count.should == 1
468
+ end
469
+
470
+ should "work with array as argument" do
471
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
472
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
473
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
474
+ @document.delete([@doc1.id, @doc2.id])
475
+
476
+ @document.count.should == 1
477
+ end
478
+ end
479
+
480
+ context "Deleting all documents" do
481
+ setup do
482
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
483
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
484
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
485
+ end
486
+
487
+ should "remove all documents when given no conditions" do
488
+ @document.delete_all
489
+ @document.count.should == 0
490
+ end
491
+
492
+ should "only remove matching documents when given conditions" do
493
+ @document.delete_all({:first_name => 'John'})
494
+ @document.count.should == 2
495
+ end
496
+
497
+ should "convert the conditions to mongo criteria" do
498
+ @document.delete_all(:age => [26, 27])
499
+ @document.count.should == 1
500
+ end
501
+ end
502
+
503
+ context "Destroying a document" do
504
+ setup do
505
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
506
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
507
+ @document.destroy(@doc1.id)
508
+ end
509
+
510
+ should "remove document from collection" do
511
+ @document.count.should == 1
512
+ end
513
+
514
+ should "not remove other documents" do
515
+ @document.find(@doc2.id).should_not be(nil)
516
+ end
517
+ end
518
+
519
+ context "Destroying multiple documents" do
520
+ should "work with multiple arguments" do
521
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
522
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
523
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
524
+ @document.destroy(@doc1.id, @doc2.id)
525
+
526
+ @document.count.should == 1
527
+ end
528
+
529
+ should "work with array as argument" do
530
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
531
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
532
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
533
+ @document.destroy([@doc1.id, @doc2.id])
534
+
535
+ @document.count.should == 1
536
+ end
537
+ end
538
+
539
+ context "Destroying all documents" do
540
+ setup do
541
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
542
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
543
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
544
+ end
545
+
546
+ should "remove all documents when given no conditions" do
547
+ @document.destroy_all
548
+ @document.count.should == 0
549
+ end
550
+
551
+ should "only remove matching documents when given conditions" do
552
+ @document.destroy_all(:first_name => 'John')
553
+ @document.count.should == 2
554
+ @document.destroy_all(:age => 26)
555
+ @document.count.should == 1
556
+ end
557
+
558
+ should "convert the conditions to mongo criteria" do
559
+ @document.destroy_all(:age => [26, 27])
560
+ @document.count.should == 1
561
+ end
562
+ end
563
+
564
+ context ":dependent" do
565
+ setup do
566
+ # FIXME: make use of already defined models
567
+ class ::Property
568
+ include MongoMapper::Document
569
+ end
570
+ Property.collection.clear
571
+
572
+ class ::Thing
573
+ include MongoMapper::Document
574
+ key :name, String
575
+ end
576
+ Thing.collection.clear
577
+ end
578
+
579
+ teardown do
580
+ Object.send :remove_const, 'Property' if defined?(::Property)
581
+ Object.send :remove_const, 'Thing' if defined?(::Thing)
582
+ end
583
+
584
+ context "many" do
585
+ context "=> destroy" do
586
+ setup do
587
+ Property.key :thing_id, String
588
+ Property.belongs_to :thing, :dependent => :destroy
589
+ Thing.many :properties, :dependent => :destroy
590
+
591
+ @thing = Thing.create(:name => "Tree")
592
+ @property1 = Property.create
593
+ @property2 = Property.create
594
+ @property3 = Property.create
595
+ @thing.properties << @property1
596
+ @thing.properties << @property2
597
+ @thing.properties << @property3
598
+ end
599
+
600
+ should "should destroy the associated documents" do
601
+ @thing.properties.count.should == 3
602
+ @thing.destroy
603
+ @thing.properties.count.should == 0
604
+ Property.count.should == 0
605
+ end
606
+ end
607
+
608
+ context "=> delete_all" do
609
+ setup do
610
+ Property.key :thing_id, String
611
+ Property.belongs_to :thing
612
+ Thing.has_many :properties, :dependent => :delete_all
613
+
614
+ @thing = Thing.create(:name => "Tree")
615
+ @property1 = Property.create
616
+ @property2 = Property.create
617
+ @property3 = Property.create
618
+ @thing.properties << @property1
619
+ @thing.properties << @property2
620
+ @thing.properties << @property3
621
+ end
622
+
623
+ should "should delete associated documents" do
624
+ @thing.properties.count.should == 3
625
+ @thing.destroy
626
+ @thing.properties.count.should == 0
627
+ Property.count.should == 0
628
+ end
629
+ end
630
+
631
+ context "=> nullify" do
632
+ setup do
633
+ Property.key :thing_id, String
634
+ Property.belongs_to :thing
635
+ Thing.has_many :properties, :dependent => :nullify
636
+
637
+ @thing = Thing.create(:name => "Tree")
638
+ @property1 = Property.create
639
+ @property2 = Property.create
640
+ @property3 = Property.create
641
+ @thing.properties << @property1
642
+ @thing.properties << @property2
643
+ @thing.properties << @property3
644
+ end
645
+
646
+ should "should nullify relationship but not destroy associated documents" do
647
+ @thing.properties.count.should == 3
648
+ @thing.destroy
649
+ @thing.properties.count.should == 0
650
+ Property.count.should == 3
651
+ end
652
+ end
653
+ end
654
+
655
+ context "belongs_to" do
656
+ context "=> destroy" do
657
+ setup do
658
+ Property.key :thing_id, String
659
+ Property.belongs_to :thing, :dependent => :destroy
660
+ Thing.has_many :properties
661
+
662
+ @thing = Thing.create(:name => "Tree")
663
+ @property1 = Property.create
664
+ @property2 = Property.create
665
+ @property3 = Property.create
666
+ @thing.properties << @property1
667
+ @thing.properties << @property2
668
+ @thing.properties << @property3
669
+ end
670
+
671
+ should "destroy the thing" do
672
+ Thing.count.should == 1
673
+ @property1.destroy
674
+ Thing.count.should == 0
675
+ @property1.thing.should be_frozen
676
+ end
677
+ end
678
+ end
679
+ end
680
+
681
+ context "Counting documents in collection" do
682
+ setup do
683
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
684
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
685
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
686
+ end
687
+
688
+ should "count all with no arguments" do
689
+ @document.count.should == 3
690
+ end
691
+
692
+ should "return 0 if there are no documents in the collection" do
693
+ @document.delete_all
694
+ @document.count.should == 0
695
+ end
696
+
697
+ should "return 0 if the collection does not exist" do
698
+ klass = Class.new do
699
+ include MongoMapper::Document
700
+ set_collection_name 'foobarbazwickdoesnotexist'
701
+ end
702
+ @document.collection.clear
703
+
704
+ klass.count.should == 0
705
+ end
706
+
707
+ should "return count for matching documents if conditions provided" do
708
+ @document.count(:age => 27).should == 1
709
+ end
710
+
711
+ should "convert the conditions to mongo criteria" do
712
+ @document.count(:age => [26, 27]).should == 2
713
+ end
714
+ end
715
+
716
+ context "Indexing" do
717
+ setup do
718
+ @document.collection.drop_indexes
719
+ end
720
+
721
+ should "allow creating index for a key" do
722
+ @document.ensure_index :first_name
723
+ MongoMapper.ensure_indexes!
724
+
725
+ @document.should have_index('first_name_1')
726
+ end
727
+
728
+ should "allow creating unique index for a key" do
729
+ @document.ensure_index :first_name, :unique => true
730
+ MongoMapper.ensure_indexes!
731
+
732
+ @document.should have_index('first_name_1')
733
+ end
734
+
735
+ should "allow creating index on multiple keys" do
736
+ @document.ensure_index [[:first_name, 1], [:last_name, -1]]
737
+ MongoMapper.ensure_indexes!
738
+
739
+ @document.should have_index('last_name_-1_first_name_1')
740
+ end
741
+
742
+ should "work with :index shortcut when defining key" do
743
+ @document.key :father, String, :index => true
744
+ MongoMapper.ensure_indexes!
745
+
746
+ @document.should have_index('father_1')
747
+ end
748
+ end
749
+ end # Document Class Methods
750
+
751
+ context "Saving a new document" do
752
+ setup do
753
+ @doc = @document.new(:first_name => 'John', :age => '27')
754
+ @doc.save
755
+ end
756
+
757
+ should "insert document into the collection" do
758
+ @document.count.should == 1
759
+ end
760
+
761
+ should "assign an id for the document" do
762
+ @doc.id.should_not be(nil)
763
+ @doc.id.size.should == 24
764
+ end
765
+
766
+ should "save attributes" do
767
+ @doc.first_name.should == 'John'
768
+ @doc.age.should == 27
769
+ end
770
+
771
+ should "update attributes in the database" do
772
+ from_db = @document.find(@doc.id)
773
+ from_db.should == @doc
774
+ from_db.first_name.should == 'John'
775
+ from_db.age.should == 27
776
+ end
777
+
778
+ should "allow to add custom attributes to the document" do
779
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
780
+ @doc.save
781
+ from_db = @document.find(@doc.id)
782
+ from_db.gender.should == 'male'
783
+ from_db.tags.should == [1, "2"]
784
+ end
785
+
786
+ should "allow to use custom methods to assign properties" do
787
+ person = RealPerson.new(:realname => "David")
788
+ person.save
789
+ from_db = RealPerson.find(person.id)
790
+ from_db.name.should == "David"
791
+ end
792
+
793
+ context "with key of type date" do
794
+ should "save the date value as a Time object" do
795
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
796
+ doc.save
797
+ doc.date.should == Date.new(2009, 12, 1)
798
+ end
799
+ end
800
+ end
801
+
802
+ context "Saving an existing document" do
803
+ setup do
804
+ @doc = @document.create(:first_name => 'John', :age => '27')
805
+ @doc.first_name = 'Johnny'
806
+ @doc.age = 30
807
+ @doc.save
808
+ end
809
+
810
+ should "not insert document into collection" do
811
+ @document.count.should == 1
812
+ end
813
+
814
+ should "update attributes" do
815
+ @doc.first_name.should == 'Johnny'
816
+ @doc.age.should == 30
817
+ end
818
+
819
+ should "update attributes in the database" do
820
+ from_db = @document.find(@doc.id)
821
+ from_db.first_name.should == 'Johnny'
822
+ from_db.age.should == 30
823
+ end
824
+
825
+ should "allow updating custom attributes" do
826
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
827
+ @doc.gender = 'Male'
828
+ @doc.save
829
+ from_db = @document.find(@doc.id)
830
+ from_db.gender.should == 'Male'
831
+ end
832
+ end
833
+
834
+ context "Calling update attributes on a new document" do
835
+ setup do
836
+ @doc = @document.new(:first_name => 'John', :age => '27')
837
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
838
+ end
839
+
840
+ should "insert document into the collection" do
841
+ @document.count.should == 1
842
+ end
843
+
844
+ should "assign an id for the document" do
845
+ @doc.id.should_not be(nil)
846
+ @doc.id.size.should == 24
847
+ end
848
+
849
+ should "save attributes" do
850
+ @doc.first_name.should == 'Johnny'
851
+ @doc.age.should == 30
852
+ end
853
+
854
+ should "update attributes in the database" do
855
+ from_db = @document.find(@doc.id)
856
+ from_db.should == @doc
857
+ from_db.first_name.should == 'Johnny'
858
+ from_db.age.should == 30
859
+ end
860
+
861
+ should "allow updating custom attributes" do
862
+ @doc.update_attributes(:gender => 'mALe')
863
+ from_db = @document.find(@doc.id)
864
+ from_db.gender.should == 'mALe'
865
+ end
866
+ end
867
+
868
+ context "Updating an existing document using update attributes" do
869
+ setup do
870
+ @doc = @document.create(:first_name => 'John', :age => '27')
871
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
872
+ end
873
+
874
+ should "not insert document into collection" do
875
+ @document.count.should == 1
876
+ end
877
+
878
+ should "update attributes" do
879
+ @doc.first_name.should == 'Johnny'
880
+ @doc.age.should == 30
881
+ end
882
+
883
+ should "update attributes in the database" do
884
+ from_db = @document.find(@doc.id)
885
+ from_db.first_name.should == 'Johnny'
886
+ from_db.age.should == 30
887
+ end
888
+ end
889
+
890
+ context "update_attributes" do
891
+ setup do
892
+ @document.key :foo, String, :required => true
893
+ end
894
+
895
+ should "return true if document valid" do
896
+ @document.new.update_attributes(:foo => 'bar').should be_true
897
+ end
898
+
899
+ should "return false if document not valid" do
900
+ @document.new.update_attributes({}).should be_false
901
+ end
902
+ end
903
+
904
+ context "Destroying a document that exists" do
905
+ setup do
906
+ @doc = @document.create(:first_name => 'John', :age => '27')
907
+ @doc.destroy
908
+ end
909
+
910
+ should "remove the document from the collection" do
911
+ @document.count.should == 0
912
+ end
913
+
914
+ should "raise error if assignment is attempted" do
915
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
916
+ end
917
+
918
+ should "do nothing if destroy is called again" do
919
+ @doc.destroy.should be_false
920
+ end
921
+ end
922
+
923
+ context "Destroying a document that is a new" do
924
+ setup do
925
+ setup do
926
+ @doc = @document.new(:first_name => 'John Nunemaker', :age => '27')
927
+ @doc.destroy
928
+ end
929
+
930
+ should "not affect collection count" do
931
+ @document.collection.count.should == 0
932
+ end
933
+
934
+ should "raise error if assignment is attempted" do
935
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
936
+ end
937
+ end
938
+ end
939
+
940
+ context "Single collection inheritance" do
941
+ setup do
942
+ class ::DocParent
943
+ include MongoMapper::Document
944
+ key :_type, String
945
+ end
946
+
947
+ class ::DocChild < ::DocParent; end
948
+ DocParent.collection.clear
949
+
950
+ @parent = DocParent.new({:name => "Daddy Warbucks"})
951
+ @child = DocChild.new({:name => "Little Orphan Annie"})
952
+ end
953
+
954
+ teardown do
955
+ Object.send :remove_const, 'DocParent' if defined?(::DocParent)
956
+ Object.send :remove_const, 'DocChild' if defined?(::DocChild)
957
+ end
958
+
959
+ should "use the same collection in the subclass" do
960
+ DocChild.collection.name.should == DocParent.collection.name
961
+ end
962
+
963
+ should "assign the class name into the _type property" do
964
+ @parent._type.should == 'DocParent'
965
+ @child._type.should == 'DocChild'
966
+ end
967
+
968
+ should "load the document with the assigned type" do
969
+ @parent.save
970
+ @child.save
971
+
972
+ collection = DocParent.find(:all)
973
+ collection.size.should == 2
974
+ collection.first.should be_kind_of(DocParent)
975
+ collection.first.name.should == "Daddy Warbucks"
976
+ collection.last.should be_kind_of(DocChild)
977
+ collection.last.name.should == "Little Orphan Annie"
978
+ end
979
+
980
+ should "gracefully handle when the type can't be constantized" do
981
+ doc = DocParent.new(:name => 'Nunes')
982
+ doc._type = 'FoobarBaz'
983
+ doc.save
984
+
985
+ collection = DocParent.all
986
+ collection.last.should == doc
987
+ collection.last.should be_kind_of(DocParent)
988
+ end
989
+ end
990
+
991
+ context "timestamping" do
992
+ setup do
993
+ @document.timestamps!
994
+ end
995
+
996
+ should "set created_at and updated_at on create" do
997
+ doc = @document.new(:first_name => 'John', :age => 27)
998
+ doc.created_at.should be(nil)
999
+ doc.updated_at.should be(nil)
1000
+ doc.save
1001
+ doc.created_at.should_not be(nil)
1002
+ doc.updated_at.should_not be(nil)
1003
+ end
1004
+
1005
+ should "set updated_at on field update but leave created_at alone" do
1006
+ doc = @document.create(:first_name => 'John', :age => 27)
1007
+ old_created_at = doc.created_at
1008
+ old_updated_at = doc.updated_at
1009
+ doc.first_name = 'Johnny'
1010
+ sleep 1 # this annoys me
1011
+ doc.save
1012
+ doc.created_at.should == old_created_at
1013
+ doc.updated_at.should_not == old_updated_at
1014
+ end
1015
+
1016
+ should "set updated_at on document update but leave created_at alone" do
1017
+ doc = @document.create(:first_name => 'John', :age => 27)
1018
+ old_created_at = doc.created_at
1019
+ old_updated_at = doc.updated_at
1020
+ sleep 1 # this annoys me
1021
+ @document.update(doc._id, { :first_name => 'Johnny' })
1022
+
1023
+ from_db = @document.find(doc.id)
1024
+ from_db.created_at.to_i.should == old_created_at.to_i
1025
+ from_db.updated_at.to_i.should_not == old_updated_at.to_i
1026
+ end
1027
+ end
1028
+
1029
+ context "#exist?" do
1030
+ setup do
1031
+ @doc = @document.create(:first_name => "James", :age => 27)
1032
+ end
1033
+
1034
+ should "be true when at least one document exists" do
1035
+ @document.exists?.should == true
1036
+ end
1037
+
1038
+ should "be false when no documents exist" do
1039
+ @doc.destroy
1040
+ @document.exists?.should == false
1041
+ end
1042
+
1043
+ should "be true when at least one document exists that matches the conditions" do
1044
+ @document.exists?(:first_name => "James").should == true
1045
+ end
1046
+
1047
+ should "be false when no documents exist with the provided conditions" do
1048
+ @document.exists?(:first_name => "Jean").should == false
1049
+ end
1050
+ end
1051
+ end