webbynode-mongo_mapper 0.6.4

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