danielharan-mongo_mapper 0.6.5

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