mongo_mapper 0.5.0

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