mrkurt-mongo_mapper 0.6.8

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 (77) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +139 -0
  8. data/lib/mongo_mapper/associations.rb +72 -0
  9. data/lib/mongo_mapper/associations/base.rb +113 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
  12. data/lib/mongo_mapper/associations/collection.rb +19 -0
  13. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
  14. data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
  15. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
  16. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
  17. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  18. data/lib/mongo_mapper/associations/one_proxy.rb +61 -0
  19. data/lib/mongo_mapper/associations/proxy.rb +111 -0
  20. data/lib/mongo_mapper/callbacks.rb +61 -0
  21. data/lib/mongo_mapper/dirty.rb +117 -0
  22. data/lib/mongo_mapper/document.rb +496 -0
  23. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  24. data/lib/mongo_mapper/embedded_document.rb +380 -0
  25. data/lib/mongo_mapper/finder_options.rb +145 -0
  26. data/lib/mongo_mapper/key.rb +36 -0
  27. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  28. data/lib/mongo_mapper/pagination.rb +66 -0
  29. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  30. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +28 -0
  31. data/lib/mongo_mapper/serialization.rb +54 -0
  32. data/lib/mongo_mapper/serializers/json_serializer.rb +48 -0
  33. data/lib/mongo_mapper/support.rb +192 -0
  34. data/lib/mongo_mapper/validations.rb +39 -0
  35. data/mongo_mapper.gemspec +173 -0
  36. data/specs.watchr +30 -0
  37. data/test/NOTE_ON_TESTING +1 -0
  38. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  39. data/test/functional/associations/test_belongs_to_proxy.rb +91 -0
  40. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  41. data/test/functional/associations/test_many_documents_proxy.rb +477 -0
  42. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  43. data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
  44. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  45. data/test/functional/associations/test_one_proxy.rb +131 -0
  46. data/test/functional/test_associations.rb +44 -0
  47. data/test/functional/test_binary.rb +33 -0
  48. data/test/functional/test_callbacks.rb +85 -0
  49. data/test/functional/test_dirty.rb +159 -0
  50. data/test/functional/test_document.rb +1198 -0
  51. data/test/functional/test_embedded_document.rb +135 -0
  52. data/test/functional/test_logger.rb +20 -0
  53. data/test/functional/test_modifiers.rb +242 -0
  54. data/test/functional/test_pagination.rb +95 -0
  55. data/test/functional/test_rails_compatibility.rb +25 -0
  56. data/test/functional/test_string_id_compatibility.rb +72 -0
  57. data/test/functional/test_validations.rb +361 -0
  58. data/test/models.rb +271 -0
  59. data/test/support/custom_matchers.rb +55 -0
  60. data/test/support/timing.rb +16 -0
  61. data/test/test_helper.rb +27 -0
  62. data/test/unit/associations/test_base.rb +182 -0
  63. data/test/unit/associations/test_proxy.rb +91 -0
  64. data/test/unit/serializers/test_json_serializer.rb +189 -0
  65. data/test/unit/test_document.rb +236 -0
  66. data/test/unit/test_dynamic_finder.rb +125 -0
  67. data/test/unit/test_embedded_document.rb +709 -0
  68. data/test/unit/test_finder_options.rb +325 -0
  69. data/test/unit/test_key.rb +172 -0
  70. data/test/unit/test_mongo_mapper.rb +65 -0
  71. data/test/unit/test_pagination.rb +119 -0
  72. data/test/unit/test_rails_compatibility.rb +52 -0
  73. data/test/unit/test_serializations.rb +52 -0
  74. data/test/unit/test_support.rb +346 -0
  75. data/test/unit/test_time_zones.rb +40 -0
  76. data/test/unit/test_validations.rb +503 -0
  77. metadata +239 -0
@@ -0,0 +1,125 @@
1
+ require 'test_helper'
2
+
3
+ class DynamicFinderTest < Test::Unit::TestCase
4
+ include MongoMapper
5
+
6
+ should "initialize with method" do
7
+ finder = DynamicFinder.new(:foobar)
8
+ finder.method.should == :foobar
9
+ end
10
+
11
+ context "found?" do
12
+ should "be true for find_by" do
13
+ DynamicFinder.new(:find_by_foo).found?.should be_true
14
+ end
15
+
16
+ should "be true for find_by with !" do
17
+ DynamicFinder.new(:find_by_foo!).found?.should be_true
18
+ end
19
+
20
+ should "be true for find_all_by" do
21
+ DynamicFinder.new(:find_all_by_foo).found?.should be_true
22
+ end
23
+
24
+ should "be true for find_or_initialize_by" do
25
+ DynamicFinder.new(:find_or_initialize_by_foo).found?.should be_true
26
+ end
27
+
28
+ should "be true for find_or_create_by" do
29
+ DynamicFinder.new(:find_or_create_by_foo).found?.should be_true
30
+ end
31
+
32
+ should "be false for anything else" do
33
+ [:foobar, :bazwick].each do |method|
34
+ DynamicFinder.new(method).found?.should be_false
35
+ end
36
+ end
37
+ end
38
+
39
+ context "find_all_by" do
40
+ should "parse one attribute" do
41
+ DynamicFinder.new(:find_all_by_foo).attributes.should == %w(foo)
42
+ end
43
+
44
+ should "parse multiple attributes" do
45
+ DynamicFinder.new(:find_all_by_foo_and_bar).attributes.should == %w(foo bar)
46
+ DynamicFinder.new(:find_all_by_foo_and_bar_and_baz).attributes.should == %w(foo bar baz)
47
+ end
48
+
49
+ should "set finder to :all" do
50
+ DynamicFinder.new(:find_all_by_foo_and_bar).finder.should == :all
51
+ end
52
+ end
53
+
54
+ context "find_by" do
55
+ should "parse one attribute" do
56
+ DynamicFinder.new(:find_by_foo).attributes.should == %w(foo)
57
+ end
58
+
59
+ should "parse multiple attributes" do
60
+ DynamicFinder.new(:find_by_foo_and_bar).attributes.should == %w(foo bar)
61
+ end
62
+
63
+ should "set finder to :first" do
64
+ DynamicFinder.new(:find_by_foo).finder.should == :first
65
+ end
66
+
67
+ should "set bang to false" do
68
+ DynamicFinder.new(:find_by_foo).bang.should be_false
69
+ end
70
+ end
71
+
72
+ context "find_by with !" do
73
+ should "parse one attribute" do
74
+ DynamicFinder.new(:find_by_foo!).attributes.should == %w(foo)
75
+ end
76
+
77
+ should "parse multiple attributes" do
78
+ DynamicFinder.new(:find_by_foo_and_bar!).attributes.should == %w(foo bar)
79
+ end
80
+
81
+ should "set finder to :first" do
82
+ DynamicFinder.new(:find_by_foo!).finder.should == :first
83
+ end
84
+
85
+ should "set bang to true" do
86
+ DynamicFinder.new(:find_by_foo!).bang.should be_true
87
+ end
88
+ end
89
+
90
+ context "find_or_initialize_by" do
91
+ should "parse one attribute" do
92
+ DynamicFinder.new(:find_or_initialize_by_foo).attributes.should == %w(foo)
93
+ end
94
+
95
+ should "parse multiple attributes" do
96
+ DynamicFinder.new(:find_or_initialize_by_foo_and_bar).attributes.should == %w(foo bar)
97
+ end
98
+
99
+ should "set finder to :first" do
100
+ DynamicFinder.new(:find_or_initialize_by_foo).finder.should == :first
101
+ end
102
+
103
+ should "set instantiator to new" do
104
+ DynamicFinder.new(:find_or_initialize_by_foo).instantiator.should == :new
105
+ end
106
+ end
107
+
108
+ context "find_or_create_by" do
109
+ should "parse one attribute" do
110
+ DynamicFinder.new(:find_or_create_by_foo).attributes.should == %w(foo)
111
+ end
112
+
113
+ should "parse multiple attributes" do
114
+ DynamicFinder.new(:find_or_create_by_foo_and_bar).attributes.should == %w(foo bar)
115
+ end
116
+
117
+ should "set finder to :first" do
118
+ DynamicFinder.new(:find_or_create_by_foo).finder.should == :first
119
+ end
120
+
121
+ should "set instantiator to new" do
122
+ DynamicFinder.new(:find_or_create_by_foo).instantiator.should == :create
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,709 @@
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 "be overrideable by modules" do
584
+ @document = Class.new do
585
+ include MongoMapper::Document
586
+ key :other_child, String
587
+ end
588
+
589
+ child = @document.new
590
+ child.other_child.should be_nil
591
+
592
+ @document.send :include, KeyOverride
593
+
594
+ overriden_child = @document.new
595
+ overriden_child.other_child.should == 'special result'
596
+ end
597
+ end
598
+
599
+ context "reading an attribute before typcasting" do
600
+ should "work for defined keys" do
601
+ doc = @document.new(:name => 12)
602
+ doc.name_before_typecast.should == 12
603
+ end
604
+
605
+ should "raise no method error for undefined keys" do
606
+ doc = @document.new
607
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
608
+ end
609
+
610
+ should "be accessible for use in a document" do
611
+ @document.class_eval do
612
+ def untypcasted_name
613
+ read_attribute_before_typecast(:name)
614
+ end
615
+ end
616
+
617
+ doc = @document.new(:name => 12)
618
+ doc.name.should == '12'
619
+ doc.untypcasted_name.should == 12
620
+ end
621
+ end
622
+
623
+ context "writing an attribute" do
624
+ should "work for defined keys" do
625
+ doc = @document.new
626
+ doc.name = 'John'
627
+ doc.name.should == 'John'
628
+ end
629
+
630
+ should "raise no method error for undefined keys" do
631
+ doc = @document.new
632
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
633
+ end
634
+
635
+ should "typecast value" do
636
+ doc = @document.new
637
+ doc.name = 1234
638
+ doc.name.should == '1234'
639
+ doc.age = '21'
640
+ doc.age.should == 21
641
+ end
642
+
643
+ should "be accessible for use in the model" do
644
+ @document.class_eval do
645
+ def name_and_age=(new_value)
646
+ new_value.match(/([^\(\s]+) \((.*)\)/)
647
+ write_attribute :name, $1
648
+ write_attribute :age, $2
649
+ end
650
+ end
651
+
652
+ doc = @document.new
653
+ doc.name_and_age = 'Frank (62)'
654
+ doc.name.should == 'Frank'
655
+ doc.age.should == 62
656
+ end
657
+
658
+ should "be overrideable by modules" do
659
+ @document = Class.new do
660
+ include MongoMapper::Document
661
+ key :other_child, String
662
+ end
663
+
664
+ child = @document.new(:other_child => 'foo')
665
+ child.other_child.should == 'foo'
666
+
667
+ @document.send :include, KeyOverride
668
+
669
+ overriden_child = @document.new(:other_child => 'foo')
670
+ overriden_child.other_child.should == 'foo modified'
671
+ end
672
+ end # writing an attribute
673
+
674
+ context "checking if an attributes value is present" do
675
+ should "work for defined keys" do
676
+ doc = @document.new
677
+ doc.name?.should be_false
678
+ doc.name = 'John'
679
+ doc.name?.should be_true
680
+ end
681
+
682
+ should "raise no method error for undefined keys" do
683
+ doc = @document.new
684
+ lambda { doc.fart? }.should raise_error(NoMethodError)
685
+ end
686
+ end
687
+
688
+ context "equality" do
689
+ setup do
690
+ @oid = Mongo::ObjectID.new
691
+ end
692
+ should "be equal if id and class are the same" do
693
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be(true)
694
+ end
695
+
696
+ should "not be equal if class same but id different" do
697
+ (@document.new('_id' => @oid) == @document.new('_id' => Mongo::ObjectID.new)).should be(false)
698
+ end
699
+
700
+ should "not be equal if id same but class different" do
701
+ @another_document = Class.new do
702
+ include MongoMapper::Document
703
+ end
704
+
705
+ (@document.new('_id' => @oid) == @another_document.new('_id' => @oid)).should be(false)
706
+ end
707
+ end
708
+ end # instance of a embedded document
709
+ end