djsun-mongomapper 0.3.5.5 → 0.4.1.2

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 (44) hide show
  1. data/README.rdoc +38 -38
  2. data/Rakefile +87 -73
  3. data/VERSION +1 -1
  4. data/lib/mongomapper.rb +67 -71
  5. data/lib/mongomapper/associations.rb +86 -84
  6. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -34
  7. data/lib/mongomapper/associations/many_embedded_proxy.rb +67 -17
  8. data/lib/mongomapper/associations/proxy.rb +74 -73
  9. data/lib/mongomapper/document.rb +342 -348
  10. data/lib/mongomapper/embedded_document.rb +354 -274
  11. data/lib/mongomapper/finder_options.rb +84 -84
  12. data/lib/mongomapper/key.rb +32 -76
  13. data/lib/mongomapper/rails_compatibility/document.rb +14 -14
  14. data/lib/mongomapper/rails_compatibility/embedded_document.rb +26 -24
  15. data/lib/mongomapper/support.rb +156 -29
  16. data/lib/mongomapper/validations.rb +69 -47
  17. data/test/custom_matchers.rb +48 -0
  18. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -56
  19. data/test/functional/associations/test_belongs_to_proxy.rb +48 -49
  20. data/test/functional/associations/test_many_documents_as_proxy.rb +208 -253
  21. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +130 -130
  22. data/test/functional/associations/test_many_embedded_proxy.rb +168 -106
  23. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -262
  24. data/test/functional/test_binary.rb +21 -0
  25. data/test/functional/test_document.rb +946 -952
  26. data/test/functional/test_embedded_document.rb +98 -0
  27. data/test/functional/test_pagination.rb +87 -80
  28. data/test/functional/test_rails_compatibility.rb +29 -29
  29. data/test/functional/test_validations.rb +262 -172
  30. data/test/models.rb +169 -169
  31. data/test/test_helper.rb +28 -66
  32. data/test/unit/serializers/test_json_serializer.rb +193 -193
  33. data/test/unit/test_document.rb +161 -123
  34. data/test/unit/test_embedded_document.rb +643 -547
  35. data/test/unit/test_finder_options.rb +183 -183
  36. data/test/unit/test_key.rb +175 -247
  37. data/test/unit/test_rails_compatibility.rb +38 -33
  38. data/test/unit/test_serializations.rb +52 -52
  39. data/test/unit/test_support.rb +268 -0
  40. data/test/unit/test_time_zones.rb +40 -0
  41. data/test/unit/test_validations.rb +499 -258
  42. metadata +22 -12
  43. data/History +0 -76
  44. data/mongomapper.gemspec +0 -145
@@ -1,123 +1,161 @@
1
- require 'test_helper'
2
- require 'models'
3
-
4
- class DocumentTest < Test::Unit::TestCase
5
- context "The Document Class" do
6
- setup do
7
- @document = Class.new do
8
- include MongoMapper::Document
9
- end
10
- end
11
-
12
- should "track its descendants" do
13
- MongoMapper::Document.descendants.should include(@document)
14
- end
15
-
16
- should "use default database by default" do
17
- @document.database.should == MongoMapper.database
18
- end
19
-
20
- should "have a connection" do
21
- @document.connection.should be_instance_of(Mongo::Connection)
22
- end
23
-
24
- should "allow setting different connection without affecting the default" do
25
- conn = Mongo::Connection.new
26
- @document.connection conn
27
- @document.connection.should == conn
28
- @document.connection.should_not == MongoMapper.connection
29
- end
30
-
31
- should "allow setting a different database without affecting the default" do
32
- @document.database AlternateDatabase
33
- @document.database.name.should == AlternateDatabase
34
-
35
- another_document = Class.new do
36
- include MongoMapper::Document
37
- end
38
- another_document.database.should == MongoMapper.database
39
- end
40
-
41
- should "default collection name to class name tableized" do
42
- class Item
43
- include MongoMapper::Document
44
- end
45
-
46
- Item.collection.should be_instance_of(Mongo::Collection)
47
- Item.collection.name.should == 'items'
48
- end
49
-
50
- should "allow setting the collection name" do
51
- @document.collection('foobar')
52
- @document.collection.should be_instance_of(Mongo::Collection)
53
- @document.collection.name.should == 'foobar'
54
- end
55
- end # Document class
56
-
57
- context "Documents that inherit from other documents" do
58
- should "default collection to inherited class" do
59
- Message.collection.name.should == 'messages'
60
- Enter.collection.name.should == 'messages'
61
- Exit.collection.name.should == 'messages'
62
- Chat.collection.name.should == 'messages'
63
- end
64
-
65
- should "track subclasses" do
66
- Message.subclasses.should == [Enter, Exit, Chat]
67
- end
68
- end
69
-
70
- context "An instance of a document" do
71
- setup do
72
- @document = Class.new do
73
- include MongoMapper::Document
74
-
75
- key :name, String
76
- key :age, Integer
77
- end
78
- @document.collection.clear
79
- end
80
-
81
- should "have access to the class's collection" do
82
- doc = @document.new
83
- doc.collection.should == @document.collection
84
- end
85
-
86
- should "use default values if defined for keys" do
87
- @document.key :active, Boolean, :default => true
88
-
89
- @document.new.active.should be_true
90
- @document.new(:active => false).active.should be_false
91
- end
92
-
93
- context "new?" do
94
- should "be true if no id" do
95
- @document.new.new?.should be_true
96
- end
97
-
98
- should "be true if id but using custom id and not saved yet" do
99
- doc = @document.new
100
- doc.id = '1234'
101
- doc.new?.should be_true
102
- end
103
- end
104
-
105
- context "equality" do
106
- should "be equal if id and class are the same" do
107
- (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
108
- end
109
-
110
- should "not be equal if class same but id different" do
111
- (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
112
- end
113
-
114
- should "not be equal if id same but class different" do
115
- @another_document = Class.new do
116
- include MongoMapper::Document
117
- end
118
-
119
- (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
120
- end
121
- end
122
- end # instance of a document
123
- end # DocumentTest
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class DocumentTest < Test::Unit::TestCase
5
+ context "The Document Class" do
6
+ setup do
7
+ @document = Class.new do
8
+ include MongoMapper::Document
9
+ end
10
+ end
11
+
12
+ should "track its descendants" do
13
+ MongoMapper::Document.descendants.should include(@document)
14
+ end
15
+
16
+ should "use default database by default" do
17
+ @document.database.should == MongoMapper.database
18
+ end
19
+
20
+ should "have a connection" do
21
+ @document.connection.should be_instance_of(Mongo::Connection)
22
+ end
23
+
24
+ should "allow setting different connection without affecting the default" do
25
+ conn = Mongo::Connection.new
26
+ @document.connection conn
27
+ @document.connection.should == conn
28
+ @document.connection.should_not == MongoMapper.connection
29
+ end
30
+
31
+ should "allow setting a different database without affecting the default" do
32
+ @document.database AlternateDatabase
33
+ @document.database.name.should == AlternateDatabase
34
+
35
+ another_document = Class.new do
36
+ include MongoMapper::Document
37
+ end
38
+ another_document.database.should == MongoMapper.database
39
+ end
40
+
41
+ should "default collection name to class name tableized" do
42
+ class Item
43
+ include MongoMapper::Document
44
+ end
45
+
46
+ Item.collection.should be_instance_of(Mongo::Collection)
47
+ Item.collection.name.should == 'items'
48
+ end
49
+
50
+ should "allow setting the collection name" do
51
+ @document.set_collection_name('foobar')
52
+ @document.collection.should be_instance_of(Mongo::Collection)
53
+ @document.collection.name.should == 'foobar'
54
+ end
55
+ end # Document class
56
+
57
+ context "Documents that inherit from other documents" do
58
+ should "default collection name to inherited class" do
59
+ Message.collection_name.should == 'messages'
60
+ Enter.collection_name.should == 'messages'
61
+ Exit.collection_name.should == 'messages'
62
+ Chat.collection_name.should == 'messages'
63
+ end
64
+
65
+ should "default associations to inherited class" do
66
+ Message.associations.keys.should include("room")
67
+ Enter.associations.keys.should include("room")
68
+ Exit.associations.keys.should include("room")
69
+ Chat.associations.keys.should include("room")
70
+ end
71
+
72
+ should "track subclasses" do
73
+ Message.subclasses.should == [Enter, Exit, Chat]
74
+ end
75
+ end
76
+
77
+ context "An instance of a document" do
78
+ setup do
79
+ @document = Class.new do
80
+ include MongoMapper::Document
81
+
82
+ key :name, String
83
+ key :age, Integer
84
+ end
85
+ @document.collection.clear
86
+ end
87
+
88
+ should "have access to the class's collection" do
89
+ doc = @document.new
90
+ doc.collection.should == @document.collection
91
+ end
92
+
93
+ should "use default values if defined for keys" do
94
+ @document.key :active, Boolean, :default => true
95
+
96
+ @document.new.active.should be_true
97
+ @document.new(:active => false).active.should be_false
98
+ end
99
+
100
+ context "root document" do
101
+ should "have a nil _root_document" do
102
+ @document.new._root_document.should be_nil
103
+ end
104
+
105
+ should "set self to the root document on embedded documents" do
106
+ document = Class.new(RealPerson) do
107
+ many :pets
108
+ end
109
+
110
+ doc = document.new 'pets' => [{}]
111
+ doc.pets.first._root_document.should == doc
112
+ end
113
+ end
114
+
115
+ context "new?" do
116
+ should "be true if no id" do
117
+ @document.new.new?.should be_true
118
+ end
119
+
120
+ should "be true if id but using custom id and not saved yet" do
121
+ doc = @document.new
122
+ doc.id = '1234'
123
+ doc.new?.should be_true
124
+ end
125
+ end
126
+
127
+ context "clone" do
128
+ should "not set the id" do
129
+ doc = @document.create(:name => "foo", :age => 27)
130
+ clone = doc.clone
131
+ clone.should be_new
132
+ end
133
+
134
+ should "copy the attributes" do
135
+ doc = @document.create(:name => "foo", :age => 27)
136
+ clone = doc.clone
137
+ clone.name.should == "foo"
138
+ clone.age.should == 27
139
+ end
140
+ end
141
+
142
+
143
+ context "equality" do
144
+ should "be equal if id and class are the same" do
145
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
146
+ end
147
+
148
+ should "not be equal if class same but id different" do
149
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
150
+ end
151
+
152
+ should "not be equal if id same but class different" do
153
+ @another_document = Class.new do
154
+ include MongoMapper::Document
155
+ end
156
+
157
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
158
+ end
159
+ end
160
+ end # instance of a document
161
+ end # DocumentTest
@@ -1,547 +1,643 @@
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" do
37
- setup do
38
- @klass = Class.new do
39
- include MongoMapper::EmbeddedDocument
40
- end
41
- end
42
-
43
- should "add _id key" do
44
- @klass.keys['_id'].should_not be_nil
45
- end
46
- end
47
-
48
- context "parent_model" do
49
- should "be nil if none of parents ancestors include EmbeddedDocument" do
50
- parent = Class.new
51
- document = Class.new(parent) do
52
- include MongoMapper::EmbeddedDocument
53
- end
54
- document.parent_model.should be_nil
55
- end
56
-
57
- should "work when other modules have been included" do
58
- grandparent = Class.new
59
- parent = Class.new grandparent do
60
- include MongoMapper::EmbeddedDocument
61
- end
62
-
63
- example_module = Module.new
64
- document = Class.new(parent) do
65
- include MongoMapper::EmbeddedDocument
66
- include example_module
67
- end
68
-
69
- document.parent_model.should == parent
70
- end
71
-
72
- should "find parent" do
73
- Parent.parent_model.should == Grandparent
74
- Child.parent_model.should == Parent
75
- end
76
- end
77
-
78
- context "defining a key" do
79
- setup do
80
- @document = Class.new do
81
- include MongoMapper::EmbeddedDocument
82
- end
83
- end
84
-
85
- should "work with name" do
86
- key = @document.key(:name)
87
- key.name.should == 'name'
88
- end
89
-
90
- should "work with name and type" do
91
- key = @document.key(:name, String)
92
- key.name.should == 'name'
93
- key.type.should == String
94
- end
95
-
96
- should "work with name, type and options" do
97
- key = @document.key(:name, String, :required => true)
98
- key.name.should == 'name'
99
- key.type.should == String
100
- key.options[:required].should be_true
101
- end
102
-
103
- should "work with name and options" do
104
- key = @document.key(:name, :required => true)
105
- key.name.should == 'name'
106
- key.options[:required].should be_true
107
- end
108
-
109
- should "be tracked per document" do
110
- @document.key(:name, String)
111
- @document.key(:age, Integer)
112
- @document.keys['name'].name.should == 'name'
113
- @document.keys['name'].type.should == String
114
- @document.keys['age'].name.should == 'age'
115
- @document.keys['age'].type.should == Integer
116
- end
117
-
118
- should "not be redefinable" do
119
- @document.key(:foo, String)
120
- @document.keys['foo'].type.should == String
121
- @document.key(:foo, Integer)
122
- @document.keys['foo'].type.should == String
123
- end
124
-
125
- should "create reader method" do
126
- @document.new.should_not respond_to(:foo)
127
- @document.key(:foo, String)
128
- @document.new.should respond_to(:foo)
129
- end
130
-
131
- should "create reader before typecast method" do
132
- @document.new.should_not respond_to(:foo_before_typecast)
133
- @document.key(:foo, String)
134
- @document.new.should respond_to(:foo_before_typecast)
135
- end
136
-
137
- should "create writer method" do
138
- @document.new.should_not respond_to(:foo=)
139
- @document.key(:foo, String)
140
- @document.new.should respond_to(:foo=)
141
- end
142
-
143
- should "create boolean method" do
144
- @document.new.should_not respond_to(:foo?)
145
- @document.key(:foo, String)
146
- @document.new.should respond_to(:foo?)
147
- end
148
- end
149
-
150
- context "keys" do
151
- should "be inherited" do
152
- Grandparent.keys.keys.sort.should == ['_id', 'grandparent']
153
- Parent.keys.keys.sort.should == ['_id', 'grandparent', 'parent']
154
- Child.keys.keys.sort.should == ['_id', 'child', 'grandparent', 'parent']
155
- end
156
-
157
- should "propogate to subclasses if key added after class definition" do
158
- Grandparent.key :_type, String
159
-
160
- Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
161
- Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
162
- Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
163
- end
164
-
165
- should "not add anonymous objects to the ancestor tree" do
166
- OtherChild.ancestors.any? { |a| a.name.blank? }.should be_false
167
- end
168
-
169
- should "not include descendant keys" do
170
- lambda { Parent.new.other_child }.should raise_error
171
- end
172
- end
173
-
174
- context "subclasses" do
175
- should "default to nil" do
176
- Child.subclasses.should be_nil
177
- end
178
-
179
- should "be recorded" do
180
- Grandparent.subclasses.should == [Parent]
181
- Parent.subclasses.should == [Child, OtherChild]
182
- end
183
- end
184
-
185
- context "Applying default values for keys" do
186
- setup do
187
- @document = Class.new do
188
- include MongoMapper::EmbeddedDocument
189
-
190
- key :name, String, :default => 'foo'
191
- key :age, Integer, :default => 20
192
- key :net_worth, Float, :default => 100.00
193
- key :active, Boolean, :default => true
194
- key :smart, Boolean, :default => false
195
- key :skills, Array, :default => [1]
196
- key :options, Hash, :default => {'foo' => 'bar'}
197
- end
198
-
199
- @doc = @document.new
200
- end
201
-
202
- should "work for strings" do
203
- @doc.name.should == 'foo'
204
- end
205
-
206
- should "work for integers" do
207
- @doc.age.should == 20
208
- end
209
-
210
- should "work for floats" do
211
- @doc.net_worth.should == 100.00
212
- end
213
-
214
- should "work for booleans" do
215
- @doc.active.should == true
216
- @doc.smart.should == false
217
- end
218
-
219
- should "work for arrays" do
220
- @doc.skills.should == [1]
221
- @doc.skills << 2
222
- @doc.skills.should == [1, 2]
223
- end
224
-
225
- should "work for hashes" do
226
- @doc.options['foo'].should == 'bar'
227
- @doc.options['baz'] = 'wick'
228
- @doc.options['baz'].should == 'wick'
229
- end
230
- end
231
-
232
- context "An instance of an embedded document" do
233
- setup do
234
- @document = Class.new do
235
- include MongoMapper::EmbeddedDocument
236
-
237
- key :name, String
238
- key :age, Integer
239
- end
240
- end
241
-
242
- should "automatically have an _id key" do
243
- @document.keys.keys.should include('_id')
244
- end
245
-
246
- should "have id method that sets _id" do
247
- doc = @document.new
248
- doc.id.should == doc._id.to_s
249
- end
250
-
251
- context "setting custom id" do
252
- should "set _id" do
253
- doc = @document.new(:id => '1234')
254
- doc._id.should == '1234'
255
- end
256
-
257
- should "know that custom id is set" do
258
- doc = @document.new
259
- doc.using_custom_id?.should be_false
260
- doc.id = '1234'
261
- doc.using_custom_id?.should be_true
262
- end
263
- end
264
-
265
- context "being initialized" do
266
- should "accept a hash that sets keys and values" do
267
- doc = @document.new(:name => 'John', :age => 23)
268
- doc.attributes.keys.sort.should == ['_id', 'age', 'name']
269
- doc.attributes['name'].should == 'John'
270
- doc.attributes['age'].should == 23
271
- end
272
-
273
- should "be able to assign keys dynamically" do
274
- doc = @document.new(:name => 'John', :skills => ['ruby', 'rails'])
275
- doc.name.should == 'John'
276
- doc.skills.should == ['ruby', 'rails']
277
- end
278
-
279
- should "not throw error if initialized with nil" do
280
- lambda {
281
- @document.new(nil)
282
- }.should_not raise_error
283
- end
284
- end
285
-
286
- context "initialized when _type key present" do
287
- setup do
288
- ::FooBar = Class.new do
289
- include MongoMapper::EmbeddedDocument
290
- key :_type, String
291
- end
292
- end
293
-
294
- teardown do
295
- Object.send(:remove_const, :FooBar)
296
- end
297
-
298
- should "set _type to class name" do
299
- FooBar.new._type.should == 'FooBar'
300
- end
301
-
302
- should "not change _type if already set" do
303
- FooBar.new(:_type => 'Foo')._type.should == 'Foo'
304
- end
305
- end
306
-
307
- context "mass assigning keys" do
308
- should "update values for keys provided" do
309
- doc = @document.new(:name => 'foobar', :age => 10)
310
- doc.attributes = {:name => 'new value', :age => 5}
311
- doc.attributes[:name].should == 'new value'
312
- doc.attributes[:age].should == 5
313
- end
314
-
315
- should "not update values for keys that were not provided" do
316
- doc = @document.new(:name => 'foobar', :age => 10)
317
- doc.attributes = {:name => 'new value'}
318
- doc.attributes[:name].should == 'new value'
319
- doc.attributes[:age].should == 10
320
- end
321
-
322
- should "not ignore keys that have methods defined" do
323
- @document.class_eval do
324
- attr_writer :password
325
-
326
- def passwd
327
- @password
328
- end
329
- end
330
-
331
- doc = @document.new(:name => 'foobar', :password => 'secret')
332
- doc.passwd.should == 'secret'
333
- end
334
-
335
- should "typecast key values" do
336
- doc = @document.new(:name => 1234, :age => '21')
337
- doc.name.should == '1234'
338
- doc.age.should == 21
339
- end
340
- end
341
-
342
- context "attributes" do
343
- should "default to hash with _id" do
344
- doc = @document.new
345
- doc.attributes.keys.sort.should == %w(_id age name)
346
- end
347
-
348
- should "return all keys, including ones with nil values" do
349
- doc = @document.new(:name => 'string', :age => nil)
350
- doc.attributes.keys.sort.should == %w(_id age name)
351
- doc.attributes.values.should include('string')
352
- end
353
- end
354
-
355
- context "key shorcut access" do
356
- should "be able to read key with []" do
357
- doc = @document.new(:name => 'string')
358
- doc[:name].should == 'string'
359
- end
360
-
361
- context "[]=" do
362
- should "write key value for existing key" do
363
- doc = @document.new
364
- doc[:name] = 'string'
365
- doc[:name].should == 'string'
366
- end
367
-
368
- should "create key and write value for missing key" do
369
- doc = @document.new
370
- doc[:foo] = 'string'
371
- @document.keys.keys.include?('foo').should be_true
372
- doc[:foo].should == 'string'
373
- end
374
- end
375
- end
376
-
377
- context "indifferent access" do
378
- should "be enabled for keys" do
379
- doc = @document.new(:name => 'string')
380
- doc.attributes[:name].should == 'string'
381
- doc.attributes['name'].should == 'string'
382
- end
383
- end
384
-
385
- context "reading an attribute" do
386
- should "work for defined keys" do
387
- doc = @document.new(:name => 'string')
388
- doc.name.should == 'string'
389
- end
390
-
391
- should "raise no method error for undefined keys" do
392
- doc = @document.new
393
- lambda { doc.fart }.should raise_error(NoMethodError)
394
- end
395
-
396
- should "be accessible for use in the model" do
397
- @document.class_eval do
398
- def name_and_age
399
- "#{read_attribute(:name)} (#{read_attribute(:age)})"
400
- end
401
- end
402
-
403
- doc = @document.new(:name => 'John', :age => 27)
404
- doc.name_and_age.should == 'John (27)'
405
- end
406
-
407
- should "set instance variable" do
408
- @document.key :foo, Array
409
- doc = @document.new
410
- doc.instance_variable_get("@foo").should be_nil
411
- doc.foo
412
- doc.instance_variable_get("@foo").should == []
413
- end
414
-
415
- should "not set instance variable if frozen" do
416
- @document.key :foo, Array
417
- doc = @document.new
418
- doc.instance_variable_get("@foo").should be_nil
419
- doc.freeze
420
- doc.foo
421
- doc.instance_variable_get("@foo").should be_nil
422
- end
423
-
424
- should "be overrideable by modules" do
425
- @document = Class.new do
426
- include MongoMapper::Document
427
- key :other_child, String
428
- end
429
-
430
- child = @document.new
431
- child.other_child.should be_nil
432
-
433
- @document.send :include, KeyOverride
434
-
435
- overriden_child = @document.new
436
- overriden_child.other_child.should == 'special result'
437
- end
438
- end
439
-
440
- context "reading an attribute before typcasting" do
441
- should "work for defined keys" do
442
- doc = @document.new(:name => 12)
443
- doc.name_before_typecast.should == 12
444
- end
445
-
446
- should "raise no method error for undefined keys" do
447
- doc = @document.new
448
- lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
449
- end
450
-
451
- should "be accessible for use in a document" do
452
- @document.class_eval do
453
- def untypcasted_name
454
- read_attribute_before_typecast(:name)
455
- end
456
- end
457
-
458
- doc = @document.new(:name => 12)
459
- doc.name.should == '12'
460
- doc.untypcasted_name.should == 12
461
- end
462
- end
463
-
464
- context "writing an attribute" do
465
- should "work for defined keys" do
466
- doc = @document.new
467
- doc.name = 'John'
468
- doc.name.should == 'John'
469
- end
470
-
471
- should "raise no method error for undefined keys" do
472
- doc = @document.new
473
- lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
474
- end
475
-
476
- should "typecast value" do
477
- doc = @document.new
478
- doc.name = 1234
479
- doc.name.should == '1234'
480
- doc.age = '21'
481
- doc.age.should == 21
482
- end
483
-
484
- should "be accessible for use in the model" do
485
- @document.class_eval do
486
- def name_and_age=(new_value)
487
- new_value.match(/([^\(\s]+) \((.*)\)/)
488
- write_attribute :name, $1
489
- write_attribute :age, $2
490
- end
491
- end
492
-
493
- doc = @document.new
494
- doc.name_and_age = 'Frank (62)'
495
- doc.name.should == 'Frank'
496
- doc.age.should == 62
497
- end
498
-
499
- should "be overrideable by modules" do
500
- @document = Class.new do
501
- include MongoMapper::Document
502
- key :other_child, String
503
- end
504
-
505
- child = @document.new(:other_child => 'foo')
506
- child.other_child.should == 'foo'
507
-
508
- @document.send :include, KeyOverride
509
-
510
- overriden_child = @document.new(:other_child => 'foo')
511
- overriden_child.other_child.should == 'foo modified'
512
- end
513
- end # writing an attribute
514
-
515
- context "checking if an attributes value is present" do
516
- should "work for defined keys" do
517
- doc = @document.new
518
- doc.name?.should be_false
519
- doc.name = 'John'
520
- doc.name?.should be_true
521
- end
522
-
523
- should "raise no method error for undefined keys" do
524
- doc = @document.new
525
- lambda { doc.fart? }.should raise_error(NoMethodError)
526
- end
527
- end
528
-
529
- context "equality" do
530
- should "be equal if id and class are the same" do
531
- (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
532
- end
533
-
534
- should "not be equal if class same but id different" do
535
- (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
536
- end
537
-
538
- should "not be equal if id same but class different" do
539
- @another_document = Class.new do
540
- include MongoMapper::Document
541
- end
542
-
543
- (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
544
- end
545
- end
546
- end # instance of a embedded document
547
- end
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 "add _id key" do
44
+ @klass.keys['_id'].should_not be_nil
45
+ end
46
+
47
+ context "#to_mongo" do
48
+ should "be nil if nil" do
49
+ @klass.to_mongo(nil).should be_nil
50
+ end
51
+
52
+ should "convert to_mongo for other values" do
53
+ doc = @klass.new(:foo => 'bar')
54
+ to_mongo = @klass.to_mongo(doc)
55
+ to_mongo.is_a?(Hash).should be_true
56
+ to_mongo['foo'].should == 'bar'
57
+ end
58
+ end
59
+
60
+ context "#from_mongo" do
61
+ should "be nil if nil" do
62
+ @klass.from_mongo(nil).should be_nil
63
+ end
64
+
65
+ should "be instance if instance of class" do
66
+ doc = @klass.new
67
+ @klass.from_mongo(doc).should == doc
68
+ end
69
+
70
+ should "be instance if hash of attributes" do
71
+ doc = @klass.from_mongo({:foo => 'bar'})
72
+ doc.instance_of?(@klass).should be_true
73
+ doc.foo.should == 'bar'
74
+ end
75
+ end
76
+ end
77
+
78
+ context "parent_model" do
79
+ should "be nil if none of parents ancestors include EmbeddedDocument" do
80
+ parent = Class.new
81
+ document = Class.new(parent) do
82
+ include MongoMapper::EmbeddedDocument
83
+ end
84
+ document.parent_model.should be_nil
85
+ end
86
+
87
+ should "work when other modules have been included" do
88
+ grandparent = Class.new
89
+ parent = Class.new grandparent do
90
+ include MongoMapper::EmbeddedDocument
91
+ end
92
+
93
+ example_module = Module.new
94
+ document = Class.new(parent) do
95
+ include MongoMapper::EmbeddedDocument
96
+ include example_module
97
+ end
98
+
99
+ document.parent_model.should == parent
100
+ end
101
+
102
+ should "find parent" do
103
+ Parent.parent_model.should == Grandparent
104
+ Child.parent_model.should == Parent
105
+ end
106
+ end
107
+
108
+ context "defining a key" do
109
+ setup do
110
+ @document = Class.new do
111
+ include MongoMapper::EmbeddedDocument
112
+ end
113
+ end
114
+
115
+ should "work with name" do
116
+ key = @document.key(:name)
117
+ key.name.should == 'name'
118
+ end
119
+
120
+ should "work with name and type" do
121
+ key = @document.key(:name, String)
122
+ key.name.should == 'name'
123
+ key.type.should == String
124
+ end
125
+
126
+ should "work with name, type and options" do
127
+ key = @document.key(:name, String, :required => true)
128
+ key.name.should == 'name'
129
+ key.type.should == String
130
+ key.options[:required].should be_true
131
+ end
132
+
133
+ should "work with name and options" do
134
+ key = @document.key(:name, :required => true)
135
+ key.name.should == 'name'
136
+ key.options[:required].should be_true
137
+ end
138
+
139
+ should "be tracked per document" do
140
+ @document.key(:name, String)
141
+ @document.key(:age, Integer)
142
+ @document.keys['name'].name.should == 'name'
143
+ @document.keys['name'].type.should == String
144
+ @document.keys['age'].name.should == 'age'
145
+ @document.keys['age'].type.should == Integer
146
+ end
147
+
148
+ should "not be redefinable" do
149
+ @document.key(:foo, String)
150
+ @document.keys['foo'].type.should == String
151
+ @document.key(:foo, Integer)
152
+ @document.keys['foo'].type.should == String
153
+ end
154
+
155
+ should "create reader method" do
156
+ @document.new.should_not respond_to(:foo)
157
+ @document.key(:foo, String)
158
+ @document.new.should respond_to(:foo)
159
+ end
160
+
161
+ should "create reader before typecast method" do
162
+ @document.new.should_not respond_to(:foo_before_typecast)
163
+ @document.key(:foo, String)
164
+ @document.new.should respond_to(:foo_before_typecast)
165
+ end
166
+
167
+ should "create writer method" do
168
+ @document.new.should_not respond_to(:foo=)
169
+ @document.key(:foo, String)
170
+ @document.new.should respond_to(:foo=)
171
+ end
172
+
173
+ should "create boolean method" do
174
+ @document.new.should_not respond_to(:foo?)
175
+ @document.key(:foo, String)
176
+ @document.new.should respond_to(:foo?)
177
+ end
178
+ end
179
+
180
+ context "keys" do
181
+ should "be inherited" do
182
+ Grandparent.keys.keys.sort.should == ['_id', 'grandparent']
183
+ Parent.keys.keys.sort.should == ['_id', 'grandparent', 'parent']
184
+ Child.keys.keys.sort.should == ['_id', 'child', 'grandparent', 'parent']
185
+ end
186
+
187
+ should "propogate to subclasses if key added after class definition" do
188
+ Grandparent.key :_type, String
189
+
190
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
191
+ Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
192
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
193
+ end
194
+
195
+ should "not add anonymous objects to the ancestor tree" do
196
+ OtherChild.ancestors.any? { |a| a.name.blank? }.should be_false
197
+ end
198
+
199
+ should "not include descendant keys" do
200
+ lambda { Parent.new.other_child }.should raise_error
201
+ end
202
+ end
203
+
204
+ context "#inspect" do
205
+ setup do
206
+ @document = Class.new do
207
+ include MongoMapper::Document
208
+ key :animals
209
+ end
210
+ end
211
+
212
+ should "call inspect on the document's attributes instead of to_s" do
213
+ doc = @document.new
214
+ doc.animals = %w(dog cat)
215
+ doc.inspect.should include(%(animals: ["dog", "cat"]))
216
+ end
217
+ end
218
+
219
+ context "subclasses" do
220
+ should "default to nil" do
221
+ Child.subclasses.should be_nil
222
+ end
223
+
224
+ should "be recorded" do
225
+ Grandparent.subclasses.should == [Parent]
226
+ Parent.subclasses.should == [Child, OtherChild]
227
+ end
228
+ end
229
+
230
+ context "Applying default values for keys" do
231
+ setup do
232
+ @document = Class.new do
233
+ include MongoMapper::EmbeddedDocument
234
+
235
+ key :name, String, :default => 'foo'
236
+ key :age, Integer, :default => 20
237
+ key :net_worth, Float, :default => 100.00
238
+ key :active, Boolean, :default => true
239
+ key :smart, Boolean, :default => false
240
+ key :skills, Array, :default => [1]
241
+ key :options, Hash, :default => {'foo' => 'bar'}
242
+ end
243
+
244
+ @doc = @document.new
245
+ end
246
+
247
+ should "work for strings" do
248
+ @doc.name.should == 'foo'
249
+ end
250
+
251
+ should "work for integers" do
252
+ @doc.age.should == 20
253
+ end
254
+
255
+ should "work for floats" do
256
+ @doc.net_worth.should == 100.00
257
+ end
258
+
259
+ should "work for booleans" do
260
+ @doc.active.should == true
261
+ @doc.smart.should == false
262
+ end
263
+
264
+ should "work for arrays" do
265
+ @doc.skills.should == [1]
266
+ @doc.skills << 2
267
+ @doc.skills.should == [1, 2]
268
+ end
269
+
270
+ should "work for hashes" do
271
+ @doc.options['foo'].should == 'bar'
272
+ @doc.options['baz'] = 'wick'
273
+ @doc.options['baz'].should == 'wick'
274
+ end
275
+ end
276
+
277
+ context "An instance of an embedded document" do
278
+ setup do
279
+ @document = Class.new do
280
+ include MongoMapper::EmbeddedDocument
281
+
282
+ key :name, String
283
+ key :age, Integer
284
+ end
285
+ end
286
+
287
+ should "automatically have an _id key" do
288
+ @document.keys.keys.should include('_id')
289
+ end
290
+
291
+ should "have id method that sets _id" do
292
+ doc = @document.new
293
+ doc.id.should == doc._id.to_s
294
+ end
295
+
296
+ should "have a nil _root_document" do
297
+ @document.new._root_document.should be_nil
298
+ end
299
+
300
+ context "setting custom id" do
301
+ should "set _id" do
302
+ doc = @document.new(:id => '1234')
303
+ doc._id.should == '1234'
304
+ end
305
+
306
+ should "know that custom id is set" do
307
+ doc = @document.new
308
+ doc.using_custom_id?.should be_false
309
+ doc.id = '1234'
310
+ doc.using_custom_id?.should be_true
311
+ end
312
+ end
313
+
314
+ context "being initialized" do
315
+ should "accept a hash that sets keys and values" do
316
+ doc = @document.new(:name => 'John', :age => 23)
317
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
318
+ doc.attributes['name'].should == 'John'
319
+ doc.attributes['age'].should == 23
320
+ end
321
+
322
+ should "be able to assign keys dynamically" do
323
+ doc = @document.new(:name => 'John', :skills => ['ruby', 'rails'])
324
+ doc.name.should == 'John'
325
+ doc.skills.should == ['ruby', 'rails']
326
+ end
327
+
328
+ should "set the root on embedded documents" do
329
+ document = Class.new(@document) do
330
+ many :children
331
+ end
332
+
333
+ doc = document.new :_root_document => 'document', 'children' => [{}]
334
+ doc.children.first._root_document.should == 'document'
335
+ end
336
+
337
+ should "not throw error if initialized with nil" do
338
+ lambda {
339
+ @document.new(nil)
340
+ }.should_not raise_error
341
+ end
342
+ end
343
+
344
+ context "initialized when _type key present" do
345
+ setup do
346
+ ::FooBar = Class.new do
347
+ include MongoMapper::EmbeddedDocument
348
+ key :_type, String
349
+ end
350
+ end
351
+
352
+ teardown do
353
+ Object.send(:remove_const, :FooBar)
354
+ end
355
+
356
+ should "set _type to class name" do
357
+ FooBar.new._type.should == 'FooBar'
358
+ end
359
+
360
+ should "not change _type if already set" do
361
+ FooBar.new(:_type => 'Foo')._type.should == 'Foo'
362
+ end
363
+ end
364
+
365
+ context "mass assigning keys" do
366
+ should "update values for keys provided" do
367
+ doc = @document.new(:name => 'foobar', :age => 10)
368
+ doc.attributes = {:name => 'new value', :age => 5}
369
+ doc.attributes[:name].should == 'new value'
370
+ doc.attributes[:age].should == 5
371
+ end
372
+
373
+ should "not update values for keys that were not provided" do
374
+ doc = @document.new(:name => 'foobar', :age => 10)
375
+ doc.attributes = {:name => 'new value'}
376
+ doc.attributes[:name].should == 'new value'
377
+ doc.attributes[:age].should == 10
378
+ end
379
+
380
+ should "not ignore keys that have methods defined" do
381
+ @document.class_eval do
382
+ attr_writer :password
383
+
384
+ def passwd
385
+ @password
386
+ end
387
+ end
388
+
389
+ doc = @document.new(:name => 'foobar', :password => 'secret')
390
+ doc.passwd.should == 'secret'
391
+ end
392
+
393
+ should "typecast key values" do
394
+ doc = @document.new(:name => 1234, :age => '21')
395
+ doc.name.should == '1234'
396
+ doc.age.should == 21
397
+ end
398
+ end
399
+
400
+ context "attributes" do
401
+ should "default to hash with all keys" do
402
+ doc = @document.new
403
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
404
+ end
405
+
406
+ should "return all keys with values" do
407
+ doc = @document.new(:name => 'string', :age => nil)
408
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
409
+ doc.attributes.values.should include('string')
410
+ doc.attributes.values.should include(nil)
411
+ end
412
+ end
413
+
414
+ context "to_mongo" do
415
+ should "default to hash with _id key" do
416
+ doc = @document.new
417
+ doc.to_mongo.keys.sort.should == %w(_id age name)
418
+ end
419
+
420
+ should "return all keys" do
421
+ doc = @document.new(:name => 'string', :age => nil)
422
+ doc.to_mongo.keys.sort.should == %w(_id age name)
423
+ end
424
+ end
425
+
426
+ should "convert dates into times" do
427
+ document = Class.new(@document) do
428
+ key :start_date, Date
429
+ end
430
+ doc = document.new :start_date => "12/05/2009"
431
+ doc.start_date.should == Date.new(2009, 12, 05)
432
+ end
433
+
434
+ context "clone" do
435
+ should "regenerate the id" do
436
+ doc = @document.new(:name => "foo", :age => 27)
437
+ doc_id = doc.id
438
+ clone = doc.clone
439
+ clone_id = clone.id
440
+ clone_id.should_not == doc_id
441
+ end
442
+
443
+ should "copy the attributes" do
444
+ doc = @document.new(:name => "foo", :age => 27)
445
+ clone = doc.clone
446
+ clone.name.should == "foo"
447
+ clone.age.should == 27
448
+ end
449
+ end
450
+
451
+ context "key shorcut access" do
452
+ should "be able to read key with []" do
453
+ doc = @document.new(:name => 'string')
454
+ doc[:name].should == 'string'
455
+ end
456
+
457
+ context "[]=" do
458
+ should "write key value for existing key" do
459
+ doc = @document.new
460
+ doc[:name] = 'string'
461
+ doc[:name].should == 'string'
462
+ end
463
+
464
+ should "create key and write value for missing key" do
465
+ doc = @document.new
466
+ doc[:foo] = 'string'
467
+ @document.keys.keys.include?('foo').should be_true
468
+ doc[:foo].should == 'string'
469
+ end
470
+ end
471
+ end
472
+
473
+ context "indifferent access" do
474
+ should "be enabled for keys" do
475
+ doc = @document.new(:name => 'string')
476
+ doc.attributes[:name].should == 'string'
477
+ doc.attributes['name'].should == 'string'
478
+ end
479
+ end
480
+
481
+ context "reading an attribute" do
482
+ should "work for defined keys" do
483
+ doc = @document.new(:name => 'string')
484
+ doc.name.should == 'string'
485
+ end
486
+
487
+ should "raise no method error for undefined keys" do
488
+ doc = @document.new
489
+ lambda { doc.fart }.should raise_error(NoMethodError)
490
+ end
491
+
492
+ should "be accessible for use in the model" do
493
+ @document.class_eval do
494
+ def name_and_age
495
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
496
+ end
497
+ end
498
+
499
+ doc = @document.new(:name => 'John', :age => 27)
500
+ doc.name_and_age.should == 'John (27)'
501
+ end
502
+
503
+ should "set instance variable" do
504
+ @document.key :foo, Array
505
+ doc = @document.new
506
+ doc.instance_variable_get("@foo").should be_nil
507
+ doc.foo
508
+ doc.instance_variable_get("@foo").should == []
509
+ end
510
+
511
+ should "not set instance variable if frozen" do
512
+ @document.key :foo, Array
513
+ doc = @document.new
514
+ doc.instance_variable_get("@foo").should be_nil
515
+ doc.freeze
516
+ doc.foo
517
+ doc.instance_variable_get("@foo").should be_nil
518
+ end
519
+
520
+ should "be overrideable by modules" do
521
+ @document = Class.new do
522
+ include MongoMapper::Document
523
+ key :other_child, String
524
+ end
525
+
526
+ child = @document.new
527
+ child.other_child.should be_nil
528
+
529
+ @document.send :include, KeyOverride
530
+
531
+ overriden_child = @document.new
532
+ overriden_child.other_child.should == 'special result'
533
+ end
534
+ end
535
+
536
+ context "reading an attribute before typcasting" do
537
+ should "work for defined keys" do
538
+ doc = @document.new(:name => 12)
539
+ doc.name_before_typecast.should == 12
540
+ end
541
+
542
+ should "raise no method error for undefined keys" do
543
+ doc = @document.new
544
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
545
+ end
546
+
547
+ should "be accessible for use in a document" do
548
+ @document.class_eval do
549
+ def untypcasted_name
550
+ read_attribute_before_typecast(:name)
551
+ end
552
+ end
553
+
554
+ doc = @document.new(:name => 12)
555
+ doc.name.should == '12'
556
+ doc.untypcasted_name.should == 12
557
+ end
558
+ end
559
+
560
+ context "writing an attribute" do
561
+ should "work for defined keys" do
562
+ doc = @document.new
563
+ doc.name = 'John'
564
+ doc.name.should == 'John'
565
+ end
566
+
567
+ should "raise no method error for undefined keys" do
568
+ doc = @document.new
569
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
570
+ end
571
+
572
+ should "typecast value" do
573
+ doc = @document.new
574
+ doc.name = 1234
575
+ doc.name.should == '1234'
576
+ doc.age = '21'
577
+ doc.age.should == 21
578
+ end
579
+
580
+ should "be accessible for use in the model" do
581
+ @document.class_eval do
582
+ def name_and_age=(new_value)
583
+ new_value.match(/([^\(\s]+) \((.*)\)/)
584
+ write_attribute :name, $1
585
+ write_attribute :age, $2
586
+ end
587
+ end
588
+
589
+ doc = @document.new
590
+ doc.name_and_age = 'Frank (62)'
591
+ doc.name.should == 'Frank'
592
+ doc.age.should == 62
593
+ end
594
+
595
+ should "be overrideable by modules" do
596
+ @document = Class.new do
597
+ include MongoMapper::Document
598
+ key :other_child, String
599
+ end
600
+
601
+ child = @document.new(:other_child => 'foo')
602
+ child.other_child.should == 'foo'
603
+
604
+ @document.send :include, KeyOverride
605
+
606
+ overriden_child = @document.new(:other_child => 'foo')
607
+ overriden_child.other_child.should == 'foo modified'
608
+ end
609
+ end # writing an attribute
610
+
611
+ context "checking if an attributes value is present" do
612
+ should "work for defined keys" do
613
+ doc = @document.new
614
+ doc.name?.should be_false
615
+ doc.name = 'John'
616
+ doc.name?.should be_true
617
+ end
618
+
619
+ should "raise no method error for undefined keys" do
620
+ doc = @document.new
621
+ lambda { doc.fart? }.should raise_error(NoMethodError)
622
+ end
623
+ end
624
+
625
+ context "equality" do
626
+ should "be equal if id and class are the same" do
627
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
628
+ end
629
+
630
+ should "not be equal if class same but id different" do
631
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
632
+ end
633
+
634
+ should "not be equal if id same but class different" do
635
+ @another_document = Class.new do
636
+ include MongoMapper::Document
637
+ end
638
+
639
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
640
+ end
641
+ end
642
+ end # instance of a embedded document
643
+ end