mongo_mapper-unstable 2009.10.11

Sign up to get free protection for your applications and to get access to all the features.
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