mongo_mapper 0.5.0

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