mongo_mapper-unstable 2009.12.30 → 2010.1.4

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 (56) hide show
  1. data/README.rdoc +2 -17
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper/associations/base.rb +19 -10
  5. data/lib/mongo_mapper/associations/in_array_proxy.rb +137 -0
  6. data/lib/mongo_mapper/associations/one_proxy.rb +64 -0
  7. data/lib/mongo_mapper/associations/proxy.rb +7 -4
  8. data/lib/mongo_mapper/associations.rb +11 -3
  9. data/lib/mongo_mapper/callbacks.rb +30 -78
  10. data/lib/mongo_mapper/dirty.rb +5 -24
  11. data/lib/mongo_mapper/document.rb +117 -144
  12. data/lib/mongo_mapper/embedded_document.rb +7 -11
  13. data/lib/mongo_mapper/finder_options.rb +13 -21
  14. data/lib/mongo_mapper/mongo_mapper.rb +125 -0
  15. data/lib/mongo_mapper/pagination.rb +12 -1
  16. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +1 -0
  17. data/lib/mongo_mapper/serialization.rb +2 -2
  18. data/lib/mongo_mapper/serializers/json_serializer.rb +2 -46
  19. data/lib/mongo_mapper/support.rb +2 -2
  20. data/lib/mongo_mapper.rb +8 -2
  21. data/mongo_mapper.gemspec +14 -8
  22. data/specs.watchr +3 -5
  23. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +8 -0
  24. data/test/functional/associations/test_belongs_to_proxy.rb +54 -9
  25. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  26. data/test/functional/associations/test_many_documents_proxy.rb +103 -53
  27. data/test/functional/associations/test_many_embedded_proxy.rb +4 -14
  28. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -1
  29. data/test/functional/associations/test_one_proxy.rb +149 -0
  30. data/test/functional/test_binary.rb +13 -4
  31. data/test/functional/test_callbacks.rb +1 -5
  32. data/test/functional/test_dirty.rb +1 -4
  33. data/test/functional/test_document.rb +576 -640
  34. data/test/functional/test_embedded_document.rb +7 -20
  35. data/test/functional/test_modifiers.rb +238 -0
  36. data/test/functional/test_pagination.rb +1 -3
  37. data/test/functional/test_string_id_compatibility.rb +3 -8
  38. data/test/functional/test_validations.rb +13 -75
  39. data/test/models.rb +1 -1
  40. data/test/support/timing.rb +1 -1
  41. data/test/test_helper.rb +28 -0
  42. data/test/unit/associations/test_base.rb +54 -13
  43. data/test/unit/associations/test_proxy.rb +12 -0
  44. data/test/unit/test_document.rb +36 -26
  45. data/test/unit/test_embedded_document.rb +14 -51
  46. data/test/unit/test_finder_options.rb +20 -7
  47. data/test/unit/test_key.rb +1 -4
  48. data/test/unit/test_pagination.rb +6 -0
  49. data/test/unit/test_rails_compatibility.rb +4 -1
  50. data/test/unit/test_serializations.rb +1 -2
  51. data/test/unit/test_support.rb +4 -0
  52. data/test/unit/test_time_zones.rb +1 -2
  53. data/test/unit/test_validations.rb +3 -14
  54. metadata +12 -6
  55. data/lib/mongo_mapper/observing.rb +0 -50
  56. data/test/unit/test_observing.rb +0 -101
@@ -22,10 +22,10 @@ class ManyDocumentsProxyTest < Test::Unit::TestCase
22
22
 
23
23
  should "be able to replace the association" do
24
24
  project = Project.new
25
- project.statuses = [Status.new("name" => "ready")]
25
+ project.statuses = [Status.new(:name => "ready")]
26
26
  project.save.should be_true
27
27
 
28
- project = project.reload
28
+ project.reload
29
29
  project.statuses.size.should == 1
30
30
  project.statuses[0].name.should == "ready"
31
31
  end
@@ -36,7 +36,7 @@ class ManyDocumentsProxyTest < Test::Unit::TestCase
36
36
  project.statuses.push Status.new(:name => 'push')
37
37
  project.statuses.concat Status.new(:name => 'concat')
38
38
 
39
- project = project.reload
39
+ project.reload
40
40
  project.statuses[0].project_id.should == project.id
41
41
  project.statuses[1].project_id.should == project.id
42
42
  project.statuses[2].project_id.should == project.id
@@ -235,84 +235,44 @@ class ManyDocumentsProxyTest < Test::Unit::TestCase
235
235
  lambda {
236
236
  status = @project1.statuses.find_or_create_by_name('Delivered')
237
237
  status.project.should == @project1
238
- }.should change { Status.count }.by(1)
238
+ }.should change { Status.count }
239
239
  end
240
240
  end
241
241
  end
242
242
 
243
- context "with :all" do
243
+ context "all" do
244
244
  should "work" do
245
245
  @project1.statuses.find(:all, :order => "position asc").should == [@brand_new, @complete]
246
- end
247
-
248
- should "work with conditions" do
249
- statuses = @project1.statuses.find(:all, :name => 'Complete')
250
- statuses.should == [@complete]
251
- end
252
-
253
- should "work with order" do
254
- statuses = @project1.statuses.find(:all, :order => 'name asc')
255
- statuses.should == [@complete, @brand_new]
256
- end
257
- end
258
-
259
- context "with #all" do
260
- should "work" do
261
246
  @project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
262
247
  end
263
248
 
264
249
  should "work with conditions" do
265
- statuses = @project1.statuses.all(:name => 'Complete')
266
- statuses.should == [@complete]
267
- end
268
-
269
- should "work with order" do
270
- statuses = @project1.statuses.all(:order => 'name asc')
271
- statuses.should == [@complete, @brand_new]
250
+ @project1.statuses.find(:all, :name => 'Complete').should == [@complete]
251
+ @project1.statuses.all(:name => 'Complete').should == [@complete]
272
252
  end
273
253
  end
274
254
 
275
- context "with :first" do
255
+ context "first" do
276
256
  should "work" do
277
257
  @project1.statuses.find(:first, :order => 'name').should == @complete
278
- end
279
-
280
- should "work with conditions" do
281
- status = @project1.statuses.find(:first, :name => 'Complete')
282
- status.should == @complete
283
- end
284
- end
285
-
286
- context "with #first" do
287
- should "work" do
288
258
  @project1.statuses.first(:order => 'name').should == @complete
289
259
  end
290
260
 
291
261
  should "work with conditions" do
292
- status = @project1.statuses.first(:name => 'Complete')
293
- status.should == @complete
262
+ @project1.statuses.find(:first, :name => 'Complete').should == @complete
263
+ @project1.statuses.first(:name => 'Complete').should == @complete
294
264
  end
295
265
  end
296
266
 
297
- context "with :last" do
267
+ context "last" do
298
268
  should "work" do
299
269
  @project1.statuses.find(:last, :order => "position asc").should == @complete
300
- end
301
-
302
- should "work with conditions" do
303
- status = @project1.statuses.find(:last, :order => 'position', :name => 'New')
304
- status.should == @brand_new
305
- end
306
- end
307
-
308
- context "with #last" do
309
- should "work" do
310
270
  @project1.statuses.last(:order => "position asc").should == @complete
311
271
  end
312
272
 
313
273
  should "work with conditions" do
314
- status = @project1.statuses.last(:order => 'position', :name => 'New')
315
- status.should == @brand_new
274
+ @project1.statuses.find(:last, :order => 'position', :name => 'New').should == @brand_new
275
+ @project1.statuses.last(:order => 'position', :name => 'New').should == @brand_new
316
276
  end
317
277
  end
318
278
 
@@ -384,4 +344,94 @@ class ManyDocumentsProxyTest < Test::Unit::TestCase
384
344
  project.collaborators.top.should == collaborator1
385
345
  end
386
346
  end
347
+
348
+ context ":dependent" do
349
+ setup do
350
+ # FIXME: make use of already defined models
351
+ class ::Property
352
+ include MongoMapper::Document
353
+ end
354
+ Property.collection.remove
355
+
356
+ class ::Thing
357
+ include MongoMapper::Document
358
+ key :name, String
359
+ end
360
+ Thing.collection.remove
361
+ end
362
+
363
+ teardown do
364
+ Object.send :remove_const, 'Property' if defined?(::Property)
365
+ Object.send :remove_const, 'Thing' if defined?(::Thing)
366
+ end
367
+
368
+ context "=> destroy" do
369
+ setup do
370
+ Property.key :thing_id, ObjectId
371
+ Property.belongs_to :thing, :dependent => :destroy
372
+ Thing.many :properties, :dependent => :destroy
373
+
374
+ @thing = Thing.create(:name => "Tree")
375
+ @property1 = Property.create
376
+ @property2 = Property.create
377
+ @property3 = Property.create
378
+ @thing.properties << @property1
379
+ @thing.properties << @property2
380
+ @thing.properties << @property3
381
+ end
382
+
383
+ should "should destroy the associated documents" do
384
+ @thing.properties.count.should == 3
385
+ @thing.destroy
386
+ @thing.properties.count.should == 0
387
+ Property.count.should == 0
388
+ end
389
+ end
390
+
391
+ context "=> delete_all" do
392
+ setup do
393
+ Property.key :thing_id, ObjectId
394
+ Property.belongs_to :thing
395
+ Thing.has_many :properties, :dependent => :delete_all
396
+
397
+ @thing = Thing.create(:name => "Tree")
398
+ @property1 = Property.create
399
+ @property2 = Property.create
400
+ @property3 = Property.create
401
+ @thing.properties << @property1
402
+ @thing.properties << @property2
403
+ @thing.properties << @property3
404
+ end
405
+
406
+ should "should delete associated documents" do
407
+ @thing.properties.count.should == 3
408
+ @thing.destroy
409
+ @thing.properties.count.should == 0
410
+ Property.count.should == 0
411
+ end
412
+ end
413
+
414
+ context "=> nullify" do
415
+ setup do
416
+ Property.key :thing_id, ObjectId
417
+ Property.belongs_to :thing
418
+ Thing.has_many :properties, :dependent => :nullify
419
+
420
+ @thing = Thing.create(:name => "Tree")
421
+ @property1 = Property.create
422
+ @property2 = Property.create
423
+ @property3 = Property.create
424
+ @thing.properties << @property1
425
+ @thing.properties << @property2
426
+ @thing.properties << @property3
427
+ end
428
+
429
+ should "should nullify relationship but not destroy associated documents" do
430
+ @thing.properties.count.should == 3
431
+ @thing.destroy
432
+ @thing.properties.count.should == 0
433
+ Property.count.should == 3
434
+ end
435
+ end
436
+ end
387
437
  end
@@ -33,12 +33,9 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
33
33
  end
34
34
 
35
35
  should "allow embedding arbitrarily deep" do
36
- @document = Class.new do
37
- include MongoMapper::Document
38
- set_collection_name 'test'
36
+ @document = Doc do
39
37
  key :person, Person
40
38
  end
41
- @document.collection.remove
42
39
 
43
40
  meg = Person.new(:name => "Meg")
44
41
  meg.child = Person.new(:name => "Steve")
@@ -80,12 +77,9 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
80
77
 
81
78
  context "embedding many embedded documents" do
82
79
  setup do
83
- @document = Class.new do
84
- include MongoMapper::Document
85
- set_collection_name 'test'
80
+ @document = Doc do
86
81
  many :people
87
82
  end
88
- @document.collection.remove
89
83
  end
90
84
 
91
85
  should "persist all embedded documents" do
@@ -142,20 +136,16 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
142
136
 
143
137
  context "extending the association" do
144
138
  setup do
145
- @address_class = Class.new do
146
- include MongoMapper::EmbeddedDocument
139
+ @address_class = EDoc do
147
140
  key :address, String
148
141
  key :city, String
149
142
  key :state, String
150
143
  key :zip, Integer
151
144
  end
152
145
 
153
- @project_class = Class.new do
154
- include MongoMapper::Document
146
+ @project_class = Doc do
155
147
  key :name, String
156
148
  end
157
-
158
- @project_class.collection.remove
159
149
  end
160
150
 
161
151
  should "work using a block passed to many" do
@@ -326,10 +326,11 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
326
326
  end
327
327
 
328
328
  should "work using many's :extend option" do
329
+
329
330
  room = Room.new(:name => "Amazing Room")
330
331
  accounts = room.accounts = [
331
332
  Bot.new(:last_logged_in => 3.weeks.ago),
332
- User.new(:last_logged_in => nil),
333
+ AccountUser.new(:last_logged_in => nil),
333
334
  Bot.new(:last_logged_in => 1.week.ago)
334
335
  ]
335
336
  room.save
@@ -0,0 +1,149 @@
1
+ require 'test_helper'
2
+
3
+ class OneProxyTest < Test::Unit::TestCase
4
+ def setup
5
+ @post_class = Doc('Post')
6
+ @author_class = Doc do
7
+ key :post_id, ObjectId
8
+ end
9
+ end
10
+
11
+ should "default to nil" do
12
+ @post_class.one :author, :class => @author_class
13
+ @post_class.new.author.nil?.should be_true
14
+ end
15
+
16
+ should "be able to replace the association" do
17
+ @post_class.one :author, :class => @author_class
18
+
19
+ post = @post_class.new
20
+ author = @author_class.new(:name => 'Frank')
21
+ post.author = author
22
+ post.reload
23
+
24
+ post.author.should == author
25
+ post.author.nil?.should be_false
26
+
27
+ new_author = @author_class.new(:name => 'Emily')
28
+ post.author = new_author
29
+ post.author.should == new_author
30
+ end
31
+
32
+ should "have boolean method for testing presence" do
33
+ @post_class.one :author, :class => @author_class
34
+
35
+ post = @post_class.new
36
+ post.author?.should be_false
37
+
38
+ post.author = @author_class.new(:name => 'Frank')
39
+ post.author?.should be_true
40
+ end
41
+
42
+ should "work with criteria" do
43
+ @post_class.one :primary_author, :class => @author_class, :primary => true
44
+ @post_class.one :author, :class => @author_class
45
+
46
+ post = @post_class.create
47
+ author = @author_class.create(:name => 'Frank', :primary => false, :post_id => post.id)
48
+ primary = @author_class.create(:name => 'Bill', :primary => true, :post_id => post.id)
49
+ post.author.should == author
50
+ post.primary_author.should == primary
51
+ end
52
+
53
+ should "unset the association" do
54
+ @post_class.one :author, :class => @author_class
55
+ post = @post_class.new
56
+ author = @author_class.new
57
+ post.author = author
58
+ post.reload
59
+
60
+ post.author = nil
61
+ post.author.nil?.should be_false
62
+ end
63
+
64
+ should "work with :dependent delete" do
65
+ @post_class.one :author, :class => @author_class, :dependent => :delete
66
+
67
+ post = @post_class.create
68
+ author = @author_class.new
69
+ post.author = author
70
+ post.reload
71
+
72
+ @author_class.any_instance.expects(:delete).once
73
+ post.author = @author_class.new
74
+ end
75
+
76
+ should "work with :dependent destroy" do
77
+ @post_class.one :author, :class => @author_class, :dependent => :destroy
78
+
79
+ post = @post_class.create
80
+ author = @author_class.new
81
+ post.author = author
82
+ post.reload
83
+
84
+ @author_class.any_instance.expects(:destroy).once
85
+ post.author = @author_class.new
86
+ end
87
+
88
+ should "work with :dependent nullify" do
89
+ @post_class.one :author, :class => @author_class, :dependent => :nullify
90
+
91
+ post = @post_class.create
92
+ author = @author_class.new
93
+ post.author = author
94
+ post.reload
95
+
96
+ post.author = @author_class.new
97
+
98
+ author.reload
99
+ author.post_id.should be_nil
100
+ end
101
+
102
+ should "be able to build" do
103
+ @post_class.one :author, :class => @author_class
104
+
105
+ post = @post_class.create
106
+ author = post.author.build(:name => 'John')
107
+ post.author.should be_instance_of(@author_class)
108
+ post.author.should be_new
109
+ post.author.name.should == 'John'
110
+ post.author.should == author
111
+ post.author.post_id.should == post.id
112
+ end
113
+
114
+ should "be able to create" do
115
+ @post_class.one :author, :class => @author_class
116
+
117
+ post = @post_class.create
118
+ author = post.author.create(:name => 'John')
119
+ post.author.should be_instance_of(@author_class)
120
+ post.author.should_not be_new
121
+ post.author.name.should == 'John'
122
+ post.author.should == author
123
+ post.author.post_id.should == post.id
124
+ end
125
+
126
+ context "#create!" do
127
+ setup do
128
+ @author_class.key :name, String, :required => true
129
+ @post_class.one :author, :class => @author_class
130
+ end
131
+
132
+ should "raise exception if invalid" do
133
+ post = @post_class.create
134
+ assert_raises(MongoMapper::DocumentNotValid) do
135
+ post.author.create!
136
+ end
137
+ end
138
+
139
+ should "work if valid" do
140
+ post = @post_class.create
141
+ author = post.author.create!(:name => 'John')
142
+ post.author.should be_instance_of(@author_class)
143
+ post.author.should_not be_new
144
+ post.author.name.should == 'John'
145
+ post.author.should == author
146
+ post.author.post_id.should == post.id
147
+ end
148
+ end
149
+ end
@@ -2,12 +2,9 @@ require 'test_helper'
2
2
 
3
3
  class BinaryTest < Test::Unit::TestCase
4
4
  should "serialize and deserialize correctly" do
5
- klass = Class.new do
6
- include MongoMapper::Document
7
- set_collection_name 'test'
5
+ klass = Doc do
8
6
  key :contents, Binary
9
7
  end
10
- klass.collection.remove
11
8
 
12
9
  doc = klass.new(:contents => '010101')
13
10
  doc.save
@@ -15,4 +12,16 @@ class BinaryTest < Test::Unit::TestCase
15
12
  doc = doc.reload
16
13
  doc.contents.to_s.should == ByteBuffer.new('010101').to_s
17
14
  end
15
+
16
+ context "Saving a document with a blank binary value" do
17
+ setup do
18
+ @document = Doc do
19
+ key :file, Binary
20
+ end
21
+ end
22
+
23
+ should "not fail" do
24
+ assert_nothing_raised { @document.new(:file => nil).save }
25
+ end
26
+ end
18
27
  end
@@ -3,10 +3,7 @@ require 'test_helper'
3
3
  class CallbacksTest < Test::Unit::TestCase
4
4
  context "Defining and running callbacks" do
5
5
  setup do
6
- @document = Class.new do
7
- include MongoMapper::Document
8
- set_collection_name 'test'
9
-
6
+ @document = Doc do
10
7
  key :name, String
11
8
 
12
9
  [ :before_validation_on_create, :before_validation_on_update,
@@ -30,7 +27,6 @@ class CallbacksTest < Test::Unit::TestCase
30
27
  @history = nil
31
28
  end
32
29
  end
33
- @document.collection.remove
34
30
  end
35
31
 
36
32
  should "get the order right for creating documents" do
@@ -3,12 +3,9 @@ require 'models'
3
3
 
4
4
  class DirtyTest < Test::Unit::TestCase
5
5
  def setup
6
- @document = Class.new do
7
- include MongoMapper::Document
8
- set_collection_name 'test'
6
+ @document = Doc do
9
7
  key :phrase, String
10
8
  end
11
- @document.collection.remove
12
9
 
13
10
  Status.collection.remove
14
11
  Project.collection.remove