numon 0.0.1

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