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