mongoid 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/HISTORY +39 -0
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongoid.rb +3 -1
  5. data/lib/mongoid/associations.rb +5 -5
  6. data/lib/mongoid/associations/has_many.rb +8 -2
  7. data/lib/mongoid/associations/has_many_related.rb +10 -4
  8. data/lib/mongoid/attributes.rb +19 -13
  9. data/lib/mongoid/commands.rb +12 -6
  10. data/lib/mongoid/commands/create.rb +2 -2
  11. data/lib/mongoid/commands/delete_all.rb +1 -1
  12. data/lib/mongoid/commands/save.rb +2 -2
  13. data/lib/mongoid/components.rb +1 -0
  14. data/lib/mongoid/contexts.rb +4 -0
  15. data/lib/mongoid/contexts/enumerable.rb +105 -0
  16. data/lib/mongoid/contexts/mongo.rb +228 -0
  17. data/lib/mongoid/contexts/paging.rb +42 -0
  18. data/lib/mongoid/criteria.rb +42 -191
  19. data/lib/mongoid/document.rb +19 -13
  20. data/lib/mongoid/extensions.rb +1 -0
  21. data/lib/mongoid/extensions/array/accessors.rb +3 -1
  22. data/lib/mongoid/extensions/float/conversions.rb +1 -1
  23. data/lib/mongoid/extensions/hash/accessors.rb +1 -1
  24. data/lib/mongoid/extensions/integer/conversions.rb +1 -0
  25. data/lib/mongoid/fields.rb +6 -5
  26. data/lib/mongoid/matchers.rb +36 -0
  27. data/lib/mongoid/matchers/all.rb +11 -0
  28. data/lib/mongoid/matchers/default.rb +20 -0
  29. data/lib/mongoid/matchers/exists.rb +13 -0
  30. data/lib/mongoid/matchers/gt.rb +11 -0
  31. data/lib/mongoid/matchers/gte.rb +11 -0
  32. data/lib/mongoid/matchers/in.rb +11 -0
  33. data/lib/mongoid/matchers/lt.rb +11 -0
  34. data/lib/mongoid/matchers/lte.rb +11 -0
  35. data/lib/mongoid/matchers/ne.rb +11 -0
  36. data/lib/mongoid/matchers/nin.rb +11 -0
  37. data/lib/mongoid/matchers/size.rb +11 -0
  38. data/lib/mongoid/scope.rb +17 -1
  39. data/mongoid.gemspec +51 -5
  40. data/spec/integration/mongoid/associations_spec.rb +67 -5
  41. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  42. data/spec/integration/mongoid/commands_spec.rb +51 -12
  43. data/spec/integration/mongoid/criteria_spec.rb +3 -3
  44. data/spec/integration/mongoid/document_spec.rb +8 -8
  45. data/spec/integration/mongoid/finders_spec.rb +1 -1
  46. data/spec/integration/mongoid/inheritance_spec.rb +6 -0
  47. data/spec/integration/mongoid/named_scope_spec.rb +1 -1
  48. data/spec/spec_helper.rb +47 -6
  49. data/spec/unit/mongoid/associations/has_many_related_spec.rb +42 -0
  50. data/spec/unit/mongoid/associations/has_many_spec.rb +40 -1
  51. data/spec/unit/mongoid/attributes_spec.rb +1 -1
  52. data/spec/unit/mongoid/commands/create_spec.rb +3 -3
  53. data/spec/unit/mongoid/commands/delete_all_spec.rb +2 -2
  54. data/spec/unit/mongoid/commands/save_spec.rb +2 -2
  55. data/spec/unit/mongoid/commands_spec.rb +12 -12
  56. data/spec/unit/mongoid/contexts/enumerable_spec.rb +208 -0
  57. data/spec/unit/mongoid/contexts/mongo_spec.rb +370 -0
  58. data/spec/unit/mongoid/criteria_spec.rb +182 -21
  59. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +9 -9
  60. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +2 -1
  61. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +4 -4
  62. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +1 -1
  63. data/spec/unit/mongoid/fields_spec.rb +3 -3
  64. data/spec/unit/mongoid/identity_spec.rb +2 -2
  65. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  66. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  67. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  68. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  69. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  70. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  71. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  72. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  73. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  74. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  75. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  76. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  77. data/spec/unit/mongoid/scope_spec.rb +70 -0
  78. data/spec/unit/mongoid/timestamps_spec.rb +2 -2
  79. metadata +50 -4
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ class Mongo
5
+ include Paging
6
+ attr_reader :selector, :options, :klass
7
+
8
+ AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
9
+ # Aggregate the context. This will take the internally built selector and options
10
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
11
+ # collection itself will be retrieved from the class provided, and once the
12
+ # query has returned it will provided a grouping of keys with counts.
13
+ #
14
+ # Example:
15
+ #
16
+ # <tt>context.aggregate</tt>
17
+ #
18
+ # Returns:
19
+ #
20
+ # A +Hash+ with field values as keys, counts as values
21
+ def aggregate
22
+ @klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE, true)
23
+ end
24
+
25
+ # Get the count of matching documents in the database for the context.
26
+ #
27
+ # Example:
28
+ #
29
+ # <tt>context.count</tt>
30
+ #
31
+ # Returns:
32
+ #
33
+ # An +Integer+ count of documents.
34
+ def count
35
+ @count ||= @klass.collection.find(@selector, process_options).count
36
+ end
37
+
38
+ GROUP_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
39
+ # Groups the context. This will take the internally built selector and options
40
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
41
+ # collection itself will be retrieved from the class provided, and once the
42
+ # query has returned it will provided a grouping of keys with objects.
43
+ #
44
+ # Example:
45
+ #
46
+ # <tt>context.group</tt>
47
+ #
48
+ # Returns:
49
+ #
50
+ # A +Hash+ with field values as keys, arrays of documents as values.
51
+ def group
52
+ @klass.collection.group(
53
+ @options[:fields],
54
+ @selector,
55
+ { :group => [] },
56
+ GROUP_REDUCE,
57
+ true
58
+ ).collect do |docs|
59
+ docs["group"] = docs["group"].collect do |attrs|
60
+ instantiate(attrs)
61
+ end
62
+ docs
63
+ end
64
+ end
65
+
66
+ # Execute the context. This will take the selector and options
67
+ # and pass them on to the Ruby driver's +find()+ method on the collection. The
68
+ # collection itself will be retrieved from the class provided, and once the
69
+ # query has returned new documents of the type of class provided will be instantiated.
70
+ #
71
+ # Example:
72
+ #
73
+ # <tt>mongo.execute</tt>
74
+ #
75
+ # Returns:
76
+ #
77
+ # An +Array+ of documents
78
+ def execute
79
+ attributes = @klass.collection.find(@selector, process_options)
80
+ if attributes
81
+ @count = attributes.count
82
+ attributes.collect { |doc| instantiate(doc) }
83
+ else
84
+ []
85
+ end
86
+ end
87
+
88
+ # Create the new mongo context. This will execute the queries given the
89
+ # selector and options against the database.
90
+ #
91
+ # Example:
92
+ #
93
+ # <tt>Mongoid::Contexts::Mongo.new(selector, options, klass)</tt>
94
+ def initialize(selector, options, klass)
95
+ @selector, @options, @klass = selector, options, klass
96
+ if klass.hereditary
97
+ @hereditary = true
98
+ end
99
+ end
100
+
101
+ # Return the last result for the +Context+. Essentially does a find_one on
102
+ # the collection with the sorting reversed. If no sorting parameters have
103
+ # been provided it will default to ids.
104
+ #
105
+ # Example:
106
+ #
107
+ # <tt>context.last</tt>
108
+ #
109
+ # Returns:
110
+ #
111
+ # The last document in the collection.
112
+ def last
113
+ opts = process_options
114
+ sorting = opts[:sort]
115
+ sorting = [[:_id, :asc]] unless sorting
116
+ opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
117
+ attributes = @klass.collection.find_one(@selector, opts)
118
+ attributes ? instantiate(attributes) : nil
119
+ end
120
+
121
+ MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
122
+ "if (prev.max < obj.[field]) { prev.max = obj.[field]; } }"
123
+ # Return the max value for a field.
124
+ #
125
+ # This will take the internally built selector and options
126
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
127
+ # collection itself will be retrieved from the class provided, and once the
128
+ # query has returned it will provided a grouping of keys with sums.
129
+ #
130
+ # Example:
131
+ #
132
+ # <tt>context.max(:age)</tt>
133
+ #
134
+ # Returns:
135
+ #
136
+ # A numeric max value.
137
+ def max(field)
138
+ grouped(:max, field.to_s, MAX_REDUCE)
139
+ end
140
+
141
+ MIN_REDUCE = "function(obj, prev) { if (prev.min == 'start') { prev.min = obj.[field]; } " +
142
+ "if (prev.min > obj.[field]) { prev.min = obj.[field]; } }"
143
+ # Return the min value for a field.
144
+ #
145
+ # This will take the internally built selector and options
146
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
147
+ # collection itself will be retrieved from the class provided, and once the
148
+ # query has returned it will provided a grouping of keys with sums.
149
+ #
150
+ # Example:
151
+ #
152
+ # <tt>context.min(:age)</tt>
153
+ #
154
+ # Returns:
155
+ #
156
+ # A numeric minimum value.
157
+ def min(field)
158
+ grouped(:min, field.to_s, MIN_REDUCE)
159
+ end
160
+
161
+ # Return the first result for the +Context+.
162
+ #
163
+ # Example:
164
+ #
165
+ # <tt>context.one</tt>
166
+ #
167
+ # Return:
168
+ #
169
+ # The first document in the collection.
170
+ def one
171
+ attributes = @klass.collection.find_one(@selector, process_options)
172
+ attributes ? instantiate(attributes) : nil
173
+ end
174
+
175
+ alias :first :one
176
+
177
+ SUM_REDUCE = "function(obj, prev) { if (prev.sum == 'start') { prev.sum = 0; } prev.sum += obj.[field]; }"
178
+ # Sum the context.
179
+ #
180
+ # This will take the internally built selector and options
181
+ # and pass them on to the Ruby driver's +group()+ method on the collection. The
182
+ # collection itself will be retrieved from the class provided, and once the
183
+ # query has returned it will provided a grouping of keys with sums.
184
+ #
185
+ # Example:
186
+ #
187
+ # <tt>context.sum(:age)</tt>
188
+ #
189
+ # Returns:
190
+ #
191
+ # A numeric value that is the sum.
192
+ def sum(field)
193
+ grouped(:sum, field.to_s, SUM_REDUCE)
194
+ end
195
+
196
+ protected
197
+ # Common functionality for grouping operations. Currently used by min, max
198
+ # and sum. Will gsub the field name in the supplied reduce function.
199
+ def grouped(start, field, reduce)
200
+ collection = @klass.collection.group(
201
+ nil,
202
+ @selector,
203
+ { start => "start" },
204
+ reduce.gsub("[field]", field),
205
+ true
206
+ )
207
+ collection.first[start.to_s]
208
+ end
209
+
210
+ # If hereditary instantiate by _type otherwise use the klass.
211
+ def instantiate(attrs)
212
+ @hereditary ? attrs["_type"].constantize.instantiate(attrs) : @klass.instantiate(attrs)
213
+ end
214
+
215
+ # Filters the field list. If no fields have been supplied, then it will be
216
+ # empty. If fields have been defined then _type will be included as well.
217
+ def process_options
218
+ fields = @options[:fields]
219
+ if fields && fields.size > 0 && !fields.include?(:_type)
220
+ fields << :_type
221
+ @options[:fields] = fields
222
+ end
223
+ @options.dup
224
+ end
225
+
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Contexts #:nodoc:
4
+ module Paging
5
+ # Paginates the documents.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>context.paginate</tt>
10
+ #
11
+ # Returns:
12
+ #
13
+ # A collection of documents paginated.
14
+ def paginate
15
+ @collection ||= execute
16
+ WillPaginate::Collection.create(page, per_page, count) do |pager|
17
+ pager.replace(@collection)
18
+ end
19
+ end
20
+
21
+ # Either returns the page option and removes it from the options, or
22
+ # returns a default value of 1.
23
+ #
24
+ # Returns:
25
+ #
26
+ # An +Integer+ page number.
27
+ def page
28
+ skips, limits = @options[:skip], @options[:limit]
29
+ (skips && limits) ? (skips + limits) / limits : 1
30
+ end
31
+
32
+ # Get the number of results per page or the default of 20.
33
+ #
34
+ # Returns:
35
+ #
36
+ # The +Integer+ number of documents in each page.
37
+ def per_page
38
+ (@options[:limit] || 20).to_i
39
+ end
40
+ end
41
+ end
42
+ end
@@ -17,8 +17,36 @@ module Mongoid #:nodoc:
17
17
  class Criteria
18
18
  include Enumerable
19
19
 
20
+ attr_accessor :documents
20
21
  attr_reader :klass, :options, :selector
21
22
 
23
+ delegate \
24
+ :aggregate,
25
+ :count,
26
+ :execute,
27
+ :first,
28
+ :group,
29
+ :last,
30
+ :max,
31
+ :min,
32
+ :one,
33
+ :page,
34
+ :paginate,
35
+ :per_page,
36
+ :sum, :to => :context
37
+
38
+ # Concatinate the criteria with another enumerable. If the other is a
39
+ # +Criteria+ then it needs to get the collection from it.
40
+ def +(other)
41
+ entries + (other.is_a?(Criteria) ? other.entries : other)
42
+ end
43
+
44
+ # Returns the difference between the criteria and another enumerable. If
45
+ # the other is a +Criteria+ then it needs to get the collection from it.
46
+ def -(other)
47
+ entries - (other.is_a?(Criteria) ? other.entries : other)
48
+ end
49
+
22
50
  # Returns true if the supplied +Enumerable+ or +Criteria+ is equal to the results
23
51
  # of this +Criteria+ or the criteria itself.
24
52
  #
@@ -39,19 +67,6 @@ module Mongoid #:nodoc:
39
67
  end
40
68
  end
41
69
 
42
- AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
43
- # Aggregate the criteria. This will take the internally built selector and options
44
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
45
- # collection itself will be retrieved from the class provided, and once the
46
- # query has returned it will provided a grouping of keys with counts.
47
- #
48
- # Example:
49
- #
50
- # <tt>criteria.select(:field1).where(:field1 => "Title").aggregate(Person)</tt>
51
- def aggregate
52
- @klass.collection.group(@options[:fields], @selector, { :count => 0 }, AGGREGATE_REDUCE, true)
53
- end
54
-
55
70
  # Adds a criterion to the +Criteria+ that specifies values that must all
56
71
  # be matched in order to return results. Similar to an "in" clause but the
57
72
  # underlying conditional logic is an "AND" and not an "OR". The MongoDB
@@ -92,15 +107,12 @@ module Mongoid #:nodoc:
92
107
  where(selector)
93
108
  end
94
109
 
95
- # Get the count of matching documents in the database for the +Criteria+.
96
- #
97
- # Example:
98
- #
99
- # <tt>criteria.count</tt>
110
+ # Return or create the context in which this criteria should be executed.
100
111
  #
101
- # Returns: <tt>Integer</tt>
102
- def count
103
- @count ||= @klass.collection.find(@selector, process_options).count
112
+ # This will return an Enumerable context if the class is embedded,
113
+ # otherwise it will return a Mongo context for root classes.
114
+ def context
115
+ @context ||= determine_context
104
116
  end
105
117
 
106
118
  # Merges the supplied argument hash into a single criteria
@@ -167,30 +179,6 @@ module Mongoid #:nodoc:
167
179
  @options = extras; filter_options; self
168
180
  end
169
181
 
170
- GROUP_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
171
- # Groups the criteria. This will take the internally built selector and options
172
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
173
- # collection itself will be retrieved from the class provided, and once the
174
- # query has returned it will provided a grouping of keys with objects.
175
- #
176
- # Example:
177
- #
178
- # <tt>criteria.select(:field1).where(:field1 => "Title").group(Person)</tt>
179
- def group
180
- @klass.collection.group(
181
- @options[:fields],
182
- @selector,
183
- { :group => [] },
184
- GROUP_REDUCE,
185
- true
186
- ).collect do |docs|
187
- docs["group"] = docs["group"].collect do |attrs|
188
- instantiate(attrs)
189
- end
190
- docs
191
- end
192
- end
193
-
194
182
  # Adds a criterion to the +Criteria+ that specifies values where any can
195
183
  # be matched in order to return results. This is similar to an SQL "IN"
196
184
  # clause. The MongoDB conditional operator that will be used is "$in".
@@ -234,29 +222,13 @@ module Mongoid #:nodoc:
234
222
  # type: One of :all, :first:, or :last
235
223
  # klass: The class to execute on.
236
224
  def initialize(klass)
237
- @selector, @options, @klass = {}, {}, klass
225
+ @selector, @options, @klass, @documents = {}, {}, klass, []
238
226
  if klass.hereditary
239
227
  @selector = { :_type => { "$in" => klass._types } }
240
228
  @hereditary = true
241
229
  end
242
230
  end
243
231
 
244
- # Return the last result for the +Criteria+. Essentially does a find_one on
245
- # the collection with the sorting reversed. If no sorting parameters have
246
- # been provided it will default to ids.
247
- #
248
- # Example:
249
- #
250
- # <tt>Criteria.select(:name).where(:name = "Chrissy").last</tt>
251
- def last
252
- opts = process_options
253
- sorting = opts[:sort]
254
- sorting = [[:_id, :asc]] unless sorting
255
- opts[:sort] = sorting.collect { |option| [ option[0], option[1].invert ] }
256
- attributes = @klass.collection.find_one(@selector, opts)
257
- attributes ? instantiate(attributes) : nil
258
- end
259
-
260
232
  # Adds a criterion to the +Criteria+ that specifies the maximum number of
261
233
  # results to return. This is mostly used in conjunction with <tt>skip()</tt>
262
234
  # to handle paginated results.
@@ -274,38 +246,6 @@ module Mongoid #:nodoc:
274
246
  @options[:limit] = value; self
275
247
  end
276
248
 
277
- MIN_REDUCE = "function(obj, prev) { if (prev.min == 'start') { prev.min = obj.[field]; } " +
278
- "if (prev.min > obj.[field]) { prev.min = obj.[field]; } }"
279
- # Return the min value for a field.
280
- #
281
- # This will take the internally built selector and options
282
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
283
- # collection itself will be retrieved from the class provided, and once the
284
- # query has returned it will provided a grouping of keys with sums.
285
- #
286
- # Example:
287
- #
288
- # <tt>criteria.min(:age)</tt>
289
- def min(field)
290
- grouped(:min, field.to_s, MIN_REDUCE)
291
- end
292
-
293
- MAX_REDUCE = "function(obj, prev) { if (prev.max == 'start') { prev.max = obj.[field]; } " +
294
- "if (prev.max < obj.[field]) { prev.max = obj.[field]; } }"
295
- # Return the max value for a field.
296
- #
297
- # This will take the internally built selector and options
298
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
299
- # collection itself will be retrieved from the class provided, and once the
300
- # query has returned it will provided a grouping of keys with sums.
301
- #
302
- # Example:
303
- #
304
- # <tt>criteria.max(:age)</tt>
305
- def max(field)
306
- grouped(:max, field.to_s, MAX_REDUCE)
307
- end
308
-
309
249
  # Merges another object into this +Criteria+. The other object may be a
310
250
  # +Criteria+ or a +Hash+. This is used to combine multiple scopes together,
311
251
  # where a chained scope situation may be desired.
@@ -320,6 +260,7 @@ module Mongoid #:nodoc:
320
260
  def merge(other)
321
261
  @selector.update(other.selector)
322
262
  @options.update(other.options)
263
+ @documents = other.documents
323
264
  end
324
265
 
325
266
  # Used for chaining +Criteria+ scopes together in the for of class methods
@@ -357,7 +298,7 @@ module Mongoid #:nodoc:
357
298
  new_scope.merge(self)
358
299
  return new_scope
359
300
  else
360
- return collect.send(name, *args)
301
+ return entries.send(name, *args)
361
302
  end
362
303
  end
363
304
 
@@ -388,18 +329,6 @@ module Mongoid #:nodoc:
388
329
  @options[:skip]
389
330
  end
390
331
 
391
- # Return the first result for the +Criteria+.
392
- #
393
- # Example:
394
- #
395
- # <tt>Criteria.select(:name).where(:name = "Chrissy").one</tt>
396
- def one
397
- attributes = @klass.collection.find_one(@selector, process_options)
398
- attributes ? instantiate(attributes) : nil
399
- end
400
-
401
- alias :first :one
402
-
403
332
  # Adds a criterion to the +Criteria+ that specifies the fields that will
404
333
  # get returned from the Document. Used mainly for list views that do not
405
334
  # require all fields to be present. This is similar to SQL "SELECT" values.
@@ -433,30 +362,6 @@ module Mongoid #:nodoc:
433
362
  @options[:sort] = params; self
434
363
  end
435
364
 
436
- # Either returns the page option and removes it from the options, or
437
- # returns a default value of 1.
438
- def page
439
- skips, limits = @options[:skip], @options[:limit]
440
- (skips && limits) ? (skips + limits) / limits : 1
441
- end
442
-
443
- # Executes the +Criteria+ and paginates the results.
444
- #
445
- # Example:
446
- #
447
- # <tt>criteria.paginate</tt>
448
- def paginate
449
- @collection ||= execute
450
- WillPaginate::Collection.create(page, per_page, count) do |pager|
451
- pager.replace(@collection)
452
- end
453
- end
454
-
455
- # Returns the number of results per page or the default of 20.
456
- def per_page
457
- (@options[:limit] || 20).to_i
458
- end
459
-
460
365
  # Returns the selector and options as a +Hash+ that would be passed to a
461
366
  # scope for use with named scopes.
462
367
  def scoped
@@ -481,20 +386,7 @@ module Mongoid #:nodoc:
481
386
  @options[:skip] = value; self
482
387
  end
483
388
 
484
- SUM_REDUCE = "function(obj, prev) { if (prev.sum == 'start') { prev.sum = 0; } prev.sum += obj.[field]; }"
485
- # Sum the criteria.
486
- #
487
- # This will take the internally built selector and options
488
- # and pass them on to the Ruby driver's +group()+ method on the collection. The
489
- # collection itself will be retrieved from the class provided, and once the
490
- # query has returned it will provided a grouping of keys with sums.
491
- #
492
- # Example:
493
- #
494
- # <tt>criteria.sum(:age)</tt>
495
- def sum(field)
496
- grouped(:sum, field.to_s, SUM_REDUCE)
497
- end
389
+ alias :to_ary :to_a
498
390
 
499
391
  # Translate the supplied arguments into a +Criteria+ object.
500
392
  #
@@ -554,24 +446,12 @@ module Mongoid #:nodoc:
554
446
  end
555
447
 
556
448
  protected
557
- # Execute the criteria. This will take the internally built selector and options
558
- # and pass them on to the Ruby driver's +find()+ method on the collection. The
559
- # collection itself will be retrieved from the class provided, and once the
560
- # query has returned new documents of the type of class provided will be instantiated.
561
- #
562
- # If this is a +Criteria+ to only find the first object, this will return a
563
- # single object of the type of class provided.
564
- #
565
- # If this is a +Criteria+ to find multiple results, will return an +Array+ of
566
- # objects of the type of class provided.
567
- def execute
568
- attributes = @klass.collection.find(@selector, process_options)
569
- if attributes
570
- @count = attributes.count
571
- attributes.collect { |doc| instantiate(doc) }
572
- else
573
- []
449
+ # Determines the context to be used for this criteria.
450
+ def determine_context
451
+ if @klass.embedded
452
+ return Contexts::Enumerable.new(@selector, @options, @documents)
574
453
  end
454
+ Contexts::Mongo.new(@selector, @options, @klass)
575
455
  end
576
456
 
577
457
  # Filters the unused options out of the options +Hash+. Currently this
@@ -586,35 +466,6 @@ module Mongoid #:nodoc:
586
466
  end
587
467
  end
588
468
 
589
- # Common functionality for grouping operations. Currently used by min, max
590
- # and sum. Will gsub the field name in the supplied reduce function.
591
- def grouped(start, field, reduce)
592
- collection = @klass.collection.group(
593
- nil,
594
- @selector,
595
- { start => "start" },
596
- reduce.gsub("[field]", field),
597
- true
598
- )
599
- collection.first[start.to_s]
600
- end
601
-
602
- # If hereditary instantiate by _type otherwise use the klass.
603
- def instantiate(attrs)
604
- @hereditary ? attrs["_type"].constantize.instantiate(attrs) : @klass.instantiate(attrs)
605
- end
606
-
607
- # Filters the field list. If no fields have been supplied, then it will be
608
- # empty. If fields have been defined then _type will be included as well.
609
- def process_options
610
- fields = @options[:fields]
611
- if fields && fields.size > 0 && !fields.include?(:_type)
612
- fields << :_type
613
- @options[:fields] = fields
614
- end
615
- @options.dup
616
- end
617
-
618
469
  # Update the selector setting the operator on the value for each key in the
619
470
  # supplied attributes +Hash+.
620
471
  def update_selector(attributes, operator)