mongo_mapper 0.6.10 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/README.rdoc +5 -14
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper.rb +48 -56
  5. data/lib/mongo_mapper/document.rb +136 -164
  6. data/lib/mongo_mapper/embedded_document.rb +29 -354
  7. data/lib/mongo_mapper/plugins.rb +31 -0
  8. data/lib/mongo_mapper/plugins/associations.rb +105 -0
  9. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  10. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  11. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  12. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  13. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  14. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  15. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  16. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  17. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  18. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  20. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  21. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  22. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  23. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  24. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  25. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  26. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  27. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  28. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  29. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  30. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  31. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  32. data/lib/mongo_mapper/plugins/pagination/proxy.rb +68 -0
  33. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  34. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  35. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  36. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  37. data/lib/mongo_mapper/query.rb +130 -0
  38. data/lib/mongo_mapper/support.rb +40 -17
  39. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  40. data/lib/mongo_mapper/support/find.rb +77 -0
  41. data/mongo_mapper.gemspec +55 -38
  42. data/performance/read_write.rb +52 -0
  43. data/specs.watchr +23 -2
  44. data/test/functional/associations/test_belongs_to_proxy.rb +12 -10
  45. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -21
  46. data/test/functional/associations/test_many_documents_proxy.rb +2 -8
  47. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +59 -39
  48. data/test/functional/associations/test_many_embedded_proxy.rb +145 -81
  49. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -40
  50. data/test/functional/associations/test_one_proxy.rb +25 -10
  51. data/test/functional/test_binary.rb +2 -8
  52. data/test/functional/test_callbacks.rb +1 -5
  53. data/test/functional/test_dirty.rb +27 -23
  54. data/test/functional/test_document.rb +224 -165
  55. data/test/functional/test_embedded_document.rb +72 -82
  56. data/test/functional/test_identity_map.rb +508 -0
  57. data/test/functional/test_modifiers.rb +15 -5
  58. data/test/functional/test_pagination.rb +1 -3
  59. data/test/functional/test_protected.rb +155 -0
  60. data/test/functional/test_string_id_compatibility.rb +7 -12
  61. data/test/functional/test_validations.rb +26 -58
  62. data/test/models.rb +0 -39
  63. data/test/test_helper.rb +37 -3
  64. data/test/unit/associations/test_base.rb +5 -5
  65. data/test/unit/associations/test_proxy.rb +8 -6
  66. data/test/unit/test_descendant_appends.rb +71 -0
  67. data/test/unit/test_document.rb +71 -76
  68. data/test/unit/test_dynamic_finder.rb +27 -29
  69. data/test/unit/test_embedded_document.rb +555 -601
  70. data/test/unit/{test_key.rb → test_keys.rb} +2 -5
  71. data/test/unit/test_mongo_mapper.rb +69 -9
  72. data/test/unit/test_pagination.rb +40 -32
  73. data/test/unit/test_plugins.rb +50 -0
  74. data/test/unit/{test_finder_options.rb → test_query.rb} +74 -65
  75. data/test/unit/test_rails.rb +123 -0
  76. data/test/unit/{test_serializations.rb → test_serialization.rb} +1 -2
  77. data/test/unit/test_support.rb +23 -7
  78. data/test/unit/test_time_zones.rb +3 -4
  79. data/test/unit/test_validations.rb +58 -17
  80. metadata +53 -36
  81. data/lib/mongo_mapper/associations.rb +0 -78
  82. data/lib/mongo_mapper/associations/base.rb +0 -119
  83. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  84. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  85. data/lib/mongo_mapper/associations/collection.rb +0 -19
  86. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  87. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  88. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  89. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  90. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  91. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  92. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  93. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  94. data/lib/mongo_mapper/callbacks.rb +0 -61
  95. data/lib/mongo_mapper/dirty.rb +0 -117
  96. data/lib/mongo_mapper/dynamic_finder.rb +0 -74
  97. data/lib/mongo_mapper/finder_options.rb +0 -145
  98. data/lib/mongo_mapper/key.rb +0 -36
  99. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  100. data/lib/mongo_mapper/pagination.rb +0 -66
  101. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  102. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  103. data/lib/mongo_mapper/serialization.rb +0 -54
  104. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  105. data/lib/mongo_mapper/validations.rb +0 -39
  106. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -176,22 +176,6 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
176
176
  end
177
177
  end
178
178
 
179
- context "with :all" do
180
- should "work" do
181
- @lounge.messages.find(:all, :order => "position").should == [@lm1, @lm2]
182
- end
183
-
184
- should "work with conditions" do
185
- messages = @lounge.messages.find(:all, :body => 'Loungin!', :order => "position")
186
- messages.should == [@lm1]
187
- end
188
-
189
- should "work with order" do
190
- messages = @lounge.messages.find(:all, :order => 'position desc')
191
- messages.should == [@lm2, @lm1]
192
- end
193
- end
194
-
195
179
  context "with #all" do
196
180
  should "work" do
197
181
  @lounge.messages.all(:order => "position").should == [@lm1, @lm2]
@@ -208,17 +192,6 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
208
192
  end
209
193
  end
210
194
 
211
- context "with :first" do
212
- should "work" do
213
- @lounge.messages.find(:first, :order => "position asc").should == @lm1
214
- end
215
-
216
- should "work with conditions" do
217
- message = @lounge.messages.find(:first, :body => 'I love loungin!', :order => "position asc")
218
- message.should == @lm2
219
- end
220
- end
221
-
222
195
  context "with #first" do
223
196
  should "work" do
224
197
  @lounge.messages.first(:order => "position asc").should == @lm1
@@ -230,17 +203,6 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
230
203
  end
231
204
  end
232
205
 
233
- context "with :last" do
234
- should "work" do
235
- @lounge.messages.find(:last, :order => "position asc").should == @lm2
236
- end
237
-
238
- should "work with conditions" do
239
- message = @lounge.messages.find(:last, :body => 'Loungin!', :order => "position asc")
240
- message.should == @lm1
241
- end
242
- end
243
-
244
206
  context "with #last" do
245
207
  should "work" do
246
208
  @lounge.messages.last(:order => "position asc").should == @lm2
@@ -285,9 +247,9 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
285
247
  end
286
248
 
287
249
  should "not work for ids not in association" do
288
- lambda {
250
+ assert_raises(MongoMapper::DocumentNotFound) do
289
251
  @lounge.messages.find!(@lm1._id, @lm2._id, @hm2._id)
290
- }.should raise_error(MongoMapper::DocumentNotFound)
252
+ end
291
253
  end
292
254
  end
293
255
 
@@ -2,18 +2,10 @@ require 'test_helper'
2
2
 
3
3
  class OneProxyTest < Test::Unit::TestCase
4
4
  def setup
5
- @post_class = Class.new do
6
- include MongoMapper::Document
7
- def self.name; 'Post' end
8
- end
9
-
10
- @author_class = Class.new do
11
- include MongoMapper::Document
5
+ @post_class = Doc('Post')
6
+ @author_class = Doc do
12
7
  key :post_id, ObjectId
13
8
  end
14
-
15
- @post_class.collection.remove
16
- @author_class.collection.remove
17
9
  end
18
10
 
19
11
  should "default to nil" do
@@ -21,6 +13,18 @@ class OneProxyTest < Test::Unit::TestCase
21
13
  @post_class.new.author.nil?.should be_true
22
14
  end
23
15
 
16
+ should "send object id to target" 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
+ author.save.should be_true
23
+ post.save.should be_true
24
+
25
+ post.author.object_id.should == post.author.target.object_id
26
+ end
27
+
24
28
  should "be able to replace the association" do
25
29
  @post_class.one :author, :class => @author_class
26
30
 
@@ -47,6 +51,17 @@ class OneProxyTest < Test::Unit::TestCase
47
51
  post.author?.should be_true
48
52
  end
49
53
 
54
+ should "work with criteria" do
55
+ @post_class.one :primary_author, :class => @author_class, :primary => true
56
+ @post_class.one :author, :class => @author_class
57
+
58
+ post = @post_class.create
59
+ author = @author_class.create(:name => 'Frank', :primary => false, :post_id => post.id)
60
+ primary = @author_class.create(:name => 'Bill', :primary => true, :post_id => post.id)
61
+ post.author.should == author
62
+ post.primary_author.should == primary
63
+ end
64
+
50
65
  should "unset the association" do
51
66
  @post_class.one :author, :class => @author_class
52
67
  post = @post_class.new
@@ -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
@@ -18,12 +15,9 @@ class BinaryTest < Test::Unit::TestCase
18
15
 
19
16
  context "Saving a document with a blank binary value" do
20
17
  setup do
21
- @document = Class.new do
22
- include MongoMapper::Document
23
- set_collection_name 'test'
18
+ @document = Doc do
24
19
  key :file, Binary
25
20
  end
26
- @document.collection.remove
27
21
  end
28
22
 
29
23
  should "not fail" do
@@ -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
@@ -1,26 +1,17 @@
1
1
  require 'test_helper'
2
- require 'models'
3
2
 
4
3
  class DirtyTest < Test::Unit::TestCase
5
4
  def setup
6
- @document = Class.new do
7
- include MongoMapper::Document
8
- set_collection_name 'test'
9
- key :phrase, String
10
- end
11
- @document.collection.remove
12
-
13
- Status.collection.remove
14
- Project.collection.remove
5
+ @document = Doc { key :phrase, String }
15
6
  end
16
-
7
+
17
8
  context "marking changes" do
18
9
  should "not happen if there are none" do
19
10
  doc = @document.new
20
11
  doc.phrase_changed?.should be_false
21
12
  doc.phrase_change.should be_nil
22
13
  end
23
-
14
+
24
15
  should "happen when change happens" do
25
16
  doc = @document.new
26
17
  doc.phrase = 'Golly Gee Willikers Batman'
@@ -28,12 +19,12 @@ class DirtyTest < Test::Unit::TestCase
28
19
  doc.phrase_was.should be_nil
29
20
  doc.phrase_change.should == [nil, 'Golly Gee Willikers Batman']
30
21
  end
31
-
22
+
32
23
  should "happen when initializing" do
33
24
  doc = @document.new(:phrase => 'Foo')
34
25
  doc.changed?.should be_true
35
26
  end
36
-
27
+
37
28
  should "clear changes on save" do
38
29
  doc = @document.new
39
30
  doc.phrase = 'Golly Gee Willikers Batman'
@@ -42,7 +33,7 @@ class DirtyTest < Test::Unit::TestCase
42
33
  doc.phrase_changed?.should_not be_true
43
34
  doc.phrase_change.should be_nil
44
35
  end
45
-
36
+
46
37
  should "clear changes on save!" do
47
38
  doc = @document.new
48
39
  doc.phrase = 'Golly Gee Willikers Batman'
@@ -51,15 +42,18 @@ class DirtyTest < Test::Unit::TestCase
51
42
  doc.phrase_changed?.should_not be_true
52
43
  doc.phrase_change.should be_nil
53
44
  end
54
-
45
+
55
46
  should "not happen when loading from database" do
56
47
  doc = @document.create(:phrase => 'Foo')
48
+ doc = @document.find(doc.id)
49
+
50
+ doc.changed?.should be_false
57
51
  doc.phrase = 'Fart'
58
52
  doc.changed?.should be_true
59
53
  doc.reload
60
54
  doc.changed?.should be_false
61
55
  end
62
-
56
+
63
57
  should "happen if changed after loading from database" do
64
58
  doc = @document.create(:phrase => 'Foo')
65
59
  doc.reload
@@ -68,7 +62,7 @@ class DirtyTest < Test::Unit::TestCase
68
62
  doc.changed?.should be_true
69
63
  end
70
64
  end
71
-
65
+
72
66
  context "blank new value and type integer" do
73
67
  should "not mark changes" do
74
68
  @document.key :age, Integer
@@ -115,7 +109,7 @@ class DirtyTest < Test::Unit::TestCase
115
109
  should "be hash of keys with values of changes if there are changes" do
116
110
  doc = @document.new
117
111
  doc.phrase = 'A penny saved is a penny earned.'
118
- doc.changes.should == {'phrase' => [nil, 'A penny saved is a penny earned.']}
112
+ doc.changes['phrase'].should == [nil, 'A penny saved is a penny earned.']
119
113
  end
120
114
  end
121
115
 
@@ -150,10 +144,20 @@ class DirtyTest < Test::Unit::TestCase
150
144
 
151
145
  context "changing a foreign key through association" do
152
146
  should "mark changes" do
153
- status = Status.create(:name => 'Foo')
154
- status.project = Project.create(:name => 'Bar')
155
- status.changed?.should be_true
156
- status.changed.should == %w(project_id)
147
+ project_class = Doc do
148
+ key :name, String
149
+ end
150
+
151
+ milestone_class = Doc do
152
+ key :project_id, ObjectId
153
+ key :name, String
154
+ end
155
+ milestone_class.belongs_to :project, :class => project_class
156
+
157
+ milestone = milestone_class.create(:name => 'Launch')
158
+ milestone.project = project_class.create(:name => 'Harmony')
159
+ milestone.changed?.should be_true
160
+ milestone.changed.should == %w(project_id)
157
161
  end
158
162
  end
159
163
  end
@@ -3,8 +3,7 @@ require 'models'
3
3
 
4
4
  class DocumentTest < Test::Unit::TestCase
5
5
  def setup
6
- @document = Class.new do
7
- include MongoMapper::Document
6
+ @document = Doc do
8
7
  set_collection_name 'users'
9
8
 
10
9
  key :first_name, String
@@ -12,10 +11,9 @@ class DocumentTest < Test::Unit::TestCase
12
11
  key :age, Integer
13
12
  key :date, Date
14
13
  end
15
- @document.collection.remove
16
14
  end
17
15
 
18
- context "Using key with type Array" do
16
+ context "array key" do
19
17
  setup do
20
18
  @document.key :tags, Array
21
19
  end
@@ -62,7 +60,7 @@ class DocumentTest < Test::Unit::TestCase
62
60
  end
63
61
  end
64
62
 
65
- context "Using key with type Hash" do
63
+ context "hash key" do
66
64
  setup do
67
65
  @document.key :foo, Hash
68
66
  end
@@ -97,7 +95,7 @@ class DocumentTest < Test::Unit::TestCase
97
95
  end
98
96
  end
99
97
 
100
- context "Using key with custom type with default" do
98
+ context "custom type key with default" do
101
99
  setup do
102
100
  @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
103
101
  end
@@ -143,12 +141,7 @@ class DocumentTest < Test::Unit::TestCase
143
141
  end
144
142
 
145
143
  should "not fail if no attributes provided" do
146
- document = Class.new do
147
- include MongoMapper::Document
148
- set_collection_name 'test'
149
- end
150
- document.collection.remove
151
-
144
+ document = Doc()
152
145
  lambda { document.create }.should change { document.count }.by(1)
153
146
  end
154
147
  end
@@ -246,7 +239,19 @@ class DocumentTest < Test::Unit::TestCase
246
239
  end
247
240
 
248
241
  should "raise document not found if nothing provided for find!" do
249
- lambda { @document.find! }.should raise_error(MongoMapper::DocumentNotFound)
242
+ assert_raises(MongoMapper::DocumentNotFound) do
243
+ @document.find!
244
+ end
245
+ end
246
+
247
+ should "raise error if trying to find with :all, :first, or :last" do
248
+ [:all, :first, :last].each do |m|
249
+ assert_raises(ArgumentError) { @document.find(m) }
250
+ end
251
+
252
+ [:all, :first, :last].each do |m|
253
+ assert_raises(ArgumentError) { @document.find!(m) }
254
+ end
250
255
  end
251
256
 
252
257
  context "(with a single id)" do
@@ -259,9 +264,7 @@ class DocumentTest < Test::Unit::TestCase
259
264
  end
260
265
 
261
266
  should "raise error if document not found with find!" do
262
- lambda {
263
- @document.find!(123)
264
- }.should raise_error(MongoMapper::DocumentNotFound)
267
+ assert_raises(MongoMapper::DocumentNotFound) { @document.find!(123) }
265
268
  end
266
269
  end
267
270
 
@@ -274,7 +277,17 @@ class DocumentTest < Test::Unit::TestCase
274
277
  @document.find([@doc1._id, @doc2._id]).should == [@doc1, @doc2]
275
278
  end
276
279
 
277
- should "return array if array only has one element" do
280
+ should "compact not found when using find" do
281
+ @document.find(@doc1._id, 1234).should == [@doc1]
282
+ end
283
+
284
+ should "raise error if not all found when using find!" do
285
+ assert_raises(MongoMapper::DocumentNotFound) do
286
+ @document.find!(@doc1._id, 1234)
287
+ end
288
+ end
289
+
290
+ should "return array if array with one element" do
278
291
  @document.find([@doc1._id]).should == [@doc1]
279
292
  end
280
293
  end
@@ -284,43 +297,21 @@ class DocumentTest < Test::Unit::TestCase
284
297
  @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
285
298
  end
286
299
 
287
- context "(with :all)" do
288
- should "find all documents" do
289
- @document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
290
- end
291
-
292
- should "be able to add conditions" do
293
- @document.find(:all, :first_name => 'John').should == [@doc1]
294
- end
295
- end
296
-
297
- context "(with #all)" do
300
+ context "#all" do
298
301
  should "find all documents based on criteria" do
299
302
  @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
300
303
  @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
301
304
  end
302
305
  end
303
306
 
304
- context "(with :first)" do
305
- should "find first document" do
306
- @document.find(:first, :order => 'first_name').should == @doc1
307
- end
308
- end
309
-
310
- context "(with #first)" do
307
+ context "#first" do
311
308
  should "find first document based on criteria" do
312
309
  @document.first(:order => 'first_name').should == @doc1
313
310
  @document.first(:age => 28).should == @doc2
314
311
  end
315
312
  end
316
313
 
317
- context "(with :last)" do
318
- should "find last document" do
319
- @document.find(:last, :order => 'age').should == @doc2
320
- end
321
- end
322
-
323
- context "(with #last)" do
314
+ context "#last" do
324
315
  should "find last document based on criteria" do
325
316
  @document.last(:order => 'age').should == @doc2
326
317
  @document.last(:order => 'age', :age => 28).should == @doc2
@@ -331,7 +322,7 @@ class DocumentTest < Test::Unit::TestCase
331
322
  end
332
323
  end
333
324
 
334
- context "(with :find_by)" do
325
+ context "#find_by..." do
335
326
  should "find document based on argument" do
336
327
  @document.find_by_first_name('John').should == @doc1
337
328
  @document.find_by_last_name('Nunemaker', :order => 'age desc').should == @doc1
@@ -347,7 +338,19 @@ class DocumentTest < Test::Unit::TestCase
347
338
  end
348
339
  end
349
340
 
350
- context "(with dynamic finders)" do
341
+ context "#find_each" do
342
+ should "yield all documents found, based on criteria" do
343
+ yield_documents = []
344
+ @document.find_each(:order => "first_name") {|doc| yield_documents << doc }
345
+ yield_documents.should == [@doc1, @doc3, @doc2]
346
+
347
+ yield_documents = []
348
+ @document.find_each(:last_name => 'Nunemaker', :order => 'age desc') {|doc| yield_documents << doc }
349
+ yield_documents.should == [@doc1, @doc3]
350
+ end
351
+ end
352
+
353
+ context "dynamic finders" do
351
354
  should "find document based on all arguments" do
352
355
  @document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 27).should == @doc1
353
356
  end
@@ -400,7 +403,44 @@ class DocumentTest < Test::Unit::TestCase
400
403
  end
401
404
 
402
405
  should "return nil if document not found" do
403
- @document.find_by_id(1234).should be(nil)
406
+ @document.find_by_id(1234).should be_nil
407
+ end
408
+ end
409
+
410
+ context "first_or_create" do
411
+ should "find if exists" do
412
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
413
+ lambda {
414
+ found = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
415
+ found.should == created
416
+ }.should_not change { @document.count }
417
+ end
418
+
419
+ should "create if not found" do
420
+ lambda {
421
+ created = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
422
+ created.first_name.should == 'John'
423
+ created.last_name.should == 'Nunemaker'
424
+ }.should change { @document.count }.by(1)
425
+ end
426
+ end
427
+
428
+ context "first_or_new" do
429
+ should "find if exists" do
430
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
431
+ lambda {
432
+ found = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
433
+ found.should == created
434
+ }.should_not change { @document.count }
435
+ end
436
+
437
+ should "initialize if not found" do
438
+ lambda {
439
+ created = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
440
+ created.first_name.should == 'John'
441
+ created.last_name.should == 'Nunemaker'
442
+ created.should be_new
443
+ }.should_not change { @document.count }
404
444
  end
405
445
  end
406
446
 
@@ -541,11 +581,9 @@ class DocumentTest < Test::Unit::TestCase
541
581
  end
542
582
 
543
583
  should "return 0 if the collection does not exist" do
544
- klass = Class.new do
545
- include MongoMapper::Document
584
+ klass = Doc do
546
585
  set_collection_name 'foobarbazwickdoesnotexist'
547
586
  end
548
- @document.collection.remove
549
587
 
550
588
  klass.count.should == 0
551
589
  end
@@ -567,10 +605,10 @@ class DocumentTest < Test::Unit::TestCase
567
605
  @document.new.database.should == @document.database
568
606
  end
569
607
 
570
- context "#save (new document)" do
608
+ context "#update_attributes (new document)" do
571
609
  setup do
572
610
  @doc = @document.new(:first_name => 'John', :age => '27')
573
- @doc.save
611
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
574
612
  end
575
613
 
576
614
  should "insert document into the collection" do
@@ -582,46 +620,27 @@ class DocumentTest < Test::Unit::TestCase
582
620
  end
583
621
 
584
622
  should "save attributes" do
585
- @doc.first_name.should == 'John'
586
- @doc.age.should == 27
623
+ @doc.first_name.should == 'Johnny'
624
+ @doc.age.should == 30
587
625
  end
588
626
 
589
627
  should "update attributes in the database" do
590
628
  doc = @doc.reload
591
629
  doc.should == @doc
592
- doc.first_name.should == 'John'
593
- doc.age.should == 27
594
- end
595
-
596
- should "allow to add custom attributes to the document" do
597
- @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
598
- @doc.save
599
- doc = @doc.reload
600
- doc.gender.should == 'male'
601
- doc.tags.should == [1, "2"]
602
- end
603
-
604
- should "allow to use custom methods to assign properties" do
605
- person = RealPerson.new(:realname => 'David')
606
- person.save
607
- person.reload.name.should == 'David'
630
+ doc.first_name.should == 'Johnny'
631
+ doc.age.should == 30
608
632
  end
609
633
 
610
- context "with key of type date" do
611
- should "save the date value as a Time object" do
612
- doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
613
- doc.save
614
- doc.date.should == Date.new(2009, 12, 1)
615
- end
634
+ should "allow updating custom attributes" do
635
+ @doc.update_attributes(:gender => 'mALe')
636
+ @doc.reload.gender.should == 'mALe'
616
637
  end
617
638
  end
618
639
 
619
- context "#save (existing document)" do
640
+ context "#update_attributes (existing document)" do
620
641
  setup do
621
642
  @doc = @document.create(:first_name => 'John', :age => '27')
622
- @doc.first_name = 'Johnny'
623
- @doc.age = 30
624
- @doc.save
643
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
625
644
  end
626
645
 
627
646
  should "not insert document into collection" do
@@ -638,19 +657,26 @@ class DocumentTest < Test::Unit::TestCase
638
657
  doc.first_name.should == 'Johnny'
639
658
  doc.age.should == 30
640
659
  end
660
+ end
641
661
 
642
- should "allow updating custom attributes" do
643
- @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
644
- @doc.gender = 'Male'
645
- @doc.save
646
- @doc.reload.gender.should == 'Male'
662
+ context "#update_attributes (return value)" do
663
+ setup do
664
+ @document.key :foo, String, :required => true
647
665
  end
648
- end
649
666
 
650
- context "#update_attributes (new document)" do
667
+ should "be true if document valid" do
668
+ @document.new.update_attributes(:foo => 'bar').should be_true
669
+ end
670
+
671
+ should "be false if document not valid" do
672
+ @document.new.update_attributes({}).should be_false
673
+ end
674
+ end
675
+
676
+ context "#save (new document)" do
651
677
  setup do
652
678
  @doc = @document.new(:first_name => 'John', :age => '27')
653
- @doc.update_attributes(:first_name => 'Johnny', :age => 30)
679
+ @doc.save
654
680
  end
655
681
 
656
682
  should "insert document into the collection" do
@@ -662,27 +688,54 @@ class DocumentTest < Test::Unit::TestCase
662
688
  end
663
689
 
664
690
  should "save attributes" do
665
- @doc.first_name.should == 'Johnny'
666
- @doc.age.should == 30
691
+ @doc.first_name.should == 'John'
692
+ @doc.age.should == 27
667
693
  end
668
694
 
669
695
  should "update attributes in the database" do
670
696
  doc = @doc.reload
671
697
  doc.should == @doc
672
- doc.first_name.should == 'Johnny'
673
- doc.age.should == 30
698
+ doc.first_name.should == 'John'
699
+ doc.age.should == 27
674
700
  end
675
701
 
676
- should "allow updating custom attributes" do
677
- @doc.update_attributes(:gender => 'mALe')
678
- @doc.reload.gender.should == 'mALe'
702
+ should "allow to add custom attributes to the document" do
703
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
704
+ @doc.save
705
+ doc = @doc.reload
706
+ doc.gender.should == 'male'
707
+ doc.tags.should == [1, "2"]
708
+ end
709
+
710
+ should "allow to use custom methods to assign properties" do
711
+ klass = Doc do
712
+ key :name, String
713
+
714
+ def realname=(value)
715
+ self.name = value
716
+ end
717
+ end
718
+
719
+ person = klass.new(:realname => 'David')
720
+ person.save
721
+ person.reload.name.should == 'David'
722
+ end
723
+
724
+ context "with key of type date" do
725
+ should "save the date value as a Time object" do
726
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "12/01/2009")
727
+ doc.save
728
+ doc.date.should == Date.new(2009, 12, 1)
729
+ end
679
730
  end
680
731
  end
681
732
 
682
- context "#update_attributes (existing document)" do
733
+ context "#save (existing document)" do
683
734
  setup do
684
735
  @doc = @document.create(:first_name => 'John', :age => '27')
685
- @doc.update_attributes(:first_name => 'Johnny', :age => 30)
736
+ @doc.first_name = 'Johnny'
737
+ @doc.age = 30
738
+ @doc.save
686
739
  end
687
740
 
688
741
  should "not insert document into collection" do
@@ -699,68 +752,82 @@ class DocumentTest < Test::Unit::TestCase
699
752
  doc.first_name.should == 'Johnny'
700
753
  doc.age.should == 30
701
754
  end
702
- end
703
-
704
- context "#update_attributes" do
705
- setup do
706
- @document.key :foo, String, :required => true
707
- end
708
-
709
- should "return true if document valid" do
710
- @document.new.update_attributes(:foo => 'bar').should be_true
711
- end
712
755
 
713
- should "return false if document not valid" do
714
- @document.new.update_attributes({}).should be_false
756
+ should "allow updating custom attributes" do
757
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
758
+ @doc.gender = 'Male'
759
+ @doc.save
760
+ @doc.reload.gender.should == 'Male'
715
761
  end
716
762
  end
717
-
763
+
718
764
  context "#save (with validations off)" do
719
765
  setup do
720
- @document = Class.new do
721
- include MongoMapper::Document
722
- set_collection_name 'test'
766
+ @document = Doc do
723
767
  key :name, String, :required => true
724
768
  end
725
- @document.collection.remove
726
769
  end
727
770
 
728
- should "insert document" do
771
+ should "insert invalid document" do
729
772
  doc = @document.new
773
+ doc.expects(:valid?).never
730
774
  doc.save(:validate => false)
731
775
  @document.count.should == 1
732
776
  end
733
-
734
- should "work with false passed to save" do
735
- doc = @document.new
736
- doc.save(false)
737
- @document.count.should == 1
738
- end
739
777
  end
740
778
 
741
779
  context "#save (with options)" do
742
780
  setup do
743
- MongoMapper.ensured_indexes = []
744
-
745
- @document = Class.new do
746
- include MongoMapper::Document
747
- set_collection_name 'test'
781
+ @document = Doc do
748
782
  key :name, String
749
- ensure_index :name, :unique => true
783
+ set_collection_name 'test_indexes'
750
784
  end
751
- @document.collection.drop_indexes
752
-
753
- MongoMapper.ensure_indexes!
785
+ drop_indexes(@document)
786
+ @document.ensure_index :name, :unique => true
754
787
  end
755
-
788
+
756
789
  should "allow passing safe" do
757
- doc = @document.new(:name => 'John')
758
- doc.save
759
-
790
+ @document.create(:name => 'John')
760
791
  assert_raises(Mongo::OperationFailure) do
761
792
  @document.new(:name => 'John').save(:safe => true)
762
793
  end
763
794
  end
795
+
796
+ should "raise argument error if options has unsupported key" do
797
+ assert_raises(ArgumentError) do
798
+ @document.new.save(:foo => true)
799
+ end
800
+ end
801
+ end
802
+
803
+ context "#save! (with options)" do
804
+ setup do
805
+ @document = Doc do
806
+ key :name, String
807
+ set_collection_name 'test_indexes'
808
+ end
809
+ drop_indexes(@document)
810
+ @document.ensure_index :name, :unique => true
811
+ end
812
+
813
+ should "allow passing safe" do
814
+ @document.create(:name => 'John')
815
+ assert_raises(Mongo::OperationFailure) do
816
+ @document.new(:name => 'John').save!(:safe => true)
817
+ end
818
+ end
819
+
820
+ should "raise argument error if options has unsupported key" do
821
+ assert_raises(ArgumentError) do
822
+ @document.new.save!(:foo => true)
823
+ end
824
+ end
825
+
826
+ should "raise argument error if using validate as that would be pointless with save!" do
827
+ assert_raises(ArgumentError) do
828
+ @document.new.save!(:validate => false)
829
+ end
830
+ end
764
831
  end
765
832
 
766
833
  context "#destroy" do
@@ -843,7 +910,7 @@ class DocumentTest < Test::Unit::TestCase
843
910
  @parent.save
844
911
  @daughter.save
845
912
 
846
- collection = DocParent.find(:all)
913
+ collection = DocParent.all
847
914
  collection.size.should == 2
848
915
  collection.first.should be_kind_of(DocParent)
849
916
  collection.first.name.should == "Daddy Warbucks"
@@ -967,11 +1034,19 @@ class DocumentTest < Test::Unit::TestCase
967
1034
 
968
1035
  context "timestamping" do
969
1036
  setup do
970
- @document.timestamps!
1037
+ @klass = Doc do
1038
+ set_collection_name 'users'
1039
+
1040
+ key :first_name, String
1041
+ key :last_name, String
1042
+ key :age, Integer
1043
+ key :date, Date
1044
+ end
1045
+ @klass.timestamps!
971
1046
  end
972
1047
 
973
1048
  should "set created_at and updated_at on create" do
974
- doc = @document.new(:first_name => 'John', :age => 27)
1049
+ doc = @klass.new(:first_name => 'John', :age => 27)
975
1050
  doc.created_at.should be(nil)
976
1051
  doc.updated_at.should be(nil)
977
1052
  doc.save
@@ -981,7 +1056,7 @@ class DocumentTest < Test::Unit::TestCase
981
1056
 
982
1057
  should "not overwrite created_at if it already exists" do
983
1058
  original_created_at = 1.month.ago
984
- doc = @document.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
1059
+ doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
985
1060
  doc.created_at.to_i.should == original_created_at.to_i
986
1061
  doc.updated_at.should be_nil
987
1062
  doc.save
@@ -990,7 +1065,7 @@ class DocumentTest < Test::Unit::TestCase
990
1065
  end
991
1066
 
992
1067
  should "set updated_at on field update but leave created_at alone" do
993
- doc = @document.create(:first_name => 'John', :age => 27)
1068
+ doc = @klass.create(:first_name => 'John', :age => 27)
994
1069
  old_created_at = doc.created_at
995
1070
  old_updated_at = doc.updated_at
996
1071
  doc.first_name = 'Johnny'
@@ -1004,12 +1079,12 @@ class DocumentTest < Test::Unit::TestCase
1004
1079
  end
1005
1080
 
1006
1081
  should "set updated_at on document update but leave created_at alone" do
1007
- doc = @document.create(:first_name => 'John', :age => 27)
1082
+ doc = @klass.create(:first_name => 'John', :age => 27)
1008
1083
  old_created_at = doc.created_at
1009
1084
  old_updated_at = doc.updated_at
1010
1085
 
1011
1086
  Timecop.freeze(Time.now + 5.seconds) do
1012
- @document.update(doc._id, { :first_name => 'Johnny' })
1087
+ @klass.update(doc._id, { :first_name => 'Johnny' })
1013
1088
  end
1014
1089
 
1015
1090
  doc = doc.reload
@@ -1040,7 +1115,7 @@ class DocumentTest < Test::Unit::TestCase
1040
1115
  end
1041
1116
  end
1042
1117
 
1043
- context "#exist?" do
1118
+ context "#exists?" do
1044
1119
  setup do
1045
1120
  @doc = @document.create(:first_name => "James", :age => 27)
1046
1121
  end
@@ -1065,14 +1140,11 @@ class DocumentTest < Test::Unit::TestCase
1065
1140
 
1066
1141
  context "#reload" do
1067
1142
  setup do
1068
- @foo_class = Class.new do
1069
- include MongoMapper::Document
1143
+ @foo_class = Doc do
1070
1144
  key :name
1071
1145
  end
1072
- @foo_class.collection.remove
1073
1146
 
1074
- @bar_class = Class.new do
1075
- include MongoMapper::EmbeddedDocument
1147
+ @bar_class = EDoc do
1076
1148
  key :name
1077
1149
  end
1078
1150
 
@@ -1107,19 +1179,14 @@ class DocumentTest < Test::Unit::TestCase
1107
1179
  should "return self" do
1108
1180
  @instance.reload.object_id.should == @instance.object_id
1109
1181
  end
1110
- end
1111
-
1112
- context "Saving a document with a custom id" do
1113
- should "clear custom id flag when saved" do
1114
- @document.key :_id, String
1115
- doc = @document.new(:id => '1234')
1116
- doc.using_custom_id?.should be_true
1117
- doc.save.should be_true
1118
- doc.using_custom_id?.should be_false
1182
+
1183
+ should "raise DocumentNotFound if not found" do
1184
+ @instance.destroy
1185
+ assert_raises(MongoMapper::DocumentNotFound) { @instance.reload }
1119
1186
  end
1120
1187
  end
1121
1188
 
1122
- context "Loading a document from the database with keys that are not defined" do
1189
+ context "database has keys not defined in model" do
1123
1190
  setup do
1124
1191
  @id = Mongo::ObjectID.new
1125
1192
  @document.collection.insert({
@@ -1144,28 +1211,22 @@ class DocumentTest < Test::Unit::TestCase
1144
1211
 
1145
1212
  context "Indexing" do
1146
1213
  setup do
1147
- MongoMapper.ensured_indexes = []
1148
- @document.collection.drop_indexes
1214
+ drop_indexes(@document)
1149
1215
  end
1150
1216
 
1151
1217
  should "allow creating index for a key" do
1152
1218
  @document.ensure_index :first_name
1153
- MongoMapper.ensure_indexes!
1154
-
1155
1219
  @document.should have_index('first_name_1')
1156
1220
  end
1157
1221
 
1158
1222
  should "allow creating unique index for a key" do
1159
1223
  @document.ensure_index :first_name, :unique => true
1160
- MongoMapper.ensure_indexes!
1161
-
1162
1224
  @document.should have_index('first_name_1')
1163
1225
  end
1164
1226
 
1165
1227
  should "allow creating index on multiple keys" do
1166
1228
  @document.ensure_index [[:first_name, 1], [:last_name, -1]]
1167
- MongoMapper.ensure_indexes!
1168
-
1229
+
1169
1230
  # order is different for different versions of ruby so instead of
1170
1231
  # just checking have_index('first_name_1_last_name_-1') I'm checking
1171
1232
  # the values of the indexes to make sure the index creation was successful
@@ -1177,8 +1238,6 @@ class DocumentTest < Test::Unit::TestCase
1177
1238
 
1178
1239
  should "work with :index shortcut when defining key" do
1179
1240
  @document.key :father, String, :index => true
1180
- MongoMapper.ensure_indexes!
1181
-
1182
1241
  @document.should have_index('father_1')
1183
1242
  end
1184
1243
  end