mongo_mapper-unstable 2010.3.8 → 2010.06.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/README.rdoc +4 -8
  2. data/bin/mmconsole +1 -1
  3. data/examples/keys.rb +37 -0
  4. data/examples/plugins.rb +41 -0
  5. data/examples/querying.rb +35 -0
  6. data/examples/scopes.rb +52 -0
  7. data/lib/mongo_mapper/connection.rb +83 -0
  8. data/lib/mongo_mapper/document.rb +11 -329
  9. data/lib/mongo_mapper/embedded_document.rb +9 -38
  10. data/lib/mongo_mapper/exceptions.rb +30 -0
  11. data/lib/mongo_mapper/extensions/array.rb +19 -0
  12. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  13. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  14. data/lib/mongo_mapper/extensions/date.rb +25 -0
  15. data/lib/mongo_mapper/extensions/float.rb +14 -0
  16. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  17. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  18. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  19. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  20. data/lib/mongo_mapper/extensions/object.rb +27 -0
  21. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  22. data/lib/mongo_mapper/extensions/set.rb +20 -0
  23. data/lib/mongo_mapper/extensions/string.rb +18 -0
  24. data/lib/mongo_mapper/extensions/time.rb +29 -0
  25. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  26. data/lib/mongo_mapper/plugins/associations/base.rb +7 -6
  27. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +5 -6
  28. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +5 -6
  29. data/lib/mongo_mapper/plugins/associations/collection.rb +1 -0
  30. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +2 -1
  31. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +25 -39
  32. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +4 -4
  33. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +36 -46
  34. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +1 -0
  35. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +5 -4
  36. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +1 -0
  37. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  38. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +7 -7
  39. data/lib/mongo_mapper/plugins/associations/proxy.rb +16 -8
  40. data/lib/mongo_mapper/plugins/associations.rb +14 -22
  41. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  42. data/lib/mongo_mapper/plugins/callbacks.rb +17 -5
  43. data/lib/mongo_mapper/plugins/clone.rb +10 -4
  44. data/lib/mongo_mapper/plugins/descendants.rb +3 -2
  45. data/lib/mongo_mapper/plugins/dirty.rb +1 -0
  46. data/lib/mongo_mapper/plugins/document.rb +41 -0
  47. data/lib/mongo_mapper/{support/find.rb → plugins/dynamic_querying/dynamic_finder.rb} +3 -36
  48. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  49. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  50. data/lib/mongo_mapper/plugins/equality.rb +4 -10
  51. data/lib/mongo_mapper/plugins/identity_map.rb +29 -23
  52. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  53. data/lib/mongo_mapper/plugins/inspect.rb +1 -0
  54. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  55. data/lib/mongo_mapper/plugins/keys.rb +85 -110
  56. data/lib/mongo_mapper/plugins/logger.rb +1 -0
  57. data/lib/mongo_mapper/plugins/modifiers.rb +41 -16
  58. data/lib/mongo_mapper/plugins/pagination.rb +5 -15
  59. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  60. data/lib/mongo_mapper/plugins/protected.rb +9 -1
  61. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  62. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  63. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  64. data/lib/mongo_mapper/plugins/rails.rb +6 -1
  65. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  66. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  67. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  68. data/lib/mongo_mapper/plugins/serialization.rb +5 -4
  69. data/lib/mongo_mapper/plugins/timestamps.rb +2 -1
  70. data/lib/mongo_mapper/plugins/userstamps.rb +1 -0
  71. data/lib/mongo_mapper/plugins/validations.rb +9 -5
  72. data/lib/mongo_mapper/plugins.rb +1 -20
  73. data/lib/mongo_mapper/support/descendant_appends.rb +5 -6
  74. data/lib/mongo_mapper/version.rb +4 -0
  75. data/lib/mongo_mapper.rb +71 -128
  76. data/test/{NOTE_ON_TESTING → _NOTE_ON_TESTING} +0 -0
  77. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +5 -5
  78. data/test/functional/associations/test_belongs_to_proxy.rb +13 -21
  79. data/test/functional/associations/test_in_array_proxy.rb +7 -9
  80. data/test/functional/associations/test_many_documents_as_proxy.rb +5 -5
  81. data/test/functional/associations/test_many_documents_proxy.rb +186 -64
  82. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +22 -22
  83. data/test/functional/associations/test_many_embedded_proxy.rb +32 -32
  84. data/test/functional/associations/test_many_polymorphic_proxy.rb +47 -47
  85. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  86. data/test/functional/associations/test_one_proxy.rb +70 -49
  87. data/test/functional/test_accessible.rb +168 -0
  88. data/test/functional/test_associations.rb +11 -11
  89. data/test/functional/test_binary.rb +5 -5
  90. data/test/functional/test_caching.rb +76 -0
  91. data/test/functional/test_callbacks.rb +104 -34
  92. data/test/functional/test_dirty.rb +16 -16
  93. data/test/functional/test_document.rb +12 -924
  94. data/test/functional/test_dynamic_querying.rb +75 -0
  95. data/test/functional/test_embedded_document.rb +88 -8
  96. data/test/functional/test_identity_map.rb +41 -43
  97. data/test/functional/{test_indexing.rb → test_indexes.rb} +3 -5
  98. data/test/functional/test_logger.rb +1 -1
  99. data/test/functional/test_modifiers.rb +275 -181
  100. data/test/functional/test_pagination.rb +13 -15
  101. data/test/functional/test_protected.rb +25 -11
  102. data/test/functional/test_querying.rb +873 -0
  103. data/test/functional/test_safe.rb +76 -0
  104. data/test/functional/test_sci.rb +230 -0
  105. data/test/functional/test_scopes.rb +171 -0
  106. data/test/functional/test_string_id_compatibility.rb +11 -11
  107. data/test/functional/test_timestamps.rb +0 -2
  108. data/test/functional/test_userstamps.rb +0 -1
  109. data/test/functional/test_validations.rb +44 -31
  110. data/test/models.rb +18 -17
  111. data/test/{active_model_lint_test.rb → test_active_model_lint.rb} +3 -1
  112. data/test/test_helper.rb +59 -16
  113. data/test/unit/associations/test_base.rb +47 -42
  114. data/test/unit/associations/test_proxy.rb +15 -15
  115. data/test/unit/serializers/test_json_serializer.rb +29 -29
  116. data/test/unit/test_clone.rb +69 -0
  117. data/test/unit/test_descendant_appends.rb +3 -3
  118. data/test/unit/test_document.rb +49 -67
  119. data/test/unit/test_dynamic_finder.rb +53 -51
  120. data/test/unit/test_embedded_document.rb +19 -38
  121. data/test/unit/{test_support.rb → test_extensions.rb} +136 -122
  122. data/test/unit/test_key.rb +185 -0
  123. data/test/unit/test_keys.rb +29 -147
  124. data/test/unit/test_mongo_mapper.rb +3 -48
  125. data/test/unit/test_pagination.rb +1 -150
  126. data/test/unit/test_rails.rb +77 -19
  127. data/test/unit/test_rails_compatibility.rb +12 -12
  128. data/test/unit/test_serialization.rb +5 -5
  129. data/test/unit/test_time_zones.rb +9 -9
  130. data/test/unit/test_validations.rb +46 -46
  131. metadata +157 -155
  132. data/.gitignore +0 -10
  133. data/Rakefile +0 -55
  134. data/VERSION +0 -1
  135. data/lib/mongo_mapper/plugins/pagination/proxy.rb +0 -72
  136. data/lib/mongo_mapper/query.rb +0 -130
  137. data/lib/mongo_mapper/support.rb +0 -215
  138. data/mongo_mapper.gemspec +0 -196
  139. data/performance/read_write.rb +0 -52
  140. data/specs.watchr +0 -51
  141. data/test/support/custom_matchers.rb +0 -55
  142. data/test/support/timing.rb +0 -16
  143. data/test/unit/test_query.rb +0 -340
@@ -0,0 +1,873 @@
1
+ require 'test_helper'
2
+
3
+ class QueryingTesting < Test::Unit::TestCase
4
+ def setup
5
+ @document = Doc do
6
+ key :first_name, String
7
+ key :last_name, String
8
+ key :age, Integer
9
+ key :date, Date
10
+ end
11
+ end
12
+
13
+ context ".query" do
14
+ setup do
15
+ @query = @document.query
16
+ end
17
+
18
+ should "set model to self" do
19
+ @query.model.should == @document
20
+ end
21
+
22
+ should "always return new instance" do
23
+ @document.query.should_not equal(@query)
24
+ end
25
+
26
+ should "apply options" do
27
+ @document.query(:foo => 'bar')[:foo].should == 'bar'
28
+ end
29
+ end
30
+
31
+ context ".criteria_hash" do
32
+ setup do
33
+ @hash = @document.criteria_hash
34
+ end
35
+
36
+ should "set object id keys on hash" do
37
+ @hash.object_ids.should == [:_id]
38
+ end
39
+
40
+ should "always return new instance" do
41
+ @document.criteria_hash.should_not equal(@hash)
42
+ end
43
+
44
+ should "apply provided criteria" do
45
+ @document.criteria_hash(:foo => 'bar')[:foo].should == 'bar'
46
+ end
47
+ end
48
+
49
+ context ".create (single document)" do
50
+ setup do
51
+ @doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
52
+ end
53
+
54
+ should "create a document in correct collection" do
55
+ @document.count.should == 1
56
+ end
57
+
58
+ should "automatically set id" do
59
+ @doc.id.should be_instance_of(BSON::ObjectID)
60
+ @doc._id.should be_instance_of(BSON::ObjectID)
61
+ end
62
+
63
+ should "no longer be new?" do
64
+ @doc.new?.should be_false
65
+ end
66
+
67
+ should "return instance of document" do
68
+ @doc.should be_instance_of(@document)
69
+ @doc.first_name.should == 'John'
70
+ @doc.last_name.should == 'Nunemaker'
71
+ @doc.age.should == 27
72
+ end
73
+
74
+ should "not fail if no attributes provided" do
75
+ document = Doc()
76
+ lambda { document.create }.should change { document.count }.by(1)
77
+ end
78
+ end
79
+
80
+ context ".create (multiple documents)" do
81
+ setup do
82
+ @docs = @document.create([
83
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
84
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
85
+ ])
86
+ end
87
+
88
+ should "create multiple documents" do
89
+ @document.count.should == 2
90
+ end
91
+
92
+ should "return an array of doc instances" do
93
+ @docs.map do |doc|
94
+ doc.should be_instance_of(@document)
95
+ end
96
+ end
97
+ end
98
+
99
+ context ".update (single document)" do
100
+ setup do
101
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
102
+ @doc = @document.update(doc._id, {:age => 40})
103
+ end
104
+
105
+ should "update attributes provided" do
106
+ @doc.age.should == 40
107
+ end
108
+
109
+ should "not update existing attributes that were not set to update" do
110
+ @doc.first_name.should == 'John'
111
+ @doc.last_name.should == 'Nunemaker'
112
+ end
113
+
114
+ should "not create new document" do
115
+ @document.count.should == 1
116
+ end
117
+
118
+ should "raise error if not provided id" do
119
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
120
+ lambda { @document.update }.should raise_error(ArgumentError)
121
+ end
122
+
123
+ should "raise error if not provided attributes" do
124
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
125
+ lambda { @document.update(doc._id) }.should raise_error(ArgumentError)
126
+ lambda { @document.update(doc._id, [1]) }.should raise_error(ArgumentError)
127
+ end
128
+ end
129
+
130
+ context ".update (multiple documents)" do
131
+ setup do
132
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
133
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
134
+
135
+ @docs = @document.update({
136
+ @doc1._id => {:age => 30},
137
+ @doc2._id => {:age => 30},
138
+ })
139
+ end
140
+
141
+ should "not create any new documents" do
142
+ @document.count.should == 2
143
+ end
144
+
145
+ should "should return an array of doc instances" do
146
+ @docs.map do |doc|
147
+ doc.should be_instance_of(@document)
148
+ end
149
+ end
150
+
151
+ should "update the documents" do
152
+ @document.find(@doc1._id).age.should == 30
153
+ @document.find(@doc2._id).age.should == 30
154
+ end
155
+
156
+ should "raise error if not a hash" do
157
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
158
+ end
159
+ end
160
+
161
+ context ".find" do
162
+ setup do
163
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
164
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
165
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
166
+ end
167
+
168
+ should "return nil if nothing provided for find" do
169
+ @document.find.should be_nil
170
+ end
171
+
172
+ should "raise document not found if nothing provided for find!" do
173
+ assert_raises(MongoMapper::DocumentNotFound) do
174
+ @document.find!
175
+ end
176
+ end
177
+
178
+ context "(with a single id)" do
179
+ should "work" do
180
+ @document.find(@doc1._id).should == @doc1
181
+ end
182
+
183
+ should "return nil if document not found with find" do
184
+ @document.find(123).should be_nil
185
+ end
186
+
187
+ should "raise error if document not found with find!" do
188
+ assert_raises(MongoMapper::DocumentNotFound) { @document.find!(123) }
189
+ end
190
+ end
191
+
192
+ context "(with multiple id's)" do
193
+ should "work as arguments" do
194
+ @document.find(@doc1._id, @doc2._id).should == [@doc1, @doc2]
195
+ end
196
+
197
+ should "work as arguments with string ids" do
198
+ @document.find(@doc1._id.to_s, @doc2._id.to_s).should == [@doc1, @doc2]
199
+ end
200
+
201
+ should "work as array" do
202
+ @document.find([@doc1._id, @doc2._id]).should == [@doc1, @doc2]
203
+ end
204
+
205
+ should "work as array with string ids" do
206
+ @document.find([@doc1._id.to_s, @doc2._id.to_s]).should == [@doc1, @doc2]
207
+ end
208
+
209
+ should "compact not found when using find" do
210
+ @document.find(@doc1._id, BSON::ObjectID.new.to_s).should == [@doc1]
211
+ end
212
+
213
+ should "raise error if not all found when using find!" do
214
+ assert_raises(MongoMapper::DocumentNotFound) do
215
+ @document.find!(@doc1._id, BSON::ObjectID.new.to_s)
216
+ end
217
+ end
218
+
219
+ should "return array if array with one element" do
220
+ @document.find([@doc1._id]).should == [@doc1]
221
+ end
222
+ end
223
+
224
+ should "be able to find using condition auto-detection" do
225
+ @document.first(:first_name => 'John').should == @doc1
226
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
227
+ end
228
+
229
+ context "#all" do
230
+ should "find all documents with options" do
231
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
232
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
233
+ end
234
+ end
235
+
236
+ context "#first" do
237
+ should "find first document with options" do
238
+ @document.first(:order => 'first_name').should == @doc1
239
+ @document.first(:age => 28).should == @doc2
240
+ end
241
+ end
242
+
243
+ context "#last" do
244
+ should "find last document with options" do
245
+ @document.last(:order => 'age').should == @doc2
246
+ @document.last(:order => 'age', :age => 28).should == @doc2
247
+ end
248
+ end
249
+
250
+ context "#find_each" do
251
+ should "yield all documents found based on options" do
252
+ yield_documents = []
253
+ @document.find_each(:order => "first_name") {|doc| yield_documents << doc }
254
+ yield_documents.should == [@doc1, @doc3, @doc2]
255
+
256
+ yield_documents = []
257
+ @document.find_each(:last_name => 'Nunemaker', :order => 'age desc') {|doc| yield_documents << doc }
258
+ yield_documents.should == [@doc1, @doc3]
259
+ end
260
+ end
261
+ end # finding documents
262
+
263
+ context ".find_by_id" do
264
+ setup do
265
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
266
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
267
+ end
268
+
269
+ should "be able to find by id" do
270
+ @document.find_by_id(@doc1._id).should == @doc1
271
+ @document.find_by_id(@doc2._id).should == @doc2
272
+ end
273
+
274
+ should "return nil if document not found" do
275
+ @document.find_by_id(1234).should be_nil
276
+ end
277
+ end
278
+
279
+ context ".first_or_create" do
280
+ should "find if exists" do
281
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
282
+ lambda {
283
+ found = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
284
+ found.should == created
285
+ }.should_not change { @document.count }
286
+ end
287
+
288
+ should "create if not found" do
289
+ lambda {
290
+ created = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
291
+ created.first_name.should == 'John'
292
+ created.last_name.should == 'Nunemaker'
293
+ }.should change { @document.count }.by(1)
294
+ end
295
+
296
+ should "disregard non-keys when creating, but use them in the query" do
297
+ assert_nothing_raised do
298
+ @document.create(:first_name => 'John', :age => 9)
299
+ lambda {
300
+ @document.first_or_create(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
301
+ }.should change { @document.count }.by(1)
302
+ end
303
+ end
304
+ end
305
+
306
+ context ".first_or_new" do
307
+ should "find if exists" do
308
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
309
+ lambda {
310
+ found = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
311
+ found.should == created
312
+ }.should_not change { @document.count }
313
+ end
314
+
315
+ should "initialize if not found" do
316
+ lambda {
317
+ created = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
318
+ created.first_name.should == 'John'
319
+ created.last_name.should == 'Nunemaker'
320
+ created.should be_new
321
+ }.should_not change { @document.count }
322
+ end
323
+
324
+ should "disregard non-keys when initializing, but use them in the query" do
325
+ assert_nothing_raised do
326
+ @document.create(:first_name => 'John', :age => 9)
327
+ @document.first_or_new(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
328
+ end
329
+ end
330
+ end
331
+
332
+ context ".delete (single document)" do
333
+ setup do
334
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
335
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
336
+ @document.delete(@doc1._id)
337
+ end
338
+
339
+ should "remove document from collection" do
340
+ @document.count.should == 1
341
+ end
342
+
343
+ should "not remove other documents" do
344
+ @document.find(@doc2._id).should_not be(nil)
345
+ end
346
+ end
347
+
348
+ context ".delete (multiple documents)" do
349
+ should "work with multiple arguments" do
350
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
351
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
352
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
353
+ @document.delete(@doc1._id, @doc2._id)
354
+
355
+ @document.count.should == 1
356
+ end
357
+
358
+ should "work with array as argument" do
359
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
360
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
361
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
362
+ @document.delete([@doc1._id, @doc2._id])
363
+
364
+ @document.count.should == 1
365
+ end
366
+ end
367
+
368
+ context ".delete_all" do
369
+ setup do
370
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
371
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
372
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
373
+ end
374
+
375
+ should "remove all documents when given no conditions" do
376
+ @document.delete_all
377
+ @document.count.should == 0
378
+ end
379
+
380
+ should "only remove matching documents when given conditions" do
381
+ @document.delete_all({:first_name => 'John'})
382
+ @document.count.should == 2
383
+ end
384
+
385
+ should "convert the conditions to mongo criteria" do
386
+ @document.delete_all(:age => [26, 27])
387
+ @document.count.should == 1
388
+ end
389
+ end
390
+
391
+ context ".destroy (single document)" do
392
+ setup do
393
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
394
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
395
+ @document.destroy(@doc1._id)
396
+ end
397
+
398
+ should "remove document from collection" do
399
+ @document.count.should == 1
400
+ end
401
+
402
+ should "not remove other documents" do
403
+ @document.find(@doc2._id).should_not be(nil)
404
+ end
405
+ end
406
+
407
+ context ".destroy (multiple documents)" do
408
+ should "work with multiple arguments" do
409
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
410
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
411
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
412
+ @document.destroy(@doc1._id, @doc2._id)
413
+
414
+ @document.count.should == 1
415
+ end
416
+
417
+ should "work with array as argument" do
418
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
419
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
420
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
421
+ @document.destroy([@doc1._id, @doc2._id])
422
+
423
+ @document.count.should == 1
424
+ end
425
+ end
426
+
427
+ context ".destroy_all" do
428
+ setup do
429
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
430
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
431
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
432
+ end
433
+
434
+ should "remove all documents when given no conditions" do
435
+ @document.destroy_all
436
+ @document.count.should == 0
437
+ end
438
+
439
+ should "only remove matching documents when given conditions" do
440
+ @document.destroy_all(:first_name => 'John')
441
+ @document.count.should == 2
442
+ @document.destroy_all(:age => 26)
443
+ @document.count.should == 1
444
+ end
445
+
446
+ should "convert the conditions to mongo criteria" do
447
+ @document.destroy_all(:age => [26, 27])
448
+ @document.count.should == 1
449
+ end
450
+ end
451
+
452
+ context ".count" do
453
+ setup do
454
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
455
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
456
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
457
+ end
458
+
459
+ should "count all with no arguments" do
460
+ @document.count.should == 3
461
+ end
462
+
463
+ should "return 0 if there are no documents in the collection" do
464
+ @document.delete_all
465
+ @document.count.should == 0
466
+ end
467
+
468
+ should "return 0 if the collection does not exist" do
469
+ klass = Doc do
470
+ set_collection_name 'foobarbazwickdoesnotexist'
471
+ end
472
+
473
+ klass.count.should == 0
474
+ end
475
+
476
+ should "return count for matching documents if conditions provided" do
477
+ @document.count(:age => 27).should == 1
478
+ end
479
+
480
+ should "convert the conditions to mongo criteria" do
481
+ @document.count(:age => [26, 27]).should == 2
482
+ end
483
+ end
484
+
485
+ context ".exists?" do
486
+ setup do
487
+ @doc = @document.create(:first_name => "James", :age => 27)
488
+ end
489
+
490
+ should "be true when at least one document exists" do
491
+ @document.exists?.should == true
492
+ end
493
+
494
+ should "be false when no documents exist" do
495
+ @doc.destroy
496
+ @document.exists?.should == false
497
+ end
498
+
499
+ should "be true when at least one document exists that matches the conditions" do
500
+ @document.exists?(:first_name => "James").should == true
501
+ end
502
+
503
+ should "be false when no documents exist with the provided conditions" do
504
+ @document.exists?(:first_name => "Jean").should == false
505
+ end
506
+ end
507
+
508
+ context ".where" do
509
+ setup do
510
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
511
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
512
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
513
+ @query = @document.where(:last_name => 'Nunemaker')
514
+ end
515
+
516
+ should "fetch documents when kicker called" do
517
+ docs = @query.all
518
+ docs.should include(@doc1)
519
+ docs.should include(@doc3)
520
+ docs.should_not include(@doc2)
521
+ end
522
+
523
+ should "be chainable" do
524
+ @query.sort(:age).first.should == @doc3
525
+ end
526
+ end
527
+
528
+ context ".fields" do
529
+ setup do
530
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
531
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
532
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
533
+ @query = @document.fields(:age)
534
+ end
535
+
536
+ should "fetch documents when kicker called" do
537
+ docs = @query.all
538
+ docs.should include(@doc1)
539
+ docs.should include(@doc3)
540
+ docs.should include(@doc2)
541
+ docs.each do |doc|
542
+ doc.age.should_not be_nil
543
+ doc.first_name.should be_nil # key was not loaded
544
+ doc.last_name.should be_nil # key was not loaded
545
+ end
546
+ end
547
+
548
+ should "be chainable" do
549
+ @query.sort(:age).all.map(&:age).should == [26, 27, 28]
550
+ end
551
+ end
552
+
553
+ context ".limit" do
554
+ setup do
555
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
556
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
557
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
558
+ @query = @document.limit(2)
559
+ end
560
+
561
+ should "fetch documents when kicker called" do
562
+ docs = @query.all
563
+ docs.size.should == 2
564
+ end
565
+
566
+ should "be chainable" do
567
+ result = [26, 27]
568
+ @query.sort(:age).all.map(&:age).should == result
569
+ @query.count.should > result.size
570
+ end
571
+ end
572
+
573
+ context ".skip" do
574
+ setup do
575
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
576
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
577
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
578
+ @query = @document.skip(1)
579
+ end
580
+
581
+ should "fetch documents when kicker called" do
582
+ docs = @query.all
583
+ docs.size.should == 2 # skipping 1 out of 3
584
+ end
585
+
586
+ should "be chainable" do
587
+ result = [27, 28]
588
+ @query.sort(:age).all.map(&:age).should == result
589
+ @query.count.should > result.size
590
+ end
591
+ end
592
+
593
+ context ".sort" do
594
+ setup do
595
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
596
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
597
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
598
+ @query = @document.sort(:age)
599
+ end
600
+
601
+ should "fetch documents when kicker called" do
602
+ @query.all.should == [@doc3, @doc1, @doc2]
603
+ end
604
+
605
+ should "be chainable" do
606
+ result = [28]
607
+ @query.skip(2).all.map(&:age).should == result
608
+ @query.count.should > result.size
609
+ end
610
+ end
611
+
612
+ context "#update_attributes (new document)" do
613
+ setup do
614
+ @doc = @document.new(:first_name => 'John', :age => '27')
615
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
616
+ end
617
+
618
+ should "insert document into the collection" do
619
+ @document.count.should == 1
620
+ end
621
+
622
+ should "assign an id for the document" do
623
+ @doc.id.should be_instance_of(BSON::ObjectID)
624
+ end
625
+
626
+ should "save attributes" do
627
+ @doc.first_name.should == 'Johnny'
628
+ @doc.age.should == 30
629
+ end
630
+
631
+ should "update attributes in the database" do
632
+ doc = @doc.reload
633
+ doc.should == @doc
634
+ doc.first_name.should == 'Johnny'
635
+ doc.age.should == 30
636
+ end
637
+
638
+ should "allow updating custom attributes" do
639
+ @doc.update_attributes(:gender => 'mALe')
640
+ @doc.reload.gender.should == 'mALe'
641
+ end
642
+ end
643
+
644
+ context "#update_attributes (existing document)" do
645
+ setup do
646
+ @doc = @document.create(:first_name => 'John', :age => '27')
647
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
648
+ end
649
+
650
+ should "not insert document into collection" do
651
+ @document.count.should == 1
652
+ end
653
+
654
+ should "update attributes" do
655
+ @doc.first_name.should == 'Johnny'
656
+ @doc.age.should == 30
657
+ end
658
+
659
+ should "update attributes in the database" do
660
+ doc = @doc.reload
661
+ doc.first_name.should == 'Johnny'
662
+ doc.age.should == 30
663
+ end
664
+ end
665
+
666
+ context "#update_attributes (return value)" do
667
+ setup do
668
+ @document.key :foo, String, :required => true
669
+ end
670
+
671
+ should "be true if document valid" do
672
+ @document.new.update_attributes(:foo => 'bar').should be_true
673
+ end
674
+
675
+ should "be false if document not valid" do
676
+ @document.new.update_attributes({}).should be_false
677
+ end
678
+ end
679
+
680
+ context "#save (new document)" do
681
+ setup do
682
+ @doc = @document.new(:first_name => 'John', :age => '27')
683
+ @doc.save
684
+ end
685
+
686
+ should "insert document into the collection" do
687
+ @document.count.should == 1
688
+ end
689
+
690
+ should "assign an id for the document" do
691
+ @doc.id.should be_instance_of(BSON::ObjectID)
692
+ end
693
+
694
+ should "save attributes" do
695
+ @doc.first_name.should == 'John'
696
+ @doc.age.should == 27
697
+ end
698
+
699
+ should "update attributes in the database" do
700
+ doc = @doc.reload
701
+ doc.should == @doc
702
+ doc.first_name.should == 'John'
703
+ doc.age.should == 27
704
+ end
705
+
706
+ should "allow to add custom attributes to the document" do
707
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
708
+ @doc.save
709
+ doc = @doc.reload
710
+ doc.gender.should == 'male'
711
+ doc.tags.should == [1, "2"]
712
+ end
713
+
714
+ should "allow to use custom methods to assign properties" do
715
+ klass = Doc do
716
+ key :name, String
717
+
718
+ def realname=(value)
719
+ self.name = value
720
+ end
721
+ end
722
+
723
+ person = klass.new(:realname => 'David')
724
+ person.save
725
+ person.reload.name.should == 'David'
726
+ end
727
+
728
+ context "with key of type date" do
729
+ should "save the date value as a Time object" do
730
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "2009-12-01")
731
+ doc.save
732
+ doc.date.should == Date.new(2009, 12, 1)
733
+ end
734
+ end
735
+ end
736
+
737
+ context "#save (existing document)" do
738
+ setup do
739
+ @doc = @document.create(:first_name => 'John', :age => '27')
740
+ @doc.first_name = 'Johnny'
741
+ @doc.age = 30
742
+ @doc.save
743
+ end
744
+
745
+ should "not insert document into collection" do
746
+ @document.count.should == 1
747
+ end
748
+
749
+ should "update attributes" do
750
+ @doc.first_name.should == 'Johnny'
751
+ @doc.age.should == 30
752
+ end
753
+
754
+ should "update attributes in the database" do
755
+ doc = @doc.reload
756
+ doc.first_name.should == 'Johnny'
757
+ doc.age.should == 30
758
+ end
759
+
760
+ should "allow updating custom attributes" do
761
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
762
+ @doc.gender = 'Male'
763
+ @doc.save
764
+ @doc.reload.gender.should == 'Male'
765
+ end
766
+ end
767
+
768
+ context "#save (with validations off)" do
769
+ setup do
770
+ @document = Doc do
771
+ key :name, String, :required => true
772
+ end
773
+ end
774
+
775
+ should "insert invalid document" do
776
+ doc = @document.new
777
+ doc.expects(:valid?).never
778
+ doc.save(:validate => false)
779
+ @document.count.should == 1
780
+ end
781
+ end
782
+
783
+ context "#save (with options)" do
784
+ setup do
785
+ @document = Doc do
786
+ key :name, String
787
+ end
788
+ @document.ensure_index :name, :unique => true
789
+ end
790
+ teardown { drop_indexes(@document) }
791
+
792
+ should "allow passing safe" do
793
+ @document.create(:name => 'John')
794
+ assert_raises(Mongo::OperationFailure) do
795
+ @document.new(:name => 'John').save(:safe => true)
796
+ end
797
+ end
798
+
799
+ should "raise argument error if options has unsupported key" do
800
+ assert_raises(ArgumentError) do
801
+ @document.new.save(:foo => true)
802
+ end
803
+ end
804
+ end
805
+
806
+ context "#save! (with options)" do
807
+ setup do
808
+ @document = Doc { key :name, String }
809
+ @document.ensure_index :name, :unique => true
810
+ end
811
+ teardown { drop_indexes(@document) }
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
831
+ end
832
+
833
+ context "#destroy" do
834
+ setup do
835
+ @doc = @document.create(:first_name => 'John', :age => '27')
836
+ @doc.destroy
837
+ end
838
+
839
+ should "remove the document from the collection" do
840
+ @document.count.should == 0
841
+ end
842
+ end
843
+
844
+ context "#delete" do
845
+ setup do
846
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
847
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
848
+
849
+ @document.class_eval do
850
+ before_destroy :before_destroy_callback
851
+ after_destroy :after_destroy_callback
852
+
853
+ def history; @history ||= [] end
854
+ def before_destroy_callback; history << :after_destroy end
855
+ def after_destroy_callback; history << :after_destroy end
856
+ end
857
+
858
+ @doc1.delete
859
+ end
860
+
861
+ should "remove document from collection" do
862
+ @document.count.should == 1
863
+ end
864
+
865
+ should "not remove other documents" do
866
+ @document.find(@doc2.id).should_not be(nil)
867
+ end
868
+
869
+ should "not call before/after destroy callbacks" do
870
+ @doc1.history.should == []
871
+ end
872
+ end
873
+ end