mongo_mapper-unstable 2010.3.8 → 2010.06.23

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 (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