mongo_mapper 0.5.8 → 0.6.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 (47) hide show
  1. data/Rakefile +4 -4
  2. data/VERSION +1 -1
  3. data/bin/mmconsole +10 -5
  4. data/lib/mongo_mapper.rb +28 -5
  5. data/lib/mongo_mapper/associations.rb +113 -12
  6. data/lib/mongo_mapper/associations/base.rb +24 -9
  7. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +1 -1
  8. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +1 -1
  9. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +2 -2
  10. data/lib/mongo_mapper/associations/many_documents_proxy.rb +7 -2
  11. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +22 -36
  12. data/lib/mongo_mapper/associations/proxy.rb +11 -6
  13. data/lib/mongo_mapper/document.rb +37 -21
  14. data/lib/mongo_mapper/embedded_document.rb +32 -18
  15. data/lib/mongo_mapper/finder_options.rb +19 -12
  16. data/lib/mongo_mapper/rails_compatibility/document.rb +4 -0
  17. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +4 -0
  18. data/lib/mongo_mapper/support.rb +18 -46
  19. data/lib/mongo_mapper/types.rb +64 -0
  20. data/lib/mongo_mapper/validations.rb +13 -43
  21. data/mongo_mapper.gemspec +13 -10
  22. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +10 -10
  23. data/test/functional/associations/test_belongs_to_proxy.rb +29 -30
  24. data/test/functional/associations/test_many_documents_as_proxy.rb +13 -12
  25. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +34 -34
  26. data/test/functional/associations/test_many_embedded_proxy.rb +69 -74
  27. data/test/functional/associations/test_many_polymorphic_proxy.rb +10 -10
  28. data/test/functional/associations/test_many_proxy.rb +14 -15
  29. data/test/functional/test_associations.rb +4 -4
  30. data/test/functional/test_binary.rb +1 -1
  31. data/test/functional/test_dirty.rb +6 -6
  32. data/test/functional/test_document.rb +76 -69
  33. data/test/functional/test_embedded_document.rb +15 -14
  34. data/test/functional/test_pagination.rb +9 -1
  35. data/test/functional/test_string_id_compatibility.rb +72 -0
  36. data/test/functional/test_validations.rb +56 -7
  37. data/test/models.rb +7 -7
  38. data/test/test_helper.rb +2 -5
  39. data/test/unit/test_association_base.rb +6 -1
  40. data/test/unit/test_document.rb +22 -13
  41. data/test/unit/test_embedded_document.rb +47 -5
  42. data/test/unit/test_finder_options.rb +22 -3
  43. data/test/unit/test_mongo_mapper.rb +65 -0
  44. data/test/unit/test_rails_compatibility.rb +14 -0
  45. data/test/unit/test_support.rb +45 -0
  46. metadata +9 -6
  47. data/test/unit/test_mongomapper.rb +0 -28
@@ -28,9 +28,9 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
28
28
  @doc.foo.city.should == 'South Bend'
29
29
  @doc.foo.state.should == 'IN'
30
30
 
31
- from_db = @document.find(@doc.id)
32
- from_db.foo.city.should == 'South Bend'
33
- from_db.foo.state.should == 'IN'
31
+ doc = @doc.reload
32
+ doc.foo.city.should == 'South Bend'
33
+ doc.foo.state.should == 'IN'
34
34
  end
35
35
  end
36
36
 
@@ -58,8 +58,9 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
58
58
  address = Address.new(:city => 'South Bend', :state => 'IN')
59
59
  doc = @document.new(:foo => address)
60
60
  doc.save
61
- read_doc = @document.find(doc.id)
62
- read_doc.foo.new?.should == false
61
+
62
+ doc = doc.reload
63
+ doc.foo.new?.should == false
63
64
  end
64
65
  end
65
66
 
@@ -71,15 +72,16 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
71
72
  person.pets << pet
72
73
  pet.save
73
74
 
74
- doc = RealPerson.find(person.id)
75
- doc.pets.first.should == pet
75
+ person = person.reload
76
+ person.pets.first.should == pet
76
77
  end
77
78
 
78
79
  should "save new keys" do
79
80
  person = RealPerson.new
80
81
  person[:new_attribute] = 'foobar'
81
82
  person.save
82
- from_db = RealPerson.find(person.id)
83
+
84
+ person = person.reload
83
85
  person.new_attribute.should == 'foobar'
84
86
  end
85
87
  end
@@ -92,14 +94,13 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
92
94
  person.pets << pet
93
95
  pet.save
94
96
 
95
- doc = RealPerson.find(person.id)
96
- pet = doc.pets.first
97
+ person = person.reload
98
+ pet = person.pets.first
97
99
  pet.update_attributes :name => 'koda'
98
100
 
99
- doc = RealPerson.find(person.id)
100
- embedded = doc.pets.first
101
- embedded.id.should == pet.id
102
- embedded.name.should == 'koda'
101
+ person = person.reload
102
+ person.pets.first._id.should == pet._id
103
+ person.pets.first.name.should == 'koda'
103
104
  end
104
105
  end
105
106
  end
@@ -50,8 +50,16 @@ class PaginationTest < Test::Unit::TestCase
50
50
  })
51
51
  result.should == [@doc1, @doc3]
52
52
  result.first.age.should == 27
53
+
54
+ result = @document.paginate({
55
+ :conditions => {:last_name => 'Nunemaker'},
56
+ :order => "age DESC",
57
+ :per_page => 2,
58
+ :page => 1} )
59
+ result.should == [@doc1, @doc3]
60
+ result.first.age.should == 27
53
61
  end
54
-
62
+
55
63
  should "withstand rigor" do
56
64
  result = @document.paginate({
57
65
  :per_page => 1,
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+
3
+ class StringIdCompatibilityTest < Test::Unit::TestCase
4
+ def setup
5
+ @note_class = Class.new do
6
+ include MongoMapper::EmbeddedDocument
7
+ key :_id, String
8
+ end
9
+
10
+ @task_class = Class.new do
11
+ include MongoMapper::Document
12
+ key :_id, String
13
+ key :project_id, String
14
+ belongs_to :project
15
+ end
16
+
17
+ @project_class = Class.new do
18
+ include MongoMapper::Document
19
+ key :_id, String
20
+ end
21
+
22
+ @task_class.belongs_to :project, :class => @project_class
23
+ @project_class.many :notes, :class => @note_class
24
+ @project_class.many :tasks, :class => @task_class, :foreign_key => 'project_id'
25
+
26
+ @project_class.collection.remove
27
+ @task_class.collection.remove
28
+ end
29
+
30
+ should "assign correct _id for documents" do
31
+ project = @project_class.create
32
+ project._id.should == project.id
33
+ project._id.should be_instance_of(String)
34
+ project.id.size.should == 24
35
+ lambda {
36
+ Mongo::ObjectID.from_string(project.id)
37
+ }.should_not raise_error
38
+ end
39
+
40
+ should "assign correct _id for embedded documents" do
41
+ note = @note_class.new
42
+ note.id.should == note._id
43
+ note.id.size.should == 24
44
+ end
45
+
46
+ should "find records" do
47
+ project = @project_class.create
48
+ @project_class.find(project.id).should == project
49
+ end
50
+
51
+ should "save embedded docs" do
52
+ n1 = @note_class.new
53
+ n2 = @note_class.new
54
+ n3 = @note_class.new
55
+ project = @project_class.create(:notes => [n1, n2, n3])
56
+
57
+ project = project.reload
58
+ project.notes.size.should == 3
59
+ project.notes.should == [n1, n2, n3]
60
+ end
61
+
62
+ should "be able to associate records" do
63
+ t1 = @task_class.new(:body => 'First task')
64
+ t2 = @task_class.new(:body => 'Second task')
65
+ t3 = @task_class.new(:body => 'Third task')
66
+ project = @project_class.create(:name => 'MM', :tasks => [t1, t2, t3])
67
+
68
+ project = project.reload
69
+ project.tasks.count.should == 3
70
+ project.tasks.should == [t1, t2, t3]
71
+ end
72
+ end
@@ -24,7 +24,24 @@ class ValidationsTest < Test::Unit::TestCase
24
24
  doc.errors.full_messages.should == ["Name can't be empty"]
25
25
  end
26
26
  end
27
-
27
+
28
+ context "Skipping validations when saving" do
29
+ setup do
30
+ @document = Class.new do
31
+ include MongoMapper::Document
32
+ set_collection_name 'test'
33
+ key :name, String, :required => true
34
+ end
35
+ @document.collection.remove
36
+ end
37
+
38
+ should "insert document" do
39
+ doc = @document.new
40
+ doc.save(false)
41
+ @document.count.should == 1
42
+ end
43
+ end
44
+
28
45
  context "Saving a document that is invalid (destructive)" do
29
46
  setup do
30
47
  @document = Class.new do
@@ -76,7 +93,7 @@ class ValidationsTest < Test::Unit::TestCase
76
93
  should "not update document" do
77
94
  @doc.name = nil
78
95
  @doc.save
79
- @document.find(@doc.id).name.should == 'John Nunemaker'
96
+ @doc.reload.name.should == 'John Nunemaker'
80
97
  end
81
98
 
82
99
  should "populate document's errors" do
@@ -212,7 +229,39 @@ class ValidationsTest < Test::Unit::TestCase
212
229
  doc2 = document.new("name" => "")
213
230
  doc2.should_not have_error_on(:name)
214
231
  end
215
-
232
+
233
+ should "allow entries that differ only in case by default" do
234
+ document = Class.new do
235
+ include MongoMapper::Document
236
+ set_collection_name 'test'
237
+
238
+ key :name
239
+ validates_uniqueness_of :name
240
+ end
241
+
242
+ doc = document.new("name" => "BLAMMO")
243
+ doc.save.should be_true
244
+
245
+ doc2 = document.new("name" => "blammo")
246
+ doc2.should_not have_error_on(:name)
247
+ end
248
+
249
+ should "fail on entries that differ only in case if :case_sensitive => false" do
250
+ document = Class.new do
251
+ include MongoMapper::Document
252
+ set_collection_name 'test'
253
+
254
+ key :name
255
+ validates_uniqueness_of :name, :case_sensitive => false
256
+ end
257
+
258
+ doc = document.new("name" => "BLAMMO")
259
+ doc.save.should be_true
260
+
261
+ doc2 = document.new("name" => "blammo")
262
+ doc2.should have_error_on(:name)
263
+ end
264
+
216
265
  context "scoped by a single attribute" do
217
266
  setup do
218
267
  @document = Class.new do
@@ -225,7 +274,7 @@ class ValidationsTest < Test::Unit::TestCase
225
274
  end
226
275
  @document.collection.remove
227
276
  end
228
-
277
+
229
278
  should "fail if the same name exists in the scope" do
230
279
  doc = @document.new("name" => "joe", "scope" => "one")
231
280
  doc.save.should be_true
@@ -238,11 +287,11 @@ class ValidationsTest < Test::Unit::TestCase
238
287
  doc2 = @document.new("name" => "joe", "scope" => "one")
239
288
  doc2.should have_error_on(:name)
240
289
  end
241
-
290
+
242
291
  should "pass if the same name exists in a different scope" do
243
292
  doc = @document.new("name" => "joe", "scope" => "one")
244
293
  doc.save.should be_true
245
-
294
+
246
295
  @document \
247
296
  .stubs(:first) \
248
297
  .with(:name => 'joe', :scope => 'two') \
@@ -252,7 +301,7 @@ class ValidationsTest < Test::Unit::TestCase
252
301
  doc2.should_not have_error_on(:name)
253
302
  end
254
303
  end
255
-
304
+
256
305
  context "scoped by a multiple attributes" do
257
306
  setup do
258
307
  @document = Class.new do
@@ -40,7 +40,7 @@ class PostComment
40
40
  key :username, String, :default => 'Anonymous'
41
41
  key :body, String
42
42
 
43
- key :commentable_id, String
43
+ key :commentable_id, ObjectId
44
44
  key :commentable_type, String
45
45
  belongs_to :commentable, :polymorphic => true
46
46
 
@@ -62,7 +62,7 @@ class Message
62
62
  key :body, String
63
63
  key :position, Integer
64
64
  key :_type, String
65
- key :room_id, String
65
+ key :room_id, ObjectId
66
66
 
67
67
  belongs_to :room
68
68
  end
@@ -81,7 +81,7 @@ class Account
81
81
  include MongoMapper::Document
82
82
 
83
83
  key :_type, String
84
- key :room_id, String
84
+ key :room_id, ObjectId
85
85
  key :last_logged_in, Time
86
86
 
87
87
  belongs_to :room
@@ -145,7 +145,7 @@ end
145
145
 
146
146
  class Collaborator
147
147
  include MongoMapper::Document
148
- key :project_id, String
148
+ key :project_id, ObjectId
149
149
  key :name, String
150
150
  belongs_to :project
151
151
  end
@@ -153,8 +153,8 @@ end
153
153
  class Status
154
154
  include MongoMapper::Document
155
155
 
156
- key :project_id, String
157
- key :target_id, String
156
+ key :project_id, ObjectId
157
+ key :target_id, ObjectId
158
158
  key :target_type, String
159
159
  key :name, String, :required => true
160
160
  key :position, Integer
@@ -166,7 +166,7 @@ end
166
166
  class RealPerson
167
167
  include MongoMapper::Document
168
168
 
169
- key :room_id, String
169
+ key :room_id, ObjectId
170
170
  key :name, String
171
171
 
172
172
  belongs_to :room
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../lib/mongo_mapper')
3
3
  gem 'jnunemaker-matchy', '0.4.0'
4
4
  gem 'shoulda', '2.10.2'
5
5
  gem 'timecop', '0.3.1'
6
- gem 'mocha', '0.9.4'
6
+ gem 'mocha', '0.9.8'
7
7
 
8
8
  require 'matchy'
9
9
  require 'shoulda'
@@ -18,13 +18,10 @@ class Test::Unit::TestCase
18
18
  include CustomMatchers
19
19
  end
20
20
 
21
- DefaultDatabase = 'test' unless defined?(DefaultDatabase)
22
- AlternateDatabase = 'test2' unless defined?(AlternateDatabase)
23
-
24
21
  test_dir = File.expand_path(File.dirname(__FILE__) + '/../tmp')
25
22
  FileUtils.mkdir_p(test_dir) unless File.exist?(test_dir)
26
23
 
27
24
  MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {
28
25
  :logger => Logger.new(test_dir + '/test.log')
29
26
  })
30
- MongoMapper.database = DefaultDatabase
27
+ MongoMapper.database = 'test'
@@ -33,9 +33,14 @@ class AssociationBaseTest < Test::Unit::TestCase
33
33
  end
34
34
 
35
35
  context "klass" do
36
- should "be class_name constantized" do
36
+ should "default to class_name constantized" do
37
37
  Base.new(:belongs_to, :foo_monster).klass.should == FooMonster
38
38
  end
39
+
40
+ should "be the specified class" do
41
+ anonnymous_class = Class.new
42
+ Base.new(:belongs_to, :foo_monster, :class => anonnymous_class).klass.should == anonnymous_class
43
+ end
39
44
  end
40
45
 
41
46
  context "many?" do
@@ -36,8 +36,9 @@ class DocumentTest < Test::Unit::TestCase
36
36
  end
37
37
 
38
38
  should "allow setting a different database without affecting the default" do
39
- @document.database AlternateDatabase
40
- @document.database.name.should == AlternateDatabase
39
+ @document.set_database_name 'test2'
40
+ @document.database_name.should == 'test2'
41
+ @document.database.name.should == 'test2'
41
42
 
42
43
  another_document = Class.new do
43
44
  include MongoMapper::Document
@@ -113,7 +114,7 @@ class DocumentTest < Test::Unit::TestCase
113
114
 
114
115
  should "have access to the class's collection" do
115
116
  doc = @document.new
116
- doc.collection.should == @document.collection
117
+ doc.collection.name.should == @document.collection.name
117
118
  end
118
119
 
119
120
  should "use default values if defined for keys" do
@@ -135,13 +136,18 @@ class DocumentTest < Test::Unit::TestCase
135
136
  @document.new._root_document.should be_nil
136
137
  end
137
138
 
138
- should "set self to the root document on embedded documents" do
139
- document = Class.new(RealPerson) do
140
- many :pets
139
+ should "set self to the root document on embedded documents" do
140
+ klass = Class.new do
141
+ include MongoMapper::Document
141
142
  end
142
-
143
- doc = document.new 'pets' => [{}]
144
- doc.pets.first._root_document.should == doc
143
+
144
+ pets = Class.new do
145
+ include MongoMapper::EmbeddedDocument
146
+ end
147
+ klass.many :pets, :class => pets
148
+
149
+ doc = klass.new(:pets => [{}])
150
+ doc.pets.first._root_document.should == doc
145
151
  end
146
152
  end
147
153
 
@@ -151,6 +157,7 @@ class DocumentTest < Test::Unit::TestCase
151
157
  end
152
158
 
153
159
  should "be true if id but using custom id and not saved yet" do
160
+ @document.key :_id, String
154
161
  doc = @document.new
155
162
  doc.id = '1234'
156
163
  doc.new?.should be_true
@@ -172,14 +179,16 @@ class DocumentTest < Test::Unit::TestCase
172
179
  end
173
180
  end
174
181
 
175
-
176
182
  context "equality" do
183
+ setup do
184
+ @oid = Mongo::ObjectID.new
185
+ end
177
186
  should "be equal if id and class are the same" do
178
- (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
187
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be(true)
179
188
  end
180
189
 
181
190
  should "not be equal if class same but id different" do
182
- (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
191
+ (@document.new('_id' => @oid) == @document.new('_id' => Mongo::ObjectID.new)).should be(false)
183
192
  end
184
193
 
185
194
  should "not be equal if id same but class different" do
@@ -188,7 +197,7 @@ class DocumentTest < Test::Unit::TestCase
188
197
  set_collection_name 'test'
189
198
  end
190
199
 
191
- (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
200
+ (@document.new('_id' => @oid) == @another_document.new('_id' => @oid)).should be(false)
192
201
  end
193
202
  end
194
203
  end # instance of a document
@@ -49,6 +49,15 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
49
49
  @klass.keys['_id'].should_not be_nil
50
50
  end
51
51
 
52
+ should "know it is using object id" do
53
+ @klass.using_object_id?.should be_true
54
+ end
55
+
56
+ should "know it is not using object id if _id type is changed" do
57
+ @klass.key :_id, String
58
+ @klass.using_object_id?.should be_false
59
+ end
60
+
52
61
  context "#to_mongo" do
53
62
  should "be nil if nil" do
54
63
  @klass.to_mongo(nil).should be_nil
@@ -80,6 +89,15 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
80
89
  end
81
90
  end
82
91
 
92
+ context "looking up type constants" do
93
+ should "not raise an error" do
94
+ klass = Class.new do
95
+ include MongoMapper::EmbeddedDocument
96
+ key :file, Binary
97
+ end
98
+ end
99
+ end
100
+
83
101
  context "parent_model" do
84
102
  should "be nil if none of parents ancestors include EmbeddedDocument" do
85
103
  parent = Class.new
@@ -150,11 +168,11 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
150
168
  @document.keys['age'].type.should == Integer
151
169
  end
152
170
 
153
- should "not be redefinable" do
171
+ should "be redefinable" do
154
172
  @document.key(:foo, String)
155
173
  @document.keys['foo'].type.should == String
156
174
  @document.key(:foo, Integer)
157
- @document.keys['foo'].type.should == String
175
+ @document.keys['foo'].type.should == Integer
158
176
  end
159
177
 
160
178
  should "create reader method" do
@@ -292,6 +310,7 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
292
310
  should "have to_param that is id" do
293
311
  doc = @document.new
294
312
  doc.to_param.should == doc.id
313
+ doc.to_param.should be_instance_of(String)
295
314
  end
296
315
 
297
316
  should "have access to class logger" do
@@ -308,6 +327,24 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
308
327
  doc = @document.new
309
328
  doc.id.should == doc._id.to_s
310
329
  end
330
+
331
+ context "assigning id with _id ObjectId type" do
332
+ should "not set custom id flag" do
333
+ doc = @document.new
334
+ doc.using_custom_id?.should be_false
335
+ doc.id = Mongo::ObjectID.new
336
+ doc.using_custom_id?.should be_false
337
+ end
338
+
339
+ should "convert string object id to mongo object id" do
340
+ id = Mongo::ObjectID.new
341
+ doc = @document.new
342
+ doc.id = id.to_s
343
+ doc._id.should == id
344
+ doc.id.should == id.to_s
345
+ doc.using_custom_id?.should be_false
346
+ end
347
+ end
311
348
 
312
349
  should "have a nil _root_document" do
313
350
  @document.new._root_document.should be_nil
@@ -315,11 +352,13 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
315
352
 
316
353
  context "setting custom id" do
317
354
  should "set _id" do
355
+ @document.key :_id, String
318
356
  doc = @document.new(:id => '1234')
319
357
  doc._id.should == '1234'
320
358
  end
321
359
 
322
360
  should "know that custom id is set" do
361
+ @document.key :_id, String
323
362
  doc = @document.new
324
363
  doc.using_custom_id?.should be_false
325
364
  doc.id = '1234'
@@ -656,12 +695,15 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
656
695
  end
657
696
 
658
697
  context "equality" do
698
+ setup do
699
+ @oid = Mongo::ObjectID.new
700
+ end
659
701
  should "be equal if id and class are the same" do
660
- (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
702
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be(true)
661
703
  end
662
704
 
663
705
  should "not be equal if class same but id different" do
664
- (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
706
+ (@document.new('_id' => @oid) == @document.new('_id' => Mongo::ObjectID.new)).should be(false)
665
707
  end
666
708
 
667
709
  should "not be equal if id same but class different" do
@@ -669,7 +711,7 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
669
711
  include MongoMapper::Document
670
712
  end
671
713
 
672
- (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
714
+ (@document.new('_id' => @oid) == @another_document.new('_id' => @oid)).should be(false)
673
715
  end
674
716
  end
675
717
  end # instance of a embedded document