fcoury-mongomapper 0.2.0

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 (42) hide show
  1. data/.gitignore +7 -0
  2. data/History +30 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/lib/mongomapper.rb +70 -0
  8. data/lib/mongomapper/associations.rb +69 -0
  9. data/lib/mongomapper/associations/array_proxy.rb +6 -0
  10. data/lib/mongomapper/associations/base.rb +54 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
  12. data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
  13. data/lib/mongomapper/associations/has_many_proxy.rb +29 -0
  14. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
  15. data/lib/mongomapper/associations/proxy.rb +66 -0
  16. data/lib/mongomapper/callbacks.rb +106 -0
  17. data/lib/mongomapper/document.rb +276 -0
  18. data/lib/mongomapper/document_rails_compatibility.rb +13 -0
  19. data/lib/mongomapper/embedded_document.rb +248 -0
  20. data/lib/mongomapper/embedded_document_rails_compatibility.rb +22 -0
  21. data/lib/mongomapper/finder_options.rb +81 -0
  22. data/lib/mongomapper/key.rb +82 -0
  23. data/lib/mongomapper/observing.rb +50 -0
  24. data/lib/mongomapper/save_with_validation.rb +19 -0
  25. data/lib/mongomapper/serialization.rb +55 -0
  26. data/lib/mongomapper/serializers/json_serializer.rb +77 -0
  27. data/lib/mongomapper/validations.rb +47 -0
  28. data/mongomapper.gemspec +105 -0
  29. data/test/serializers/test_json_serializer.rb +104 -0
  30. data/test/test_associations.rb +444 -0
  31. data/test/test_callbacks.rb +84 -0
  32. data/test/test_document.rb +1002 -0
  33. data/test/test_embedded_document.rb +253 -0
  34. data/test/test_finder_options.rb +148 -0
  35. data/test/test_helper.rb +62 -0
  36. data/test/test_key.rb +200 -0
  37. data/test/test_mongomapper.rb +28 -0
  38. data/test/test_observing.rb +101 -0
  39. data/test/test_rails_compatibility.rb +73 -0
  40. data/test/test_serializations.rb +54 -0
  41. data/test/test_validations.rb +409 -0
  42. metadata +155 -0
@@ -0,0 +1,444 @@
1
+ require 'test_helper'
2
+
3
+ class Address
4
+ include MongoMapper::EmbeddedDocument
5
+
6
+ key :address, String
7
+ key :city, String
8
+ key :state, String
9
+ key :zip, Integer
10
+ end
11
+
12
+ class Project
13
+ include MongoMapper::Document
14
+
15
+ key :name, String
16
+
17
+ many :statuses
18
+ many :addresses
19
+ end
20
+
21
+ class Status
22
+ include MongoMapper::Document
23
+
24
+ belongs_to :project
25
+ belongs_to :target, :polymorphic => true
26
+
27
+ key :name, String
28
+ end
29
+
30
+ class RealPerson
31
+ include MongoMapper::Document
32
+ many :pets
33
+
34
+ key :name, String
35
+
36
+ def pets_attributes=(pets_attributes)
37
+ self.pets = []
38
+ pets_attributes.each do |attributes|
39
+ self.pets << Pet.new(attributes)
40
+ end
41
+ end
42
+ end
43
+
44
+ class Person
45
+ include MongoMapper::EmbeddedDocument
46
+ key :name, String
47
+ key :child, Person
48
+
49
+ many :pets
50
+
51
+ end
52
+
53
+ class Pet
54
+ include MongoMapper::EmbeddedDocument
55
+
56
+ key :name, String
57
+ key :species, String
58
+ end
59
+
60
+ class Media
61
+ include MongoMapper::EmbeddedDocument
62
+ key :file, String
63
+ end
64
+
65
+ class Video < Media
66
+ key :length, Integer
67
+ end
68
+
69
+ class Image < Media
70
+ key :width, Integer
71
+ key :height, Integer
72
+ end
73
+
74
+ class Music < Media
75
+ key :bitrate, String
76
+ end
77
+
78
+ class Catalog
79
+ include MongoMapper::Document
80
+
81
+ many :medias, :polymorphic => true
82
+ end
83
+
84
+ module TrModels
85
+ class Transport
86
+ include MongoMapper::EmbeddedDocument
87
+ key :license_plate, String
88
+ end
89
+
90
+ class Car < TrModels::Transport
91
+ include MongoMapper::EmbeddedDocument
92
+ key :model, String
93
+ key :year, Integer
94
+ end
95
+
96
+ class Bus < TrModels::Transport
97
+ include MongoMapper::EmbeddedDocument
98
+ key :max_passengers, Integer
99
+ end
100
+
101
+ class Ambulance < TrModels::Transport
102
+ include MongoMapper::EmbeddedDocument
103
+ key :icu, Boolean
104
+ end
105
+
106
+ class Fleet
107
+ include MongoMapper::Document
108
+ many :transports, :polymorphic => true, :class_name => "TrModels::Transport"
109
+ key :name, String
110
+
111
+ def transport_attributes=(transport_attributes)
112
+ self.transports = []
113
+ transport_attributes.each do |attributes|
114
+ self.transports << attributes["_type"].constantize.new(attributes)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ class AssociationsTest < Test::Unit::TestCase
121
+ def setup
122
+ Project.collection.clear
123
+ Status.collection.clear
124
+ Catalog.collection.clear
125
+ TrModels::Fleet.collection.clear
126
+ end
127
+
128
+ context "Nested Polymorphic Many" do
129
+ should "set associations correctly" do
130
+ fleet_attributes = {
131
+ "name" => "My Fleet",
132
+ "transport_attributes" => [
133
+ {"_type" => "TrModels::Ambulance", "license_plate" => "GGG123", "icu" => true},
134
+ {"_type" => "TrModels::Car", "license_plate" => "ABC123", "model" => "VW Golf", "year" => 2001},
135
+ {"_type" => "TrModels::Car", "license_plate" => "DEF123", "model" => "Honda Accord", "year" => 2008},
136
+ ]
137
+ }
138
+
139
+ fleet = TrModels::Fleet.new(fleet_attributes)
140
+ fleet.transports.size.should == 3
141
+ fleet.transports[0].class.should == TrModels::Ambulance
142
+ fleet.transports[0].license_plate.should == "GGG123"
143
+ fleet.transports[0].icu.should be_true
144
+ fleet.transports[1].class.should == TrModels::Car
145
+ fleet.transports[1].license_plate.should == "ABC123"
146
+ fleet.transports[1].model.should == "VW Golf"
147
+ fleet.transports[1].year.should == 2001
148
+ fleet.transports[2].class.should == TrModels::Car
149
+ fleet.transports[2].license_plate.should == "DEF123"
150
+ fleet.transports[2].model.should == "Honda Accord"
151
+ fleet.transports[2].year.should == 2008
152
+ fleet.save.should be_true
153
+
154
+ from_db = TrModels::Fleet.find(fleet.id)
155
+ from_db.transports.size.should == 3
156
+ from_db.transports[0].license_plate.should == "GGG123"
157
+ from_db.transports[0].icu.should be_true
158
+ from_db.transports[1].license_plate.should == "ABC123"
159
+ from_db.transports[1].model.should == "VW Golf"
160
+ from_db.transports[1].year.should == 2001
161
+ from_db.transports[2].license_plate.should == "DEF123"
162
+ from_db.transports[2].model.should == "Honda Accord"
163
+ from_db.transports[2].year.should == 2008
164
+ end
165
+
166
+ should "default reader to empty array" do
167
+ fleet = TrModels::Fleet.new
168
+ fleet.transports.should == []
169
+ end
170
+
171
+ should "allow adding to association like it was an array" do
172
+ fleet = TrModels::Fleet.new
173
+ fleet.transports << TrModels::Car.new
174
+ fleet.transports.push TrModels::Bus.new
175
+ fleet.transports.size.should == 2
176
+ end
177
+
178
+ should "store the association" do
179
+ fleet = TrModels::Fleet.new
180
+ fleet.transports = [TrModels::Car.new("license_plate" => "DCU2013", "model" => "Honda Civic")]
181
+ fleet.save.should be_true
182
+
183
+ from_db = TrModels::Fleet.find(fleet.id)
184
+ from_db.transports.size.should == 1
185
+ from_db.transports[0].license_plate.should == "DCU2013"
186
+ end
187
+
188
+ should "store different associations" do
189
+ fleet = TrModels::Fleet.new
190
+ fleet.transports = [
191
+ TrModels::Car.new("license_plate" => "ABC1223", "model" => "Honda Civic", "year" => 2003),
192
+ TrModels::Bus.new("license_plate" => "XYZ9090", "max_passengers" => 51),
193
+ TrModels::Ambulance.new("license_plate" => "HDD3030", "icu" => true)
194
+ ]
195
+ fleet.save.should be_true
196
+
197
+ from_db = TrModels::Fleet.find(fleet.id)
198
+ from_db.transports.size.should == 3
199
+ from_db.transports[0].license_plate.should == "ABC1223"
200
+ from_db.transports[0].model.should == "Honda Civic"
201
+ from_db.transports[0].year.should == 2003
202
+ from_db.transports[1].license_plate.should == "XYZ9090"
203
+ from_db.transports[1].max_passengers.should == 51
204
+ from_db.transports[2].license_plate.should == "HDD3030"
205
+ from_db.transports[2].icu.should == true
206
+ end
207
+ end
208
+
209
+ context "Polymorphic Many" do
210
+ should "default reader to empty array" do
211
+ catalog = Catalog.new
212
+ catalog.medias.should == []
213
+ end
214
+
215
+ should "allow adding to association like it was an array" do
216
+ catalog = Catalog.new
217
+ catalog.medias << Video.new
218
+ catalog.medias.push Video.new
219
+ catalog.medias.size.should == 2
220
+ end
221
+
222
+ should "store the association" do
223
+ catalog = Catalog.new
224
+ catalog.medias = [Video.new("file" => "video.mpg", "length" => 3600)]
225
+ catalog.save.should be_true
226
+
227
+ from_db = Catalog.find(catalog.id)
228
+ from_db.medias.size.should == 1
229
+ from_db.medias[0].file.should == "video.mpg"
230
+ end
231
+
232
+ should "store different associations" do
233
+ catalog = Catalog.new
234
+ catalog.medias = [
235
+ Video.new("file" => "video.mpg", "length" => 3600),
236
+ Music.new("file" => "music.mp3", "bitrate" => "128kbps"),
237
+ Image.new("file" => "image.png", "width" => 800, "height" => 600)
238
+ ]
239
+ catalog.save.should be_true
240
+
241
+ from_db = Catalog.find(catalog.id)
242
+ from_db.medias.size.should == 3
243
+ from_db.medias[0].file.should == "video.mpg"
244
+ from_db.medias[0].length.should == 3600
245
+ from_db.medias[1].file.should == "music.mp3"
246
+ from_db.medias[1].bitrate.should == "128kbps"
247
+ from_db.medias[2].file.should == "image.png"
248
+ from_db.medias[2].width.should == 800
249
+ from_db.medias[2].height.should == 600
250
+ end
251
+ end
252
+
253
+ context "Polymorphic Belongs To" do
254
+ should "default to nil" do
255
+ status = Status.new
256
+ status.target.should be_nil
257
+ end
258
+
259
+ should "store the association" do
260
+ status = Status.new
261
+ project = Project.new(:name => "mongomapper")
262
+ status.target = project
263
+ status.save.should be_true
264
+
265
+ from_db = Status.find(status.id)
266
+ from_db.target.should_not be_nil
267
+ from_db.target_id.should == project.id
268
+ from_db.target_type.should == "Project"
269
+ from_db.target.name.should == "mongomapper"
270
+ end
271
+
272
+ should "unset the association" do
273
+ status = Status.new
274
+ project = Project.new(:name => "mongomapper")
275
+ status.target = project
276
+ status.save.should be_true
277
+
278
+ from_db = Status.find(status.id)
279
+ from_db.target = nil
280
+ from_db.target_type.should be_nil
281
+ from_db.target_id.should be_nil
282
+ from_db.target.should be_nil
283
+ end
284
+ end
285
+
286
+ context "Belongs To" do
287
+ should "default to nil" do
288
+ status = Status.new
289
+ status.project.should be_nil
290
+ end
291
+
292
+ should "store the association" do
293
+ status = Status.new
294
+ project = Project.new(:name => "mongomapper")
295
+ status.project = project
296
+ status.save.should be_true
297
+
298
+ from_db = Status.find(status.id)
299
+ from_db.project.should_not be_nil
300
+ from_db.project.name.should == "mongomapper"
301
+ end
302
+
303
+ should "unset the association" do
304
+ status = Status.new
305
+ project = Project.new(:name => "mongomapper")
306
+ status.project = project
307
+ status.save.should be_true
308
+
309
+ from_db = Status.find(status.id)
310
+ from_db.project = nil
311
+ from_db.project.should be_nil
312
+ end
313
+ end
314
+
315
+ context "Many documents" do
316
+ should "set children documents" do
317
+ fleet_attributes = {
318
+ "name" => "My Project",
319
+ "statuses_attributes" => [
320
+ {"status" => "Ready", "license_plate" => "GGG123", "icu" => true},
321
+ {"_type" => "ACar", "license_plate" => "ABC123", "model" => "VW Golf", "year" => 2001},
322
+ {"_type" => "TrModels::Car", "license_plate" => "DEF123", "model" => "Honda Accord", "year" => 2008},
323
+ ]
324
+ }
325
+ end
326
+
327
+ should "default reader to empty array" do
328
+ project = Project.new
329
+ project.statuses.should == []
330
+ end
331
+
332
+ should "allow adding to association like it was an array" do
333
+ project = Project.new
334
+ project.statuses << Status.new
335
+ project.statuses.push Status.new
336
+ project.statuses.size.should == 2
337
+ end
338
+
339
+ should "store the association" do
340
+ project = Project.new
341
+ project.statuses = [Status.new("name" => "ready")]
342
+ project.save.should be_true
343
+
344
+ from_db = Project.find(project.id)
345
+ from_db.statuses.size.should == 1
346
+ from_db.statuses[0].name.should == "ready"
347
+ end
348
+ end
349
+
350
+ context "Many embedded documents" do
351
+ should "allow adding to association like it was an array" do
352
+ project = Project.new
353
+ project.addresses << Address.new
354
+ project.addresses.push Address.new
355
+ project.addresses.size.should == 2
356
+ end
357
+
358
+ should "be embedded in document on save" do
359
+ sb = Address.new(:city => 'South Bend', :state => 'IN')
360
+ chi = Address.new(:city => 'Chicago', :state => 'IL')
361
+ project = Project.new
362
+ project.addresses << sb
363
+ project.addresses << chi
364
+ project.save
365
+
366
+ from_db = Project.find(project.id)
367
+ from_db.addresses.size.should == 2
368
+ from_db.addresses[0].should == sb
369
+ from_db.addresses[1].should == chi
370
+ end
371
+
372
+ should "allow embedding arbitrarily deep" do
373
+ @document = Class.new do
374
+ include MongoMapper::Document
375
+ key :person, Person
376
+ end
377
+
378
+ meg = Person.new(:name => "Meg")
379
+ meg.child = Person.new(:name => "Steve")
380
+ meg.child.child = Person.new(:name => "Linda")
381
+
382
+ doc = @document.new(:person => meg)
383
+ doc.save
384
+
385
+ from_db = @document.find(doc.id)
386
+ from_db.person.name.should == 'Meg'
387
+ from_db.person.child.name.should == 'Steve'
388
+ from_db.person.child.child.name.should == 'Linda'
389
+ end
390
+
391
+ should "allow assignment of 'many' embedded documents using a hash" do
392
+ person_attributes = {
393
+ "name" => "Mr. Pet Lover",
394
+ "pets_attributes" => [
395
+ {"name" => "Jimmy", "species" => "Cocker Spainel"},
396
+ {"name" => "Sasha", "species" => "Siberian Husky"},
397
+ ]
398
+ }
399
+
400
+ pet_lover = RealPerson.new(person_attributes)
401
+ pet_lover.name.should == "Mr. Pet Lover"
402
+ pet_lover.pets[0].name.should == "Jimmy"
403
+ pet_lover.pets[0].species.should == "Cocker Spainel"
404
+ pet_lover.pets[1].name.should == "Sasha"
405
+ pet_lover.pets[1].species.should == "Siberian Husky"
406
+ pet_lover.save.should be_true
407
+
408
+ from_db = RealPerson.find(pet_lover.id)
409
+ from_db.name.should == "Mr. Pet Lover"
410
+ from_db.pets[0].name.should == "Jimmy"
411
+ from_db.pets[0].species.should == "Cocker Spainel"
412
+ from_db.pets[1].name.should == "Sasha"
413
+ from_db.pets[1].species.should == "Siberian Husky"
414
+ end
415
+
416
+ should "allow saving embedded documents in 'many' embedded documents" do
417
+ @document = Class.new do
418
+ include MongoMapper::Document
419
+
420
+ many :people
421
+ end
422
+
423
+ meg = Person.new(:name => "Meg")
424
+ sparky = Pet.new(:name => "Sparky", :species => "Dog")
425
+ koda = Pet.new(:name => "Koda", :species => "Dog")
426
+
427
+ doc = @document.new
428
+
429
+ meg.pets << sparky
430
+ meg.pets << koda
431
+
432
+ doc.people << meg
433
+ doc.save
434
+
435
+ from_db = @document.find(doc.id)
436
+ from_db.people.first.name.should == "Meg"
437
+ from_db.people.first.pets.should_not == []
438
+ from_db.people.first.pets.first.name.should == "Sparky"
439
+ from_db.people.first.pets.first.species.should == "Dog"
440
+ from_db.people.first.pets[1].name.should == "Koda"
441
+ from_db.people.first.pets[1].species.should == "Dog"
442
+ end
443
+ end
444
+ end
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+
3
+ class CallbacksTest < Test::Unit::TestCase
4
+ context "Defining and running callbacks" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+
9
+ key :name, String
10
+
11
+ [ :before_validation_on_create, :before_validation_on_update,
12
+ :before_validation, :after_validation,
13
+ :before_create, :after_create,
14
+ :before_update, :after_update,
15
+ :before_save, :after_save,
16
+ :before_destroy, :after_destroy].each do |callback|
17
+ callback_method = "#{callback}_callback"
18
+ send(callback, callback_method)
19
+ define_method(callback_method) do
20
+ history << callback.to_sym
21
+ end
22
+ end
23
+
24
+ def history
25
+ @history ||= []
26
+ end
27
+
28
+ def clear_history
29
+ @history = nil
30
+ end
31
+ end
32
+ @document.collection.clear
33
+ end
34
+
35
+ should "get the order right for creating documents" do
36
+ doc = @document.create(:name => 'John Nunemaker')
37
+ doc.history.should == [:before_validation, :before_validation_on_create, :after_validation, :before_save, :before_create, :after_create, :after_save]
38
+ end
39
+
40
+ should "get the order right for updating documents" do
41
+ doc = @document.create(:name => 'John Nunemaker')
42
+ doc.clear_history
43
+ doc.name = 'John'
44
+ doc.save
45
+ doc.history.should == [:before_validation, :before_validation_on_update, :after_validation, :before_save, :before_update, :after_update, :after_save]
46
+ end
47
+
48
+ should "work for before and after validation" do
49
+ doc = @document.new(:name => 'John Nunemaker')
50
+ doc.valid?
51
+ doc.history.should include(:before_validation)
52
+ doc.history.should include(:after_validation)
53
+ end
54
+
55
+ should "work for before and after create" do
56
+ doc = @document.create(:name => 'John Nunemaker')
57
+ doc.history.should include(:before_create)
58
+ doc.history.should include(:after_create)
59
+ end
60
+
61
+ should "work for before and after update" do
62
+ doc = @document.create(:name => 'John Nunemaker')
63
+ doc.name = 'John Doe'
64
+ doc.save
65
+ doc.history.should include(:before_update)
66
+ doc.history.should include(:after_update)
67
+ end
68
+
69
+ should "work for before and after save" do
70
+ doc = @document.new
71
+ doc.name = 'John Doe'
72
+ doc.save
73
+ doc.history.should include(:before_save)
74
+ doc.history.should include(:after_save)
75
+ end
76
+
77
+ should "work for before and after destroy" do
78
+ doc = @document.create(:name => 'John Nunemaker')
79
+ doc.destroy
80
+ doc.history.should include(:before_destroy)
81
+ doc.history.should include(:after_destroy)
82
+ end
83
+ end
84
+ end