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.
- data/.gitignore +10 -0
- data/LICENSE +20 -0
- data/README.rdoc +53 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/bin/mmconsole +60 -0
- data/lib/mongo_mapper.rb +134 -0
- data/lib/mongo_mapper/associations.rb +183 -0
- data/lib/mongo_mapper/associations/base.rb +110 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +25 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +127 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +53 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
- data/lib/mongo_mapper/associations/proxy.rb +80 -0
- data/lib/mongo_mapper/callbacks.rb +109 -0
- data/lib/mongo_mapper/dirty.rb +136 -0
- data/lib/mongo_mapper/document.rb +481 -0
- data/lib/mongo_mapper/dynamic_finder.rb +35 -0
- data/lib/mongo_mapper/embedded_document.rb +386 -0
- data/lib/mongo_mapper/finder_options.rb +133 -0
- data/lib/mongo_mapper/key.rb +36 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +53 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/serialization.rb +54 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +193 -0
- data/lib/mongo_mapper/validations.rb +41 -0
- data/mongo_mapper.gemspec +171 -0
- data/specs.watchr +32 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +196 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
- data/test/functional/associations/test_many_proxy.rb +384 -0
- data/test/functional/test_associations.rb +44 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_dirty.rb +159 -0
- data/test/functional/test_document.rb +1180 -0
- data/test/functional/test_embedded_document.rb +125 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_pagination.rb +95 -0
- data/test/functional/test_rails_compatibility.rb +25 -0
- data/test/functional/test_string_id_compatibility.rb +72 -0
- data/test/functional/test_validations.rb +369 -0
- data/test/models.rb +271 -0
- data/test/support/custom_matchers.rb +55 -0
- data/test/support/timing.rb +16 -0
- data/test/test_helper.rb +27 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_association_base.rb +166 -0
- data/test/unit/test_document.rb +204 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +718 -0
- data/test/unit/test_finder_options.rb +296 -0
- data/test/unit/test_key.rb +172 -0
- data/test/unit/test_mongo_mapper.rb +65 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +49 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +342 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- 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
|