mongo_mapper-unstable 2009.10.11

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 (73) hide show
  1. data/.gitignore +8 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +50 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper/associations/base.rb +83 -0
  8. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  9. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  10. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
  11. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  12. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  13. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
  14. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  15. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  16. data/lib/mongo_mapper/associations/proxy.rb +74 -0
  17. data/lib/mongo_mapper/associations.rb +86 -0
  18. data/lib/mongo_mapper/callbacks.rb +106 -0
  19. data/lib/mongo_mapper/dirty.rb +137 -0
  20. data/lib/mongo_mapper/document.rb +340 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +355 -0
  23. data/lib/mongo_mapper/finder_options.rb +98 -0
  24. data/lib/mongo_mapper/key.rb +36 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -0
  27. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  29. data/lib/mongo_mapper/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +161 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/lib/mongo_mapper.rb +111 -0
  35. data/mongo_mapper.gemspec +162 -0
  36. data/specs.watchr +32 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/custom_matchers.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  40. data/test/functional/associations/test_belongs_to_proxy.rb +49 -0
  41. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  45. data/test/functional/associations/test_many_proxy.rb +331 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +18 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +138 -0
  50. data/test/functional/test_document.rb +1051 -0
  51. data/test/functional/test_embedded_document.rb +97 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_pagination.rb +87 -0
  54. data/test/functional/test_rails_compatibility.rb +30 -0
  55. data/test/functional/test_validations.rb +279 -0
  56. data/test/models.rb +195 -0
  57. data/test/test_helper.rb +30 -0
  58. data/test/unit/serializers/test_json_serializer.rb +189 -0
  59. data/test/unit/test_association_base.rb +144 -0
  60. data/test/unit/test_document.rb +184 -0
  61. data/test/unit/test_dynamic_finder.rb +125 -0
  62. data/test/unit/test_embedded_document.rb +656 -0
  63. data/test/unit/test_finder_options.rb +261 -0
  64. data/test/unit/test_key.rb +172 -0
  65. data/test/unit/test_mongomapper.rb +28 -0
  66. data/test/unit/test_observing.rb +101 -0
  67. data/test/unit/test_pagination.rb +109 -0
  68. data/test/unit/test_rails_compatibility.rb +39 -0
  69. data/test/unit/test_serializations.rb +52 -0
  70. data/test/unit/test_support.rb +291 -0
  71. data/test/unit/test_time_zones.rb +40 -0
  72. data/test/unit/test_validations.rb +503 -0
  73. metadata +210 -0
@@ -0,0 +1,656 @@
1
+ require 'test_helper'
2
+
3
+ class Grandparent
4
+ include MongoMapper::EmbeddedDocument
5
+ key :grandparent, String
6
+ end
7
+
8
+ class Parent < Grandparent
9
+ include MongoMapper::EmbeddedDocument
10
+ key :parent, String
11
+ end
12
+
13
+ class Child < Parent
14
+ include MongoMapper::EmbeddedDocument
15
+ key :child, String
16
+ end
17
+
18
+ module KeyOverride
19
+ def other_child
20
+ read_attribute(:other_child) || "special result"
21
+ end
22
+
23
+ def other_child=(value)
24
+ super(value + " modified")
25
+ end
26
+ end
27
+
28
+ class OtherChild < Parent
29
+ include MongoMapper::EmbeddedDocument
30
+ include KeyOverride
31
+
32
+ key :other_child, String
33
+ end
34
+
35
+ class EmbeddedDocumentTest < Test::Unit::TestCase
36
+ context "Including MongoMapper::EmbeddedDocument in a class" do
37
+ setup do
38
+ @klass = Class.new do
39
+ include MongoMapper::EmbeddedDocument
40
+ end
41
+ end
42
+
43
+ should "give class access to logger" do
44
+ @klass.logger.should == MongoMapper.logger
45
+ @klass.logger.should be_instance_of(Logger)
46
+ end
47
+
48
+ should "add _id key" do
49
+ @klass.keys['_id'].should_not be_nil
50
+ end
51
+
52
+ context "#to_mongo" do
53
+ should "be nil if nil" do
54
+ @klass.to_mongo(nil).should be_nil
55
+ end
56
+
57
+ should "convert to_mongo for other values" do
58
+ doc = @klass.new(:foo => 'bar')
59
+ to_mongo = @klass.to_mongo(doc)
60
+ to_mongo.is_a?(Hash).should be_true
61
+ to_mongo['foo'].should == 'bar'
62
+ end
63
+ end
64
+
65
+ context "#from_mongo" do
66
+ should "be nil if nil" do
67
+ @klass.from_mongo(nil).should be_nil
68
+ end
69
+
70
+ should "be instance if instance of class" do
71
+ doc = @klass.new
72
+ @klass.from_mongo(doc).should == doc
73
+ end
74
+
75
+ should "be instance if hash of attributes" do
76
+ doc = @klass.from_mongo({:foo => 'bar'})
77
+ doc.instance_of?(@klass).should be_true
78
+ doc.foo.should == 'bar'
79
+ end
80
+ end
81
+ end
82
+
83
+ context "parent_model" do
84
+ should "be nil if none of parents ancestors include EmbeddedDocument" do
85
+ parent = Class.new
86
+ document = Class.new(parent) do
87
+ include MongoMapper::EmbeddedDocument
88
+ end
89
+ document.parent_model.should be_nil
90
+ end
91
+
92
+ should "work when other modules have been included" do
93
+ grandparent = Class.new
94
+ parent = Class.new grandparent do
95
+ include MongoMapper::EmbeddedDocument
96
+ end
97
+
98
+ example_module = Module.new
99
+ document = Class.new(parent) do
100
+ include MongoMapper::EmbeddedDocument
101
+ include example_module
102
+ end
103
+
104
+ document.parent_model.should == parent
105
+ end
106
+
107
+ should "find parent" do
108
+ Parent.parent_model.should == Grandparent
109
+ Child.parent_model.should == Parent
110
+ end
111
+ end
112
+
113
+ context "defining a key" do
114
+ setup do
115
+ @document = Class.new do
116
+ include MongoMapper::EmbeddedDocument
117
+ end
118
+ end
119
+
120
+ should "work with name" do
121
+ key = @document.key(:name)
122
+ key.name.should == 'name'
123
+ end
124
+
125
+ should "work with name and type" do
126
+ key = @document.key(:name, String)
127
+ key.name.should == 'name'
128
+ key.type.should == String
129
+ end
130
+
131
+ should "work with name, type and options" do
132
+ key = @document.key(:name, String, :required => true)
133
+ key.name.should == 'name'
134
+ key.type.should == String
135
+ key.options[:required].should be_true
136
+ end
137
+
138
+ should "work with name and options" do
139
+ key = @document.key(:name, :required => true)
140
+ key.name.should == 'name'
141
+ key.options[:required].should be_true
142
+ end
143
+
144
+ should "be tracked per document" do
145
+ @document.key(:name, String)
146
+ @document.key(:age, Integer)
147
+ @document.keys['name'].name.should == 'name'
148
+ @document.keys['name'].type.should == String
149
+ @document.keys['age'].name.should == 'age'
150
+ @document.keys['age'].type.should == Integer
151
+ end
152
+
153
+ should "not be redefinable" do
154
+ @document.key(:foo, String)
155
+ @document.keys['foo'].type.should == String
156
+ @document.key(:foo, Integer)
157
+ @document.keys['foo'].type.should == String
158
+ end
159
+
160
+ should "create reader method" do
161
+ @document.new.should_not respond_to(:foo)
162
+ @document.key(:foo, String)
163
+ @document.new.should respond_to(:foo)
164
+ end
165
+
166
+ should "create reader before typecast method" do
167
+ @document.new.should_not respond_to(:foo_before_typecast)
168
+ @document.key(:foo, String)
169
+ @document.new.should respond_to(:foo_before_typecast)
170
+ end
171
+
172
+ should "create writer method" do
173
+ @document.new.should_not respond_to(:foo=)
174
+ @document.key(:foo, String)
175
+ @document.new.should respond_to(:foo=)
176
+ end
177
+
178
+ should "create boolean method" do
179
+ @document.new.should_not respond_to(:foo?)
180
+ @document.key(:foo, String)
181
+ @document.new.should respond_to(:foo?)
182
+ end
183
+ end
184
+
185
+ context "keys" do
186
+ should "be inherited" do
187
+ Grandparent.keys.keys.sort.should == ['_id', 'grandparent']
188
+ Parent.keys.keys.sort.should == ['_id', 'grandparent', 'parent']
189
+ Child.keys.keys.sort.should == ['_id', 'child', 'grandparent', 'parent']
190
+ end
191
+
192
+ should "propogate to subclasses if key added after class definition" do
193
+ Grandparent.key :_type, String
194
+
195
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
196
+ Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
197
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
198
+ end
199
+
200
+ should "not add anonymous objects to the ancestor tree" do
201
+ OtherChild.ancestors.any? { |a| a.name.blank? }.should be_false
202
+ end
203
+
204
+ should "not include descendant keys" do
205
+ lambda { Parent.new.other_child }.should raise_error
206
+ end
207
+ end
208
+
209
+ context "#inspect" do
210
+ setup do
211
+ @document = Class.new do
212
+ include MongoMapper::Document
213
+ key :animals
214
+ end
215
+ end
216
+
217
+ should "call inspect on the document's attributes instead of to_s" do
218
+ doc = @document.new
219
+ doc.animals = %w(dog cat)
220
+ doc.inspect.should include(%(animals: ["dog", "cat"]))
221
+ end
222
+ end
223
+
224
+ context "subclasses" do
225
+ should "default to nil" do
226
+ Child.subclasses.should be_nil
227
+ end
228
+
229
+ should "be recorded" do
230
+ Grandparent.subclasses.should == [Parent]
231
+ Parent.subclasses.should == [Child, OtherChild]
232
+ end
233
+ end
234
+
235
+ context "Applying default values for keys" do
236
+ setup do
237
+ @document = Class.new do
238
+ include MongoMapper::EmbeddedDocument
239
+
240
+ key :name, String, :default => 'foo'
241
+ key :age, Integer, :default => 20
242
+ key :net_worth, Float, :default => 100.00
243
+ key :active, Boolean, :default => true
244
+ key :smart, Boolean, :default => false
245
+ key :skills, Array, :default => [1]
246
+ key :options, Hash, :default => {'foo' => 'bar'}
247
+ end
248
+
249
+ @doc = @document.new
250
+ end
251
+
252
+ should "work for strings" do
253
+ @doc.name.should == 'foo'
254
+ end
255
+
256
+ should "work for integers" do
257
+ @doc.age.should == 20
258
+ end
259
+
260
+ should "work for floats" do
261
+ @doc.net_worth.should == 100.00
262
+ end
263
+
264
+ should "work for booleans" do
265
+ @doc.active.should == true
266
+ @doc.smart.should == false
267
+ end
268
+
269
+ should "work for arrays" do
270
+ @doc.skills.should == [1]
271
+ @doc.skills << 2
272
+ @doc.skills.should == [1, 2]
273
+ end
274
+
275
+ should "work for hashes" do
276
+ @doc.options['foo'].should == 'bar'
277
+ @doc.options['baz'] = 'wick'
278
+ @doc.options['baz'].should == 'wick'
279
+ end
280
+ end
281
+
282
+ context "An instance of an embedded document" do
283
+ setup do
284
+ @document = Class.new do
285
+ include MongoMapper::EmbeddedDocument
286
+
287
+ key :name, String
288
+ key :age, Integer
289
+ end
290
+ end
291
+
292
+ should "have access to class logger" do
293
+ doc = @document.new
294
+ doc.logger.should == @document.logger
295
+ doc.logger.should be_instance_of(Logger)
296
+ end
297
+
298
+ should "automatically have an _id key" do
299
+ @document.keys.keys.should include('_id')
300
+ end
301
+
302
+ should "have id method that sets _id" do
303
+ doc = @document.new
304
+ doc.id.should == doc._id.to_s
305
+ end
306
+
307
+ should "have a nil _root_document" do
308
+ @document.new._root_document.should be_nil
309
+ end
310
+
311
+ context "setting custom id" do
312
+ should "set _id" do
313
+ doc = @document.new(:id => '1234')
314
+ doc._id.should == '1234'
315
+ end
316
+
317
+ should "know that custom id is set" do
318
+ doc = @document.new
319
+ doc.using_custom_id?.should be_false
320
+ doc.id = '1234'
321
+ doc.using_custom_id?.should be_true
322
+ end
323
+ end
324
+
325
+ context "being initialized" do
326
+ should "accept a hash that sets keys and values" do
327
+ doc = @document.new(:name => 'John', :age => 23)
328
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
329
+ doc.attributes['name'].should == 'John'
330
+ doc.attributes['age'].should == 23
331
+ end
332
+
333
+ should "be able to assign keys dynamically" do
334
+ doc = @document.new(:name => 'John', :skills => ['ruby', 'rails'])
335
+ doc.name.should == 'John'
336
+ doc.skills.should == ['ruby', 'rails']
337
+ end
338
+
339
+ should "set the root on embedded documents" do
340
+ document = Class.new(@document) do
341
+ many :children
342
+ end
343
+
344
+ doc = document.new :_root_document => 'document', 'children' => [{}]
345
+ doc.children.first._root_document.should == 'document'
346
+ end
347
+
348
+ should "not throw error if initialized with nil" do
349
+ lambda {
350
+ @document.new(nil)
351
+ }.should_not raise_error
352
+ end
353
+ end
354
+
355
+ context "initialized when _type key present" do
356
+ setup do
357
+ ::FooBar = Class.new do
358
+ include MongoMapper::EmbeddedDocument
359
+ key :_type, String
360
+ end
361
+ end
362
+
363
+ teardown do
364
+ Object.send(:remove_const, :FooBar)
365
+ end
366
+
367
+ should "set _type to class name" do
368
+ FooBar.new._type.should == 'FooBar'
369
+ end
370
+
371
+ should "not change _type if already set" do
372
+ FooBar.new(:_type => 'Foo')._type.should == 'Foo'
373
+ end
374
+ end
375
+
376
+ context "mass assigning keys" do
377
+ should "update values for keys provided" do
378
+ doc = @document.new(:name => 'foobar', :age => 10)
379
+ doc.attributes = {:name => 'new value', :age => 5}
380
+ doc.attributes[:name].should == 'new value'
381
+ doc.attributes[:age].should == 5
382
+ end
383
+
384
+ should "not update values for keys that were not provided" do
385
+ doc = @document.new(:name => 'foobar', :age => 10)
386
+ doc.attributes = {:name => 'new value'}
387
+ doc.attributes[:name].should == 'new value'
388
+ doc.attributes[:age].should == 10
389
+ end
390
+
391
+ should "not ignore keys that have methods defined" do
392
+ @document.class_eval do
393
+ attr_writer :password
394
+
395
+ def passwd
396
+ @password
397
+ end
398
+ end
399
+
400
+ doc = @document.new(:name => 'foobar', :password => 'secret')
401
+ doc.passwd.should == 'secret'
402
+ end
403
+
404
+ should "typecast key values" do
405
+ doc = @document.new(:name => 1234, :age => '21')
406
+ doc.name.should == '1234'
407
+ doc.age.should == 21
408
+ end
409
+ end
410
+
411
+ context "attributes" do
412
+ should "default to hash with all keys" do
413
+ doc = @document.new
414
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
415
+ end
416
+
417
+ should "return all keys with values" do
418
+ doc = @document.new(:name => 'string', :age => nil)
419
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
420
+ doc.attributes.values.should include('string')
421
+ doc.attributes.values.should include(nil)
422
+ end
423
+ end
424
+
425
+ context "to_mongo" do
426
+ should "default to hash with _id key" do
427
+ doc = @document.new
428
+ doc.to_mongo.keys.should == ['_id']
429
+ end
430
+
431
+ should "return all keys with non nil values" do
432
+ doc = @document.new(:name => 'string', :age => nil)
433
+ doc.to_mongo.keys.sort.should == ['_id', 'name']
434
+ doc.to_mongo.values.should include('string')
435
+ doc.to_mongo.values.should_not include(nil)
436
+ end
437
+ end
438
+
439
+ should "convert dates into times" do
440
+ document = Class.new(@document) do
441
+ key :start_date, Date
442
+ end
443
+ doc = document.new :start_date => "12/05/2009"
444
+ doc.start_date.should == Date.new(2009, 12, 05)
445
+ end
446
+
447
+ context "clone" do
448
+ should "regenerate the id" do
449
+ doc = @document.new(:name => "foo", :age => 27)
450
+ doc_id = doc.id
451
+ clone = doc.clone
452
+ clone_id = clone.id
453
+ clone_id.should_not == doc_id
454
+ end
455
+
456
+ should "copy the attributes" do
457
+ doc = @document.new(:name => "foo", :age => 27)
458
+ clone = doc.clone
459
+ clone.name.should == "foo"
460
+ clone.age.should == 27
461
+ end
462
+ end
463
+
464
+ context "key shorcut access" do
465
+ should "be able to read key with []" do
466
+ doc = @document.new(:name => 'string')
467
+ doc[:name].should == 'string'
468
+ end
469
+
470
+ context "[]=" do
471
+ should "write key value for existing key" do
472
+ doc = @document.new
473
+ doc[:name] = 'string'
474
+ doc[:name].should == 'string'
475
+ end
476
+
477
+ should "create key and write value for missing key" do
478
+ doc = @document.new
479
+ doc[:foo] = 'string'
480
+ @document.keys.keys.include?('foo').should be_true
481
+ doc[:foo].should == 'string'
482
+ end
483
+ end
484
+ end
485
+
486
+ context "indifferent access" do
487
+ should "be enabled for keys" do
488
+ doc = @document.new(:name => 'string')
489
+ doc.attributes[:name].should == 'string'
490
+ doc.attributes['name'].should == 'string'
491
+ end
492
+ end
493
+
494
+ context "reading an attribute" do
495
+ should "work for defined keys" do
496
+ doc = @document.new(:name => 'string')
497
+ doc.name.should == 'string'
498
+ end
499
+
500
+ should "raise no method error for undefined keys" do
501
+ doc = @document.new
502
+ lambda { doc.fart }.should raise_error(NoMethodError)
503
+ end
504
+
505
+ should "be accessible for use in the model" do
506
+ @document.class_eval do
507
+ def name_and_age
508
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
509
+ end
510
+ end
511
+
512
+ doc = @document.new(:name => 'John', :age => 27)
513
+ doc.name_and_age.should == 'John (27)'
514
+ end
515
+
516
+ should "set instance variable" do
517
+ @document.key :foo, Array
518
+ doc = @document.new
519
+ doc.instance_variable_get("@foo").should be_nil
520
+ doc.foo
521
+ doc.instance_variable_get("@foo").should == []
522
+ end
523
+
524
+ should "not set instance variable if frozen" do
525
+ @document.key :foo, Array
526
+ doc = @document.new
527
+ doc.instance_variable_get("@foo").should be_nil
528
+ doc.freeze
529
+ doc.foo
530
+ doc.instance_variable_get("@foo").should be_nil
531
+ end
532
+
533
+ should "be overrideable by modules" do
534
+ @document = Class.new do
535
+ include MongoMapper::Document
536
+ key :other_child, String
537
+ end
538
+
539
+ child = @document.new
540
+ child.other_child.should be_nil
541
+
542
+ @document.send :include, KeyOverride
543
+
544
+ overriden_child = @document.new
545
+ overriden_child.other_child.should == 'special result'
546
+ end
547
+ end
548
+
549
+ context "reading an attribute before typcasting" do
550
+ should "work for defined keys" do
551
+ doc = @document.new(:name => 12)
552
+ doc.name_before_typecast.should == 12
553
+ end
554
+
555
+ should "raise no method error for undefined keys" do
556
+ doc = @document.new
557
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
558
+ end
559
+
560
+ should "be accessible for use in a document" do
561
+ @document.class_eval do
562
+ def untypcasted_name
563
+ read_attribute_before_typecast(:name)
564
+ end
565
+ end
566
+
567
+ doc = @document.new(:name => 12)
568
+ doc.name.should == '12'
569
+ doc.untypcasted_name.should == 12
570
+ end
571
+ end
572
+
573
+ context "writing an attribute" do
574
+ should "work for defined keys" do
575
+ doc = @document.new
576
+ doc.name = 'John'
577
+ doc.name.should == 'John'
578
+ end
579
+
580
+ should "raise no method error for undefined keys" do
581
+ doc = @document.new
582
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
583
+ end
584
+
585
+ should "typecast value" do
586
+ doc = @document.new
587
+ doc.name = 1234
588
+ doc.name.should == '1234'
589
+ doc.age = '21'
590
+ doc.age.should == 21
591
+ end
592
+
593
+ should "be accessible for use in the model" do
594
+ @document.class_eval do
595
+ def name_and_age=(new_value)
596
+ new_value.match(/([^\(\s]+) \((.*)\)/)
597
+ write_attribute :name, $1
598
+ write_attribute :age, $2
599
+ end
600
+ end
601
+
602
+ doc = @document.new
603
+ doc.name_and_age = 'Frank (62)'
604
+ doc.name.should == 'Frank'
605
+ doc.age.should == 62
606
+ end
607
+
608
+ should "be overrideable by modules" do
609
+ @document = Class.new do
610
+ include MongoMapper::Document
611
+ key :other_child, String
612
+ end
613
+
614
+ child = @document.new(:other_child => 'foo')
615
+ child.other_child.should == 'foo'
616
+
617
+ @document.send :include, KeyOverride
618
+
619
+ overriden_child = @document.new(:other_child => 'foo')
620
+ overriden_child.other_child.should == 'foo modified'
621
+ end
622
+ end # writing an attribute
623
+
624
+ context "checking if an attributes value is present" do
625
+ should "work for defined keys" do
626
+ doc = @document.new
627
+ doc.name?.should be_false
628
+ doc.name = 'John'
629
+ doc.name?.should be_true
630
+ end
631
+
632
+ should "raise no method error for undefined keys" do
633
+ doc = @document.new
634
+ lambda { doc.fart? }.should raise_error(NoMethodError)
635
+ end
636
+ end
637
+
638
+ context "equality" do
639
+ should "be equal if id and class are the same" do
640
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
641
+ end
642
+
643
+ should "not be equal if class same but id different" do
644
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
645
+ end
646
+
647
+ should "not be equal if id same but class different" do
648
+ @another_document = Class.new do
649
+ include MongoMapper::Document
650
+ end
651
+
652
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
653
+ end
654
+ end
655
+ end # instance of a embedded document
656
+ end