mongo_mapper-unstable 2009.12.30 → 2010.1.4

Sign up to get free protection for your applications and to get access to all the features.
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