jnunemaker-mongomapper 0.1.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.
@@ -0,0 +1,938 @@
1
+ require 'test_helper'
2
+
3
+ class Address
4
+ include MongoMapper::EmbeddedDocument
5
+ key :city, String
6
+ key :state, String
7
+ end
8
+
9
+ class DocumentTest < Test::Unit::TestCase
10
+ context "The Document Class" do
11
+ setup do
12
+ @document = Class.new do
13
+ include MongoMapper::Document
14
+ end
15
+ end
16
+
17
+ should "should be able to define a key" do
18
+ key = @document.key(:name, String)
19
+ key.name.should == 'name'
20
+ key.type.should == String
21
+ key.should be_instance_of(MongoMapper::Key)
22
+ end
23
+
24
+ should "be able to define a key with options" do
25
+ key = @document.key(:name, String, :required => true)
26
+ key.options[:required].should be(true)
27
+ end
28
+
29
+ should "know what keys have been defined" do
30
+ @document.key(:name, String)
31
+ @document.key(:age, Integer)
32
+ @document.keys['name'].name.should == 'name'
33
+ @document.keys['name'].type.should == String
34
+ @document.keys['age'].name.should == 'age'
35
+ @document.keys['age'].type.should == Integer
36
+ end
37
+
38
+ should "use default database by default" do
39
+ @document.database.should == MongoMapper.database
40
+ end
41
+
42
+ should "have a connection" do
43
+ @document.connection.should be_instance_of(XGen::Mongo::Driver::Mongo)
44
+ end
45
+
46
+ should "allow setting different connection without affecting the default" do
47
+ conn = XGen::Mongo::Driver::Mongo.new
48
+ @document.connection conn
49
+ @document.connection.should == conn
50
+ @document.connection.should_not == MongoMapper.connection
51
+ end
52
+
53
+ should "allow setting a different database without affecting the default" do
54
+ @document.database AlternateDatabase
55
+ @document.database.name.should == AlternateDatabase
56
+
57
+ another_document = Class.new do
58
+ include MongoMapper::Document
59
+ end
60
+ another_document.database.should == MongoMapper.database
61
+ end
62
+
63
+ class Item; include MongoMapper::Document; end
64
+ should "default collection name to class name tableized" do
65
+ Item.collection.should be_instance_of(XGen::Mongo::Driver::Collection)
66
+ Item.collection.name.should == 'items'
67
+ end
68
+
69
+ should "allow setting the collection name" do
70
+ @document.collection('foobar')
71
+ @document.collection.should be_instance_of(XGen::Mongo::Driver::Collection)
72
+ @document.collection.name.should == 'foobar'
73
+ end
74
+ end # Document class
75
+
76
+ context "Database operations" do
77
+ setup do
78
+ @document = Class.new do
79
+ include MongoMapper::Document
80
+ collection 'users'
81
+
82
+ key :fname, String
83
+ key :lname, String
84
+ key :age, Integer
85
+ end
86
+
87
+ @document.collection.clear
88
+ end
89
+
90
+ context "Using key with type Array" do
91
+ setup do
92
+ @document.key :tags, Array
93
+ end
94
+
95
+ should "work" do
96
+ doc = @document.new
97
+ doc.tags.should == []
98
+ doc.tags = %w(foo bar)
99
+ doc.save
100
+ doc.tags.should == %w(foo bar)
101
+ @document.find(doc.id).tags.should == %w(foo bar)
102
+ end
103
+ end
104
+
105
+ context "Using key with type Hash" do
106
+ setup do
107
+ @document.key :foo, Hash
108
+ end
109
+
110
+ should "work with indifferent access" do
111
+ doc = @document.new
112
+ doc.foo = {:baz => 'bar'}
113
+ doc.save
114
+
115
+ doc = @document.find(doc.id)
116
+ doc.foo[:baz].should == 'bar'
117
+ doc.foo['baz'].should == 'bar'
118
+ end
119
+ end
120
+
121
+ context "Saving a document with an embedded document" do
122
+ setup do
123
+ @document.class_eval do
124
+ key :foo, Address
125
+ end
126
+ end
127
+
128
+ should "embed embedded document" do
129
+ address = Address.new(:city => 'South Bend', :state => 'IN')
130
+ doc = @document.new(:foo => address)
131
+ doc.save
132
+ doc.foo.city.should == 'South Bend'
133
+ doc.foo.state.should == 'IN'
134
+
135
+ from_db = @document.find(doc.id)
136
+ from_db.foo.city.should == 'South Bend'
137
+ from_db.foo.state.should == 'IN'
138
+ end
139
+ end
140
+
141
+ context "Creating a single document" do
142
+ setup do
143
+ @doc_instance = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
144
+ end
145
+
146
+ should "create a document in correct collection" do
147
+ @document.count.should == 1
148
+ end
149
+
150
+ should "automatically set id" do
151
+ @doc_instance.id.should_not be_nil
152
+ @doc_instance.id.size.should == 24
153
+ end
154
+
155
+ should "return instance of document" do
156
+ @doc_instance.should be_instance_of(@document)
157
+ @doc_instance.fname.should == 'John'
158
+ @doc_instance.lname.should == 'Nunemaker'
159
+ @doc_instance.age.should == 27
160
+ end
161
+ end
162
+
163
+ context "Creating multiple documents" do
164
+ setup do
165
+ @doc_instances = @document.create([
166
+ {:fname => 'John', :lname => 'Nunemaker', :age => '27'},
167
+ {:fname => 'Steve', :lname => 'Smith', :age => '28'},
168
+ ])
169
+ end
170
+
171
+ should "create multiple documents" do
172
+ @document.count.should == 2
173
+ end
174
+
175
+ should "return an array of doc instances" do
176
+ @doc_instances.map do |doc_instance|
177
+ doc_instance.should be_instance_of(@document)
178
+ end
179
+ end
180
+ end
181
+
182
+ context "Updating a document" do
183
+ setup do
184
+ doc = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
185
+ @doc_instance = @document.update(doc.id, {:age => 40})
186
+ end
187
+
188
+ should "update attributes provided" do
189
+ @doc_instance.age.should == 40
190
+ end
191
+
192
+ should "not update existing attributes that were not set to update" do
193
+ @doc_instance.fname.should == 'John'
194
+ @doc_instance.lname.should == 'Nunemaker'
195
+ end
196
+
197
+ should "not create new document" do
198
+ @document.count.should == 1
199
+ end
200
+ end
201
+
202
+ should "raise error when updating single doc if not provided id and attributes" do
203
+ doc = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
204
+ lambda { @document.update }.should raise_error(ArgumentError)
205
+ lambda { @document.update(doc.id) }.should raise_error(ArgumentError)
206
+ lambda { @document.update(doc.id, [1]) }.should raise_error(ArgumentError)
207
+ end
208
+
209
+ context "Updating multiple documents" do
210
+ setup do
211
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
212
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
213
+
214
+ @doc_instances = @document.update({
215
+ @doc1.id => {:age => 30},
216
+ @doc2.id => {:age => 30},
217
+ })
218
+ end
219
+
220
+ should "not create any new documents" do
221
+ @document.count.should == 2
222
+ end
223
+
224
+ should "should return an array of doc instances" do
225
+ @doc_instances.map do |doc_instance|
226
+ doc_instance.should be_instance_of(@document)
227
+ end
228
+ end
229
+
230
+ should "update the documents" do
231
+ @document.find(@doc1.id).age.should == 30
232
+ @document.find(@doc2.id).age.should == 30
233
+ end
234
+ end
235
+
236
+ should "raise error when updating multiple documents if not a hash" do
237
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
238
+ end
239
+
240
+ context "Finding documents" do
241
+ setup do
242
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
243
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
244
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
245
+ end
246
+
247
+ should "raise document not found if nothing provided" do
248
+ lambda { @document.find }.should raise_error(MongoMapper::DocumentNotFound)
249
+ end
250
+
251
+ context "with a single id" do
252
+ should "work" do
253
+ @document.find(@doc1.id).should == @doc1
254
+ end
255
+
256
+ should "raise error if document not found" do
257
+ lambda { @document.find(1) }.should raise_error(MongoMapper::DocumentNotFound)
258
+ end
259
+ end
260
+
261
+ context "with multiple id's" do
262
+ should "work as arguments" do
263
+ @document.find(@doc1.id, @doc2.id).should == [@doc1, @doc2]
264
+ end
265
+
266
+ should "work as array" do
267
+ @document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
268
+ end
269
+ end
270
+
271
+ context "with :all" do
272
+ should "find all documents" do
273
+ @document.find(:all).should == [@doc1, @doc2, @doc3]
274
+ end
275
+
276
+ should "be able to add conditions" do
277
+ @document.find(:all, :conditions => {:fname => 'John'}).should == [@doc1]
278
+ end
279
+ end
280
+
281
+ context "with #all" do
282
+ should "find all documents based on criteria" do
283
+ @document.all.should == [@doc1, @doc2, @doc3]
284
+ @document.all(:conditions => {:lname => 'Nunemaker'}).should == [@doc1, @doc3]
285
+ end
286
+ end
287
+
288
+ context "with :first" do
289
+ should "find first document" do
290
+ @document.find(:first).should == @doc1
291
+ end
292
+ end
293
+
294
+ context "with #first" do
295
+ should "find first document based on criteria" do
296
+ @document.first.should == @doc1
297
+ @document.first(:conditions => {:age => 28}).should == @doc2
298
+ end
299
+ end
300
+
301
+ context "with :last" do
302
+ should "find last document" do
303
+ @document.find(:last).should == @doc3
304
+ end
305
+ end
306
+
307
+ context "with #last" do
308
+ should "find last document based on criteria" do
309
+ @document.last.should == @doc3
310
+ @document.last(:conditions => {:age => 28}).should == @doc2
311
+ end
312
+ end
313
+ end # finding documents
314
+
315
+ context "Finding document by id" do
316
+ setup do
317
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
318
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
319
+ end
320
+
321
+ should "be able to find by id" do
322
+ @document.find_by_id(@doc1.id).should == @doc1
323
+ @document.find_by_id(@doc2.id).should == @doc2
324
+ end
325
+
326
+ should "return nil if document not found" do
327
+ @document.find_by_id(1234).should be(nil)
328
+ end
329
+ end
330
+
331
+ context "Deleting a document" do
332
+ setup do
333
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
334
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
335
+ @document.delete(@doc1.id)
336
+ end
337
+
338
+ should "remove document from collection" do
339
+ @document.count.should == 1
340
+ end
341
+
342
+ should "not remove other documents" do
343
+ @document.find(@doc2.id).should_not be(nil)
344
+ end
345
+ end
346
+
347
+ context "Deleting multiple documents" do
348
+ should "work with multiple arguments" do
349
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
350
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
351
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
352
+ @document.delete(@doc1.id, @doc2.id)
353
+
354
+ @document.count.should == 1
355
+ end
356
+
357
+ should "work with array as argument" do
358
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
359
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
360
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
361
+ @document.delete([@doc1.id, @doc2.id])
362
+
363
+ @document.count.should == 1
364
+ end
365
+ end
366
+
367
+ context "Deleting all documents" do
368
+ setup do
369
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
370
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
371
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
372
+ end
373
+
374
+ should "remove all documents when given no conditions" do
375
+ @document.delete_all
376
+ @document.count.should == 0
377
+ end
378
+
379
+ should "only remove matching documents when given conditions" do
380
+ @document.delete_all({:fname => 'John'})
381
+ @document.count.should == 2
382
+ end
383
+ end
384
+
385
+ context "Destroying a document" do
386
+ setup do
387
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
388
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
389
+ @document.destroy(@doc1.id)
390
+ end
391
+
392
+ should "remove document from collection" do
393
+ @document.count.should == 1
394
+ end
395
+
396
+ should "not remove other documents" do
397
+ @document.find(@doc2.id).should_not be(nil)
398
+ end
399
+ end
400
+
401
+ context "Destroying multiple documents" do
402
+ should "work with multiple arguments" do
403
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
404
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
405
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
406
+ @document.destroy(@doc1.id, @doc2.id)
407
+
408
+ @document.count.should == 1
409
+ end
410
+
411
+ should "work with array as argument" do
412
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
413
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
414
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
415
+ @document.destroy([@doc1.id, @doc2.id])
416
+
417
+ @document.count.should == 1
418
+ end
419
+ end
420
+
421
+ context "Destroying all documents" do
422
+ setup do
423
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
424
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
425
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
426
+ end
427
+
428
+ should "remove all documents when given no conditions" do
429
+ @document.destroy_all
430
+ @document.count.should == 0
431
+ end
432
+
433
+ should "only remove matching documents when given conditions" do
434
+ @document.destroy_all(:fname => 'John')
435
+ @document.count.should == 2
436
+ @document.destroy_all(:age => 26)
437
+ @document.count.should == 1
438
+ end
439
+ end
440
+
441
+ context "Counting documents in collection" do
442
+ setup do
443
+ @doc1 = @document.create({:fname => 'John', :lname => 'Nunemaker', :age => '27'})
444
+ @doc2 = @document.create({:fname => 'Steve', :lname => 'Smith', :age => '28'})
445
+ @doc3 = @document.create({:fname => 'Steph', :lname => 'Nunemaker', :age => '26'})
446
+ end
447
+
448
+ should "count all with no arguments" do
449
+ @document.count.should == 3
450
+ end
451
+
452
+ should "return 0 if there are no documents in the collection" do
453
+ @document.delete_all
454
+ @document.count.should == 0
455
+ end
456
+
457
+ should "return 0 if the collection does not exist" do
458
+ klass = Class.new do
459
+ include MongoMapper::Document
460
+ collection 'foobarbazwickdoesnotexist'
461
+ end
462
+
463
+ klass.count.should == 0
464
+ end
465
+
466
+ should "return count for matching documents if conditions provided" do
467
+ @document.count(:age => 27).should == 1
468
+ end
469
+ end
470
+
471
+ context "Indexing" do
472
+ setup do
473
+ @document.collection.drop_indexes
474
+ end
475
+
476
+ should "allow creating index for a key" do
477
+ lambda {
478
+ @document.ensure_index :fname
479
+ }.should change { @document.collection.index_information.size }.by(1)
480
+
481
+ index = @document.collection.index_information.last
482
+ index.should_not be_nil
483
+ index[:keys].keys.should == %w(fname)
484
+ end
485
+
486
+ should "allow creating unique index for a key" do
487
+ @document.collection.expects(:create_index).with('fname', true)
488
+ @document.ensure_index :fname, :unique => true
489
+ end
490
+
491
+ should "allow creating index on multiple keys" do
492
+ lambda {
493
+ @document.ensure_index [[:fname, 1], [:lname, -1]]
494
+ }.should change { @document.collection.index_information.size }.by(1)
495
+
496
+ index = @document.collection.index_information.last
497
+ index.should_not be_nil
498
+ index[:keys].keys.should == %w(fname lname)
499
+ end
500
+
501
+ should "work with :index shortcut when defining key" do
502
+ lambda {
503
+ @document.key :father, String, :index => true
504
+ }.should change { @document.collection.index_information.size }.by(1)
505
+
506
+ index = @document.collection.index_information.last
507
+ index.should_not be_nil
508
+ index[:keys].keys.should == %w(father)
509
+ end
510
+ end
511
+
512
+ end # Database operations
513
+
514
+ context "An instance of a document" do
515
+ setup do
516
+ @document = Class.new do
517
+ include MongoMapper::Document
518
+
519
+ key :name, String
520
+ key :age, Integer
521
+ end
522
+ @document.collection.clear
523
+ end
524
+
525
+ should "have access to the class's collection" do
526
+ doc = @document.new
527
+ doc.collection.should == @document.collection
528
+ end
529
+
530
+ should "automatically have an _id key" do
531
+ @document.keys.keys.should include('_id')
532
+ end
533
+
534
+ should "automatically have a created_at key" do
535
+ @document.keys.keys.should include('created_at')
536
+ end
537
+
538
+ should "automatically have an updated_at key" do
539
+ @document.keys.keys.should include('updated_at')
540
+ end
541
+
542
+ should "use default values if defined for keys" do
543
+ @document.key :active, Boolean, :default => true
544
+
545
+ @document.new.active.should be_true
546
+ @document.new(:active => false).active.should be_false
547
+ end
548
+
549
+ context "new?" do
550
+ should "be true if no id" do
551
+ @document.new.new?.should be(true)
552
+ end
553
+
554
+ should "be true if has id but id not in database" do
555
+ @document.new('_id' => 1).new?.should be(true)
556
+ end
557
+
558
+ should "be false if has id and id is in database" do
559
+ doc = @document.create(:name => 'John Nunemaker', :age => 27)
560
+ doc.new?.should be(false)
561
+ end
562
+ end
563
+
564
+ context "when initialized" do
565
+ should "accept a hash that sets keys and values" do
566
+ doc = @document.new(:name => 'John', :age => 23)
567
+ doc.attributes.should == {'name' => 'John', 'age' => 23}
568
+ end
569
+
570
+ should "silently reject keys that have not been defined" do
571
+ doc = @document.new(:foobar => 'baz')
572
+ doc.attributes.should == {}
573
+ end
574
+ end
575
+
576
+ context "mass assigning keys" do
577
+ should "update values for keys provided" do
578
+ doc = @document.new(:name => 'foobar', :age => 10)
579
+ doc.attributes = {:name => 'new value', :age => 5}
580
+ doc.attributes[:name].should == 'new value'
581
+ doc.attributes[:age].should == 5
582
+ end
583
+
584
+ should "not update values for keys that were not provided" do
585
+ doc = @document.new(:name => 'foobar', :age => 10)
586
+ doc.attributes = {:name => 'new value'}
587
+ doc.attributes[:name].should == 'new value'
588
+ doc.attributes[:age].should == 10
589
+ end
590
+
591
+ should "ignore keys that do not exist" do
592
+ doc = @document.new(:name => 'foobar', :age => 10)
593
+ doc.attributes = {:name => 'new value', :foobar => 'baz'}
594
+ doc.attributes[:name].should == 'new value'
595
+ doc.attributes[:foobar].should be(nil)
596
+ end
597
+
598
+ should "typecast key values" do
599
+ doc = @document.new(:name => 1234, :age => '21')
600
+ doc.name.should == '1234'
601
+ doc.age.should == 21
602
+ end
603
+ end
604
+
605
+ context "requesting keys" do
606
+ should "default to empty hash" do
607
+ doc = @document.new
608
+ doc.attributes.should == {}
609
+ end
610
+
611
+ should "return all keys that aren't nil" do
612
+ doc = @document.new(:name => 'string', :age => nil)
613
+ doc.attributes.should == {'name' => 'string'}
614
+ end
615
+ end
616
+
617
+ context "key shorcuts" do
618
+ should "be able to read key with []" do
619
+ doc = @document.new(:name => 'string')
620
+ doc[:name].should == 'string'
621
+ end
622
+
623
+ should "be able to write key value with []=" do
624
+ doc = @document.new
625
+ doc[:name] = 'string'
626
+ doc[:name].should == 'string'
627
+ end
628
+ end
629
+
630
+ context "indifferent access" do
631
+ should "be enabled for keys" do
632
+ doc = @document.new(:name => 'string')
633
+ doc.attributes[:name].should == 'string'
634
+ doc.attributes['name'].should == 'string'
635
+ end
636
+ end
637
+
638
+ context "reading an attribute" do
639
+ should "work for defined keys" do
640
+ doc = @document.new(:name => 'string')
641
+ doc.name.should == 'string'
642
+ end
643
+
644
+ should "raise no method error for undefined keys" do
645
+ doc = @document.new
646
+ lambda { doc.fart }.should raise_error(NoMethodError)
647
+ end
648
+
649
+ should "know if reader defined" do
650
+ doc = @document.new
651
+ doc.reader?('name').should be(true)
652
+ doc.reader?(:name).should be(true)
653
+ doc.reader?('age').should be(true)
654
+ doc.reader?(:age).should be(true)
655
+ doc.reader?('foobar').should be(false)
656
+ doc.reader?(:foobar).should be(false)
657
+ end
658
+
659
+ should "be accessible for use in the model" do
660
+ @document.class_eval do
661
+ def name_and_age
662
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
663
+ end
664
+ end
665
+
666
+ doc = @document.new(:name => 'John', :age => 27)
667
+ doc.name_and_age.should == 'John (27)'
668
+ end
669
+ end
670
+
671
+ context "reading an attribute before typcasting" do
672
+ should "work for defined keys" do
673
+ doc = @document.new(:name => 12)
674
+ doc.name_before_typecast.should == 12
675
+ end
676
+
677
+ should "raise no method error for undefined keys" do
678
+ doc = @document.new
679
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
680
+ end
681
+
682
+ should "be accessible for use in a document" do
683
+ @document.class_eval do
684
+ def untypcasted_name
685
+ read_attribute_before_typecast(:name)
686
+ end
687
+ end
688
+
689
+ doc = @document.new(:name => 12)
690
+ doc.name.should == '12'
691
+ doc.untypcasted_name.should == 12
692
+ end
693
+ end
694
+
695
+ context "writing an attribute" do
696
+ should "work for defined keys" do
697
+ doc = @document.new
698
+ doc.name = 'John'
699
+ doc.name.should == 'John'
700
+ end
701
+
702
+ should "raise no method error for undefined keys" do
703
+ doc = @document.new
704
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
705
+ end
706
+
707
+ should "typecast value" do
708
+ doc = @document.new
709
+ doc.name = 1234
710
+ doc.name.should == '1234'
711
+ doc.age = '21'
712
+ doc.age.should == 21
713
+ end
714
+
715
+ should "know if writer defined" do
716
+ doc = @document.new
717
+ doc.writer?('name').should be(true)
718
+ doc.writer?('name=').should be(true)
719
+ doc.writer?(:name).should be(true)
720
+ doc.writer?('age').should be(true)
721
+ doc.writer?('age=').should be(true)
722
+ doc.writer?(:age).should be(true)
723
+ doc.writer?('foobar').should be(false)
724
+ doc.writer?('foobar=').should be(false)
725
+ doc.writer?(:foobar).should be(false)
726
+ end
727
+
728
+ should "be accessible for use in the model" do
729
+ @document.class_eval do
730
+ def name_and_age=(new_value)
731
+ new_value.match(/([^\(\s]+) \((.*)\)/)
732
+ write_attribute :name, $1
733
+ write_attribute :age, $2
734
+ end
735
+ end
736
+
737
+ doc = @document.new
738
+ doc.name_and_age = 'Frank (62)'
739
+ doc.name.should == 'Frank'
740
+ doc.age.should == 62
741
+ end
742
+ end # writing an attribute
743
+
744
+ context "respond_to?" do
745
+ setup do
746
+ @doc = @document.new
747
+ end
748
+
749
+ should "work for readers" do
750
+ @doc.respond_to?(:name).should be_true
751
+ @doc.respond_to?('name').should be_true
752
+ end
753
+
754
+ should "work for writers" do
755
+ @doc.respond_to?(:name=).should be_true
756
+ @doc.respond_to?('name=').should be_true
757
+ end
758
+
759
+ should "work for readers before typecast" do
760
+ @doc.respond_to?(:name_before_typecast).should be_true
761
+ @doc.respond_to?('name_before_typecast').should be_true
762
+ end
763
+ end
764
+
765
+ context "equality" do
766
+ should "be equal if id and class are the same" do
767
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
768
+ end
769
+
770
+ should "not be equal if class same but id different" do
771
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
772
+ end
773
+
774
+ should "not be equal if id same but class different" do
775
+ @another_document = Class.new do
776
+ include MongoMapper::Document
777
+ end
778
+
779
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
780
+ end
781
+ end
782
+
783
+ context "Saving a new document" do
784
+ setup do
785
+ @doc = @document.new(:name => 'John Nunemaker', :age => '27')
786
+ @doc.save
787
+ end
788
+
789
+ should "insert document into the collection" do
790
+ @document.count.should == 1
791
+ end
792
+
793
+ should "assign an id for the document" do
794
+ @doc.id.should_not be(nil)
795
+ @doc.id.size.should == 24
796
+ end
797
+
798
+ should "save attributes" do
799
+ @doc.name.should == 'John Nunemaker'
800
+ @doc.age.should == 27
801
+ end
802
+
803
+ should "update attributes in the database" do
804
+ from_db = @document.find(@doc.id)
805
+ from_db.should == @doc
806
+ from_db.name.should == 'John Nunemaker'
807
+ from_db.age.should == 27
808
+ end
809
+ end
810
+
811
+ context "Saving an existing document" do
812
+ setup do
813
+ @doc = @document.create(:name => 'John Nunemaker', :age => '27')
814
+ @doc.name = 'John Doe'
815
+ @doc.age = 30
816
+ @doc.save
817
+ end
818
+
819
+ should "not insert document into collection" do
820
+ @document.count.should == 1
821
+ end
822
+
823
+ should "update attributes" do
824
+ @doc.name.should == 'John Doe'
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.name.should == 'John Doe'
831
+ from_db.age.should == 30
832
+ end
833
+ end
834
+
835
+ context "Calling update attributes on a new document" do
836
+ setup do
837
+ @doc = @document.new(:name => 'John Nunemaker', :age => '27')
838
+ @doc.update_attributes(:name => 'John Doe', :age => 30)
839
+ end
840
+
841
+ should "insert document into the collection" do
842
+ @document.count.should == 1
843
+ end
844
+
845
+ should "assign an id for the document" do
846
+ @doc.id.should_not be(nil)
847
+ @doc.id.size.should == 24
848
+ end
849
+
850
+ should "save attributes" do
851
+ @doc.name.should == 'John Doe'
852
+ @doc.age.should == 30
853
+ end
854
+
855
+ should "update attributes in the database" do
856
+ from_db = @document.find(@doc.id)
857
+ from_db.should == @doc
858
+ from_db.name.should == 'John Doe'
859
+ from_db.age.should == 30
860
+ end
861
+ end
862
+
863
+ context "Updating an existing document using update attributes" do
864
+ setup do
865
+ @doc = @document.create(:name => 'John Nunemaker', :age => '27')
866
+ @doc.update_attributes(:name => 'John Doe', :age => 30)
867
+ end
868
+
869
+ should "not insert document into collection" do
870
+ @document.count.should == 1
871
+ end
872
+
873
+ should "update attributes" do
874
+ @doc.name.should == 'John Doe'
875
+ @doc.age.should == 30
876
+ end
877
+
878
+ should "update attributes in the database" do
879
+ from_db = @document.find(@doc.id)
880
+ from_db.name.should == 'John Doe'
881
+ from_db.age.should == 30
882
+ end
883
+ end
884
+
885
+ context "Destroying a document that exists" do
886
+ setup do
887
+ @doc = @document.create(:name => 'John Nunemaker', :age => '27')
888
+ @doc.destroy
889
+ end
890
+
891
+ should "remove the document from the collection" do
892
+ @document.count.should == 0
893
+ end
894
+
895
+ should "raise error if assignment is attempted" do
896
+ lambda { @doc.name = 'Foo' }.should raise_error(TypeError)
897
+ end
898
+ end
899
+
900
+ context "Destroying a document that is a new" do
901
+ setup do
902
+ setup do
903
+ @doc = @document.new(:name => 'John Nunemaker', :age => '27')
904
+ @doc.destroy
905
+ end
906
+
907
+ should "not affect collection count" do
908
+ @document.collection.count.should == 0
909
+ end
910
+
911
+ should "raise error if assignment is attempted" do
912
+ lambda { @doc.name = 'Foo' }.should raise_error(TypeError)
913
+ end
914
+ end
915
+ end
916
+
917
+ context "timestamping" do
918
+ should "set created_at and updated_at on create" do
919
+ doc = @document.new(:name => 'John Nunemaker', :age => 27)
920
+ doc.created_at.should be(nil)
921
+ doc.updated_at.should be(nil)
922
+ doc.save
923
+ doc.created_at.should_not be(nil)
924
+ doc.updated_at.should_not be(nil)
925
+ end
926
+
927
+ should "set updated_at on update but leave created_at alone" do
928
+ doc = @document.create(:name => 'John Nunemaker', :age => 27)
929
+ old_created_at = doc.created_at
930
+ old_updated_at = doc.updated_at
931
+ doc.name = 'John Doe'
932
+ doc.save
933
+ doc.created_at.should == old_created_at
934
+ doc.updated_at.should_not == old_updated_at
935
+ end
936
+ end
937
+ end # instance of a document
938
+ end # DocumentTest