mongoid 1.9.5 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (213) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +29 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/caliper.yml +4 -0
  6. data/lib/mongoid.rb +20 -9
  7. data/lib/mongoid/associations.rb +100 -123
  8. data/lib/mongoid/associations/belongs_to_related.rb +3 -2
  9. data/lib/mongoid/associations/{embeds_many.rb → embed_many.rb} +29 -90
  10. data/lib/mongoid/associations/{embeds_one.rb → embed_one.rb} +7 -8
  11. data/lib/mongoid/associations/embedded_in.rb +4 -12
  12. data/lib/mongoid/associations/has_many_related.rb +4 -52
  13. data/lib/mongoid/associations/has_one_related.rb +4 -8
  14. data/lib/mongoid/associations/meta_data.rb +1 -2
  15. data/lib/mongoid/associations/options.rb +1 -6
  16. data/lib/mongoid/associations/proxy.rb +21 -14
  17. data/lib/mongoid/attributes.rb +13 -33
  18. data/lib/mongoid/callbacks.rb +11 -16
  19. data/lib/mongoid/collection.rb +3 -4
  20. data/lib/mongoid/collections/master.rb +2 -3
  21. data/lib/mongoid/collections/mimic.rb +46 -0
  22. data/lib/mongoid/collections/slaves.rb +2 -3
  23. data/lib/mongoid/commands.rb +161 -0
  24. data/lib/mongoid/commands/create.rb +19 -0
  25. data/lib/mongoid/commands/delete.rb +16 -0
  26. data/lib/mongoid/commands/delete_all.rb +23 -0
  27. data/lib/mongoid/commands/deletion.rb +18 -0
  28. data/lib/mongoid/commands/destroy.rb +17 -0
  29. data/lib/mongoid/commands/destroy_all.rb +23 -0
  30. data/lib/mongoid/commands/save.rb +29 -0
  31. data/lib/mongoid/components.rb +6 -4
  32. data/lib/mongoid/config.rb +15 -134
  33. data/lib/mongoid/contexts.rb +1 -1
  34. data/lib/mongoid/contexts/enumerable.rb +1 -1
  35. data/lib/mongoid/contexts/mongo.rb +1 -1
  36. data/lib/mongoid/contexts/paging.rb +2 -10
  37. data/lib/mongoid/criterion/inclusion.rb +0 -17
  38. data/lib/mongoid/criterion/optional.rb +1 -1
  39. data/lib/mongoid/cursor.rb +1 -0
  40. data/lib/mongoid/document.rb +45 -49
  41. data/lib/mongoid/errors.rb +1 -32
  42. data/lib/mongoid/extensions.rb +10 -12
  43. data/lib/mongoid/extensions/array/conversions.rb +6 -8
  44. data/lib/mongoid/extensions/big_decimal/conversions.rb +2 -2
  45. data/lib/mongoid/extensions/boolean/conversions.rb +2 -8
  46. data/lib/mongoid/extensions/date/conversions.rb +4 -13
  47. data/lib/mongoid/extensions/datetime/conversions.rb +6 -1
  48. data/lib/mongoid/extensions/float/conversions.rb +1 -5
  49. data/lib/mongoid/extensions/hash/assimilation.rb +3 -12
  50. data/lib/mongoid/extensions/hash/conversions.rb +4 -34
  51. data/lib/mongoid/extensions/integer/conversions.rb +1 -5
  52. data/lib/mongoid/extensions/nil/assimilation.rb +0 -4
  53. data/lib/mongoid/extensions/object/conversions.rb +2 -8
  54. data/lib/mongoid/extensions/objectid/conversions.rb +1 -1
  55. data/lib/mongoid/extensions/string/conversions.rb +1 -1
  56. data/lib/mongoid/extensions/symbol/inflections.rb +1 -1
  57. data/lib/mongoid/extensions/time/conversions.rb +18 -0
  58. data/lib/mongoid/factory.rb +1 -2
  59. data/lib/mongoid/field.rb +2 -9
  60. data/lib/mongoid/fields.rb +7 -11
  61. data/lib/mongoid/finders.rb +2 -2
  62. data/lib/mongoid/identity.rb +4 -4
  63. data/lib/mongoid/indexes.rb +7 -10
  64. data/lib/mongoid/memoization.rb +2 -8
  65. data/lib/mongoid/named_scope.rb +5 -0
  66. data/lib/mongoid/observable.rb +1 -1
  67. data/lib/mongoid/paths.rb +22 -30
  68. data/lib/mongoid/state.rb +21 -28
  69. data/lib/mongoid/timestamps.rb +1 -1
  70. data/lib/mongoid/validations.rb +51 -0
  71. data/lib/mongoid/validations/associated.rb +32 -0
  72. data/lib/mongoid/validations/locale/en.yml +4 -0
  73. data/lib/mongoid/validations/uniqueness.rb +22 -0
  74. data/lib/mongoid/versioning.rb +1 -2
  75. data/mongoid.gemspec +408 -0
  76. data/perf/benchmark.rb +77 -0
  77. data/spec/integration/mongoid/associations_spec.rb +340 -0
  78. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  79. data/spec/integration/mongoid/commands_spec.rb +227 -0
  80. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  81. data/spec/integration/mongoid/criteria_spec.rb +272 -0
  82. data/spec/integration/mongoid/document_spec.rb +650 -0
  83. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  84. data/spec/integration/mongoid/finders_spec.rb +119 -0
  85. data/spec/integration/mongoid/inheritance_spec.rb +137 -0
  86. data/spec/integration/mongoid/named_scope_spec.rb +46 -0
  87. data/spec/models/address.rb +39 -0
  88. data/spec/models/animal.rb +6 -0
  89. data/spec/models/callbacks.rb +18 -0
  90. data/spec/models/comment.rb +8 -0
  91. data/spec/models/country_code.rb +6 -0
  92. data/spec/models/employer.rb +5 -0
  93. data/spec/models/game.rb +7 -0
  94. data/spec/models/inheritance.rb +56 -0
  95. data/spec/models/location.rb +5 -0
  96. data/spec/models/mixed_drink.rb +4 -0
  97. data/spec/models/name.rb +13 -0
  98. data/spec/models/namespacing.rb +11 -0
  99. data/spec/models/patient.rb +4 -0
  100. data/spec/models/person.rb +99 -0
  101. data/spec/models/pet.rb +7 -0
  102. data/spec/models/pet_owner.rb +6 -0
  103. data/spec/models/phone.rb +7 -0
  104. data/spec/models/post.rb +15 -0
  105. data/spec/models/translation.rb +5 -0
  106. data/spec/models/vet_visit.rb +5 -0
  107. data/spec/spec.opts +3 -0
  108. data/spec/spec_helper.rb +31 -0
  109. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +145 -0
  110. data/spec/unit/mongoid/associations/embed_many_spec.rb +516 -0
  111. data/spec/unit/mongoid/associations/embed_one_spec.rb +282 -0
  112. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  113. data/spec/unit/mongoid/associations/has_many_related_spec.rb +418 -0
  114. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  115. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  116. data/spec/unit/mongoid/associations/options_spec.rb +192 -0
  117. data/spec/unit/mongoid/associations_spec.rb +595 -0
  118. data/spec/unit/mongoid/attributes_spec.rb +507 -0
  119. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  120. data/spec/unit/mongoid/collection_spec.rb +187 -0
  121. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  122. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  123. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  124. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  125. data/spec/unit/mongoid/commands/create_spec.rb +31 -0
  126. data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
  127. data/spec/unit/mongoid/commands/delete_spec.rb +38 -0
  128. data/spec/unit/mongoid/commands/destroy_all_spec.rb +21 -0
  129. data/spec/unit/mongoid/commands/destroy_spec.rb +51 -0
  130. data/spec/unit/mongoid/commands/save_spec.rb +107 -0
  131. data/spec/unit/mongoid/commands_spec.rb +270 -0
  132. data/spec/unit/mongoid/config_spec.rb +172 -0
  133. data/spec/unit/mongoid/contexts/enumerable_spec.rb +421 -0
  134. data/spec/unit/mongoid/contexts/mongo_spec.rb +682 -0
  135. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  136. data/spec/unit/mongoid/criteria_spec.rb +824 -0
  137. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  138. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  139. data/spec/unit/mongoid/criterion/inclusion_spec.rb +219 -0
  140. data/spec/unit/mongoid/criterion/optional_spec.rb +319 -0
  141. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  142. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  143. data/spec/unit/mongoid/document_spec.rb +818 -0
  144. data/spec/unit/mongoid/errors_spec.rb +103 -0
  145. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  146. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  147. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  148. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  149. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +22 -0
  150. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  151. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  152. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  153. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +67 -0
  154. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  155. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  156. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  157. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  158. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  159. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  160. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  161. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  162. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +57 -0
  163. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  164. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  165. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  166. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  167. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  168. data/spec/unit/mongoid/extras_spec.rb +102 -0
  169. data/spec/unit/mongoid/factory_spec.rb +31 -0
  170. data/spec/unit/mongoid/field_spec.rb +143 -0
  171. data/spec/unit/mongoid/fields_spec.rb +181 -0
  172. data/spec/unit/mongoid/finders_spec.rb +404 -0
  173. data/spec/unit/mongoid/identity_spec.rb +109 -0
  174. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  175. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  176. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  177. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  178. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  179. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  180. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  181. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  182. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  183. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  184. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  185. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  186. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  187. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  188. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  189. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  190. data/spec/unit/mongoid/observable_spec.rb +46 -0
  191. data/spec/unit/mongoid/paths_spec.rb +124 -0
  192. data/spec/unit/mongoid/scope_spec.rb +240 -0
  193. data/spec/unit/mongoid/state_spec.rb +83 -0
  194. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  195. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  196. data/spec/unit/mongoid/validations/uniqueness_spec.rb +47 -0
  197. data/spec/unit/mongoid/validations_spec.rb +190 -0
  198. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  199. data/spec/unit/mongoid_spec.rb +46 -0
  200. metadata +316 -110
  201. data/lib/mongoid/collections.rb +0 -41
  202. data/lib/mongoid/concern.rb +0 -31
  203. data/lib/mongoid/dirty.rb +0 -253
  204. data/lib/mongoid/extensions/time_conversions.rb +0 -35
  205. data/lib/mongoid/persistence.rb +0 -222
  206. data/lib/mongoid/persistence/command.rb +0 -39
  207. data/lib/mongoid/persistence/insert.rb +0 -50
  208. data/lib/mongoid/persistence/insert_embedded.rb +0 -38
  209. data/lib/mongoid/persistence/remove.rb +0 -39
  210. data/lib/mongoid/persistence/remove_all.rb +0 -37
  211. data/lib/mongoid/persistence/remove_embedded.rb +0 -50
  212. data/lib/mongoid/persistence/update.rb +0 -63
  213. data/lib/mongoid/version.rb +0 -4
@@ -0,0 +1,682 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Contexts::Mongo do
4
+
5
+ describe "#aggregate" do
6
+
7
+ before do
8
+ @criteria = Mongoid::Criteria.new(Person)
9
+ @criteria.only(:field1)
10
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
11
+ end
12
+
13
+ context "when klass not provided" do
14
+
15
+ before do
16
+ @reduce = "function(obj, prev) { prev.count++; }"
17
+ @collection = mock
18
+ Person.expects(:collection).returns(@collection)
19
+ end
20
+
21
+ it "calls group on the collection with the aggregate js" do
22
+ @collection.expects(:group).with([:field1], {:_type => {'$in' => ['Doctor', 'Person']}}, {:count => 0}, @reduce, true)
23
+ @context.aggregate
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "#avg" do
29
+
30
+ before do
31
+ @reduce = Mongoid::Javascript.sum.gsub("[field]", "age")
32
+ @collection = mock
33
+ Person.expects(:collection).twice.returns(@collection)
34
+ @criteria = Mongoid::Criteria.new(Person)
35
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
36
+ end
37
+
38
+ it "calls group on the collection with the aggregate js" do
39
+ @collection.expects(:group).with(
40
+ nil,
41
+ {:_type => {'$in' => ['Doctor', 'Person']}},
42
+ {:sum => "start"},
43
+ @reduce
44
+ ).returns([{"sum" => 100.0}])
45
+ @cursor = mock(:count => 10)
46
+ @collection.expects(:find).returns(@cursor)
47
+ @context.avg(:age).should == 10
48
+ end
49
+ end
50
+
51
+ describe "blank?" do
52
+
53
+ before do
54
+ @criteria = Mongoid::Criteria.new(Game)
55
+ @criteria.where(:test => 'Testing')
56
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
57
+ end
58
+
59
+ context "when a document exists" do
60
+
61
+ before do
62
+ @doc = mock
63
+ @collection = mock
64
+ Game.expects(:collection).returns(@collection)
65
+ @collection.expects(:find_one).with({ :test => "Testing" }, { :fields => [ :_id ] }).returns(@doc)
66
+ end
67
+
68
+ it "returns false" do
69
+ @context.blank?.should be_false
70
+ end
71
+ end
72
+
73
+ context "when a document does not exist" do
74
+
75
+ before do
76
+ @doc = mock
77
+ @collection = mock
78
+ Game.expects(:collection).returns(@collection)
79
+ @collection.expects(:find_one).with({ :test => "Testing" }, { :fields => [ :_id ] }).returns(nil)
80
+ end
81
+
82
+ it "returns true" do
83
+ @context.blank?.should be_true
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#count" do
89
+
90
+ before do
91
+ @criteria = Mongoid::Criteria.new(Person)
92
+ @criteria.where(:test => 'Testing')
93
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
94
+ end
95
+
96
+ context "when criteria has not been executed" do
97
+
98
+ before do
99
+ @context.instance_variable_set(:@count, 34)
100
+ end
101
+
102
+ it "returns a count from the cursor" do
103
+ @context.count.should == 34
104
+ end
105
+
106
+ end
107
+
108
+ context "when criteria has been executed" do
109
+
110
+ before do
111
+ @collection = mock
112
+ @cursor = mock
113
+ Person.expects(:collection).returns(@collection)
114
+ end
115
+
116
+ it "returns the count from the cursor without creating the documents" do
117
+ @collection.expects(:find).with(@criteria.selector, {}).returns(@cursor)
118
+ @cursor.expects(:count).returns(10)
119
+ @context.count.should == 10
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ describe "#distinct" do
127
+
128
+ before do
129
+ @criteria = Mongoid::Criteria.new(Person)
130
+ @criteria.where(:test => 'Testing')
131
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
132
+ @collection = mock
133
+ Person.expects(:collection).returns(@collection)
134
+ end
135
+
136
+ it "returns delegates to distinct on the collection" do
137
+ @collection.expects(:distinct).with(:title, @criteria.selector).returns(["Sir"])
138
+ @context.distinct(:title).should == ["Sir"]
139
+ end
140
+ end
141
+
142
+ describe "#execute" do
143
+
144
+ let(:selector) { { :field => "value" } }
145
+ let(:options) { { :skip => 20 } }
146
+
147
+ before do
148
+ @cursor = stub(:count => 500)
149
+ @collection = mock
150
+ @klass = stub(
151
+ :collection => @collection,
152
+ :hereditary => false,
153
+ :instantiate => @person,
154
+ :enslaved? => false,
155
+ :cached? => false
156
+ )
157
+ @criteria = Mongoid::Criteria.new(@klass)
158
+ @criteria.where(selector).skip(20)
159
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
160
+ end
161
+
162
+ it "calls find on the collection" do
163
+ @collection.expects(:find).with(selector, options).returns(@cursor)
164
+ @context.execute.should == @cursor
165
+ end
166
+
167
+ context "when paginating" do
168
+
169
+ it "should find the count from the cursor" do
170
+ @collection.expects(:find).with(selector, options).returns(@cursor)
171
+ @context.execute(true).should == @cursor
172
+ @context.count.should == 500
173
+ end
174
+
175
+ end
176
+
177
+ context "when field options are supplied" do
178
+
179
+ context "when _type not in the field list" do
180
+
181
+ before do
182
+ @criteria.only(:title)
183
+ @expected_options = { :skip => 20, :fields => [ :title, :_type ] }
184
+ end
185
+
186
+ it "adds _type to the fields" do
187
+ @collection.expects(:find).with(selector, @expected_options).returns(@cursor)
188
+ @context.execute.should == @cursor
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+
197
+ describe "#group" do
198
+
199
+ before do
200
+ @criteria = Mongoid::Criteria.new(Person)
201
+ @criteria.only(:field1)
202
+ @grouping = [{ "title" => "Sir", "group" => [{ "title" => "Sir", "age" => 30, "_type" => "Person" }] }]
203
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
204
+ end
205
+
206
+ context "when klass provided" do
207
+
208
+ before do
209
+ @reduce = "function(obj, prev) { prev.group.push(obj); }"
210
+ @collection = mock
211
+ Person.expects(:collection).returns(@collection)
212
+ end
213
+
214
+ it "calls group on the collection with the aggregate js" do
215
+ @collection.expects(:group).with(
216
+ [:field1],
217
+ {:_type => { "$in" => ["Doctor", "Person"] }},
218
+ {:group => []},
219
+ @reduce
220
+ ).returns(@grouping)
221
+ @context.group
222
+ end
223
+
224
+ end
225
+
226
+ end
227
+
228
+ describe ".initialize" do
229
+
230
+ let(:selector) { { :field => "value" } }
231
+ let(:options) { { :skip => 20 } }
232
+ let(:klass) { Person }
233
+ let(:criteria) { Mongoid::Criteria.new(klass) }
234
+ let(:context) { Mongoid::Contexts::Mongo.new(criteria) }
235
+
236
+ before do
237
+ criteria.where(selector).skip(20)
238
+ end
239
+
240
+ it "sets the selector" do
241
+ context.selector.should == criteria.selector
242
+ end
243
+
244
+ it "sets the options" do
245
+ context.options.should == options
246
+ end
247
+
248
+ it "sets the klass" do
249
+ context.klass.should == klass
250
+ end
251
+
252
+ context "when persisting type" do
253
+
254
+ it "set the selector to query across the _type when it is hereditary" do
255
+ context.selector[:_type].should == {'$in' => Person._types}
256
+ end
257
+
258
+ end
259
+
260
+ context "when not persisting type" do
261
+
262
+ before do
263
+ Mongoid.persist_types = false
264
+ end
265
+
266
+ after do
267
+ Mongoid.persist_types = true
268
+ end
269
+
270
+ it "does not add the type to the selector" do
271
+ context.selector[:_type].should be_nil
272
+ end
273
+
274
+ end
275
+
276
+ context "enslaved and cached classes" do
277
+
278
+ let(:klass) { Game }
279
+
280
+ it "enslaves the criteria" do
281
+ context.criteria.should be_enslaved
282
+ end
283
+
284
+ it "caches the criteria" do
285
+ context.criteria.should be_cached
286
+ end
287
+ end
288
+ end
289
+
290
+ describe "#iterate" do
291
+ before do
292
+ @criteria = Mongoid::Criteria.new(Person)
293
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
294
+ @person = Person.new(:title => "Sir")
295
+ @cursor = stub('cursor')
296
+ @cursor.stubs(:each).yields(@person)
297
+ end
298
+
299
+ context "when not caching" do
300
+
301
+ it "executes the criteria" do
302
+ @context.expects(:execute).returns(@cursor)
303
+ @context.iterate do |person|
304
+ person.should == @person
305
+ end
306
+
307
+ end
308
+
309
+ end
310
+
311
+ context "when caching" do
312
+ before do
313
+ @criteria.cache
314
+ end
315
+
316
+ it "executes the criteria" do
317
+ @context.expects(:execute).returns(@cursor)
318
+ @context.iterate do |person|
319
+ person.should == @person
320
+ end
321
+ end
322
+
323
+ it "executes only once and it caches the result" do
324
+ @context.expects(:execute).once.returns(@cursor)
325
+ @context.iterate do |person|
326
+ person.should == @person
327
+ end
328
+ @context.iterate do |person|
329
+ person.should == @person
330
+ end
331
+ end
332
+
333
+ it "executes even if there is no block" do
334
+ @context.expects(:execute).once.returns(@cursor)
335
+ @context.iterate
336
+ end
337
+
338
+ it "caches even if there is no block" do
339
+ @context.expects(:execute).once.returns(@cursor)
340
+ @context.iterate
341
+ @context.iterate do |person|
342
+ person.should == @person
343
+ end
344
+ end
345
+ end
346
+
347
+ end
348
+
349
+ describe "#last" do
350
+
351
+ before do
352
+ @collection = mock
353
+ Person.expects(:collection).returns(@collection)
354
+ end
355
+
356
+ context "when documents exist" do
357
+
358
+ before do
359
+ @criteria = Mongoid::Criteria.new(Person)
360
+ @criteria.order_by([[:title, :asc]])
361
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
362
+ @collection.expects(:find_one).with(
363
+ {:_type => {'$in' => ['Doctor', 'Person']}},
364
+ { :sort => [[:title, :desc]] }
365
+ ).returns(
366
+ { "title" => "Sir", "_type" => "Person" }
367
+ )
368
+ end
369
+
370
+ it "calls find on the collection with the selector and sort options reversed" do
371
+ @context.last.should be_a_kind_of(Person)
372
+ end
373
+
374
+ end
375
+
376
+ context "when no documents exist" do
377
+
378
+ before do
379
+ @criteria = Mongoid::Criteria.new(Person)
380
+ @criteria.order_by([[:_id, :asc]])
381
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
382
+ @collection.expects(:find_one).with(
383
+ {:_type => {'$in' => ['Doctor', 'Person']}},
384
+ { :sort => [[:_id, :desc]] }
385
+ ).returns(nil)
386
+ end
387
+
388
+ it "returns nil" do
389
+ @context.last.should be_nil
390
+ end
391
+
392
+ end
393
+
394
+ context "when no sorting options provided" do
395
+
396
+ before do
397
+ @criteria = Mongoid::Criteria.new(Person)
398
+ @criteria.order_by([[:_id, :asc]])
399
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
400
+ @collection.expects(:find_one).with(
401
+ {:_type => {'$in' => ['Doctor', 'Person']}},
402
+ { :sort => [[:_id, :desc]] }
403
+ ).returns(
404
+ { "title" => "Sir", "_type" => "Person" }
405
+ )
406
+ end
407
+
408
+ it "defaults to sort by id" do
409
+ @context.last
410
+ end
411
+
412
+ end
413
+
414
+ end
415
+
416
+ describe "#max" do
417
+
418
+ before do
419
+ @reduce = Mongoid::Javascript.max.gsub("[field]", "age")
420
+ @collection = mock
421
+ Person.expects(:collection).returns(@collection)
422
+ @criteria = Mongoid::Criteria.new(Person)
423
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
424
+ end
425
+
426
+ it "calls group on the collection with the aggregate js" do
427
+ @collection.expects(:group).with(
428
+ nil,
429
+ {:_type => {'$in' => ['Doctor', 'Person']}},
430
+ {:max => "start"},
431
+ @reduce
432
+ ).returns([{"max" => 200.0}])
433
+ @context.max(:age).should == 200.0
434
+ end
435
+
436
+ end
437
+
438
+ describe "#min" do
439
+
440
+ before do
441
+ @reduce = Mongoid::Javascript.min.gsub("[field]", "age")
442
+ @collection = mock
443
+ Person.expects(:collection).returns(@collection)
444
+ @criteria = Mongoid::Criteria.new(Person)
445
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
446
+ end
447
+
448
+ it "calls group on the collection with the aggregate js" do
449
+ @collection.expects(:group).with(
450
+ nil,
451
+ {:_type => {'$in' => ['Doctor', 'Person']}},
452
+ {:min => "start"},
453
+ @reduce
454
+ ).returns([{"min" => 4.0}])
455
+ @context.min(:age).should == 4.0
456
+ end
457
+
458
+ end
459
+
460
+ describe "#one" do
461
+
462
+ context "when documents exist" do
463
+
464
+ before do
465
+ Person.expects(:collection).returns(@collection)
466
+ @criteria = Mongoid::Criteria.new(Person)
467
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
468
+ @collection.expects(:find_one).with({:_type => {'$in' => ['Doctor', 'Person']}}, {}).returns(
469
+ { "title"=> "Sir", "_type" => "Person" }
470
+ )
471
+ end
472
+
473
+ it "calls find on the collection with the selector and options" do
474
+ @context.one.should be_a_kind_of(Person)
475
+ end
476
+
477
+ end
478
+
479
+ context "when no documents exist" do
480
+
481
+ before do
482
+ @collection = mock
483
+ Person.expects(:collection).returns(@collection)
484
+ @criteria = Mongoid::Criteria.new(Person)
485
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
486
+ @collection.expects(:find_one).with({:_type => {'$in' => ['Doctor', 'Person']}}, {}).returns(nil)
487
+ end
488
+
489
+ it "returns nil" do
490
+ @context.one.should be_nil
491
+ end
492
+
493
+ end
494
+
495
+ end
496
+
497
+ describe "#page" do
498
+
499
+ context "when the page option exists" do
500
+
501
+ before do
502
+ @criteria = Mongoid::Criteria.new(Person).extras({ :page => 5 })
503
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
504
+ end
505
+
506
+ it "returns the page option" do
507
+ @context.page.should == 5
508
+ end
509
+
510
+ end
511
+
512
+ context "when the page option does not exist" do
513
+
514
+ before do
515
+ @criteria = Mongoid::Criteria.new(Person)
516
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
517
+ end
518
+
519
+ it "returns 1" do
520
+ @context.page.should == 1
521
+ end
522
+
523
+ end
524
+
525
+ end
526
+
527
+ describe "#paginate" do
528
+
529
+ before do
530
+ @collection = mock
531
+ Person.expects(:collection).returns(@collection)
532
+ @criteria = Person.where(:_id => "1").skip(60).limit(20)
533
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
534
+ @collection.expects(:find).with(
535
+ {:_type => { "$in" => ["Doctor", "Person"] }, :_id => "1"}, :skip => 60, :limit => 20
536
+ ).returns([])
537
+ @results = @context.paginate
538
+ end
539
+
540
+ it "executes and paginates the results" do
541
+ @results.current_page.should == 4
542
+ @results.per_page.should == 20
543
+ end
544
+
545
+ end
546
+
547
+ describe "#sum" do
548
+
549
+ context "when klass not provided" do
550
+
551
+ before do
552
+ @reduce = Mongoid::Javascript.sum.gsub("[field]", "age")
553
+ @collection = mock
554
+ @criteria = Mongoid::Criteria.new(Person)
555
+ @context = Mongoid::Contexts::Mongo.new(@criteria)
556
+ Person.expects(:collection).returns(@collection)
557
+ end
558
+
559
+ it "calls group on the collection with the aggregate js" do
560
+ @collection.expects(:group).with(
561
+ nil,
562
+ {:_type => {'$in' => ['Doctor', 'Person']}},
563
+ {:sum => "start"},
564
+ @reduce
565
+ ).returns([{"sum" => 50.0}])
566
+ @context.sum(:age).should == 50.0
567
+ end
568
+
569
+ end
570
+
571
+ end
572
+
573
+ context "#id_criteria" do
574
+
575
+ let(:criteria) { Mongoid::Criteria.new(Person) }
576
+ let(:context) { criteria.context }
577
+
578
+ context "with a single argument" do
579
+
580
+ let(:id) { Mongo::ObjectID.new.to_s }
581
+
582
+ before do
583
+ criteria.expects(:id).with(id).returns(criteria)
584
+ end
585
+
586
+ context "when the document is found" do
587
+
588
+ let(:document) { stub }
589
+
590
+ it "returns a matching document" do
591
+ context.expects(:one).returns(document)
592
+ document.expects(:blank? => false)
593
+ context.id_criteria(id).should == document
594
+ end
595
+
596
+ end
597
+
598
+ context "when the document is not found" do
599
+
600
+ it "raises an error" do
601
+ context.expects(:one).returns(nil)
602
+ lambda { context.id_criteria(id) }.should raise_error
603
+ end
604
+
605
+ end
606
+
607
+ end
608
+
609
+ context "multiple arguments" do
610
+
611
+ context "when an array of ids" do
612
+
613
+ let(:ids) do
614
+ (0..2).inject([]) { |ary, i| ary << Mongo::ObjectID.new.to_s }
615
+ end
616
+
617
+ context "when documents are found" do
618
+
619
+ let(:docs) do
620
+ (0..2).inject([]) { |ary, i| ary << stub }
621
+ end
622
+
623
+ before do
624
+ criteria.expects(:id).with(ids).returns(criteria)
625
+ end
626
+
627
+ it "returns matching documents" do
628
+ context.expects(:execute).returns(docs)
629
+ context.id_criteria(ids).should == docs
630
+ end
631
+
632
+ end
633
+
634
+ context "when documents are not found" do
635
+
636
+ it "raises an error" do
637
+ context.expects(:execute).returns([])
638
+ lambda { context.id_criteria(ids) }.should raise_error
639
+ end
640
+
641
+ end
642
+
643
+ end
644
+
645
+ context "when an array of object ids" do
646
+
647
+ let(:ids) do
648
+ (0..2).inject([]) { |ary, i| ary << Mongo::ObjectID.new }
649
+ end
650
+
651
+ context "when documents are found" do
652
+
653
+ let(:docs) do
654
+ (0..2).inject([]) { |ary, i| ary << stub }
655
+ end
656
+
657
+ before do
658
+ criteria.expects(:id).with(ids).returns(criteria)
659
+ end
660
+
661
+ it "returns matching documents" do
662
+ context.expects(:execute).returns(docs)
663
+ context.id_criteria(ids).should == docs
664
+ end
665
+
666
+ end
667
+
668
+ context "when documents are not found" do
669
+
670
+ it "raises an error" do
671
+ context.expects(:execute).returns([])
672
+ lambda { context.id_criteria(ids) }.should raise_error
673
+ end
674
+
675
+ end
676
+
677
+ end
678
+ end
679
+
680
+ end
681
+
682
+ end