mongoid 2.0.0.rc.7 → 2.0.0.rc.8

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 (90) hide show
  1. data/lib/config/locales/en.yml +3 -0
  2. data/lib/config/locales/id.yml +46 -0
  3. data/lib/config/locales/ja.yml +40 -0
  4. data/lib/config/locales/vi.yml +45 -0
  5. data/lib/mongoid.rb +5 -3
  6. data/lib/mongoid/attributes.rb +24 -63
  7. data/lib/mongoid/attributes/processing.rb +5 -2
  8. data/lib/mongoid/callbacks.rb +10 -0
  9. data/lib/mongoid/collection.rb +24 -0
  10. data/lib/mongoid/collections/master.rb +14 -6
  11. data/lib/mongoid/collections/operations.rb +1 -1
  12. data/lib/mongoid/collections/retry.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +26 -10
  14. data/lib/mongoid/components.rb +4 -4
  15. data/lib/mongoid/config.rb +6 -3
  16. data/lib/mongoid/contexts.rb +0 -1
  17. data/lib/mongoid/contexts/enumerable.rb +19 -7
  18. data/lib/mongoid/contexts/mongo.rb +9 -5
  19. data/lib/mongoid/copyable.rb +10 -8
  20. data/lib/mongoid/criteria.rb +83 -61
  21. data/lib/mongoid/criterion/builder.rb +34 -0
  22. data/lib/mongoid/criterion/creational.rb +2 -2
  23. data/lib/mongoid/criterion/exclusion.rb +58 -32
  24. data/lib/mongoid/criterion/inclusion.rb +49 -10
  25. data/lib/mongoid/criterion/optional.rb +1 -1
  26. data/lib/mongoid/criterion/selector.rb +80 -11
  27. data/lib/mongoid/cursor.rb +6 -1
  28. data/lib/mongoid/default_scope.rb +27 -19
  29. data/lib/mongoid/document.rb +26 -1
  30. data/lib/mongoid/errors.rb +1 -0
  31. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  32. data/lib/mongoid/extensions/object_id/conversions.rb +7 -4
  33. data/lib/mongoid/factory.rb +1 -1
  34. data/lib/mongoid/field.rb +47 -30
  35. data/lib/mongoid/fields.rb +9 -2
  36. data/lib/mongoid/finders.rb +15 -49
  37. data/lib/mongoid/identity.rb +6 -4
  38. data/lib/mongoid/keys.rb +85 -31
  39. data/lib/mongoid/multi_parameter_attributes.rb +2 -2
  40. data/lib/mongoid/named_scope.rb +129 -28
  41. data/lib/mongoid/observer.rb +36 -0
  42. data/lib/mongoid/paranoia.rb +3 -3
  43. data/lib/mongoid/paths.rb +1 -1
  44. data/lib/mongoid/persistence.rb +2 -0
  45. data/lib/mongoid/persistence/atomic.rb +88 -0
  46. data/lib/mongoid/persistence/atomic/add_to_set.rb +30 -0
  47. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  48. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  49. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  50. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  51. data/lib/mongoid/railtie.rb +13 -1
  52. data/lib/mongoid/relations.rb +1 -0
  53. data/lib/mongoid/relations/accessors.rb +20 -2
  54. data/lib/mongoid/relations/builders/embedded/one.rb +1 -0
  55. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  56. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  57. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  58. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  59. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  60. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  61. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  62. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  63. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  64. data/lib/mongoid/relations/embedded/many.rb +57 -25
  65. data/lib/mongoid/relations/macros.rb +6 -4
  66. data/lib/mongoid/relations/many.rb +51 -10
  67. data/lib/mongoid/relations/metadata.rb +4 -2
  68. data/lib/mongoid/relations/proxy.rb +39 -24
  69. data/lib/mongoid/relations/referenced/many.rb +47 -26
  70. data/lib/mongoid/relations/referenced/many_to_many.rb +47 -14
  71. data/lib/mongoid/relations/referenced/one.rb +14 -0
  72. data/lib/mongoid/sharding.rb +51 -0
  73. data/lib/mongoid/state.rb +3 -2
  74. data/lib/mongoid/timestamps.rb +5 -29
  75. data/lib/mongoid/timestamps/created.rb +31 -0
  76. data/lib/mongoid/timestamps/updated.rb +33 -0
  77. data/lib/mongoid/validations.rb +10 -3
  78. data/lib/mongoid/validations/referenced.rb +58 -0
  79. data/lib/mongoid/version.rb +1 -1
  80. data/lib/mongoid/versioning.rb +67 -5
  81. data/lib/rails/generators/mongoid/model/templates/model.rb +2 -0
  82. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  83. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  84. data/lib/rails/generators/mongoid_generator.rb +10 -1
  85. data/lib/rails/mongoid.rb +1 -0
  86. metadata +29 -8
  87. data/lib/mongoid/contexts/ids.rb +0 -25
  88. data/lib/mongoid/modifiers.rb +0 -24
  89. data/lib/mongoid/modifiers/command.rb +0 -18
  90. data/lib/mongoid/modifiers/inc.rb +0 -24
@@ -1,35 +1,44 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Collections #:nodoc:
4
+
5
+ # This class wraps the MongoDB slaves databases.
4
6
  class Slaves
7
+ include Mongoid::Collections::Retry
5
8
 
6
9
  attr_reader :iterator
7
10
 
8
11
  # All read operations should delegate to the slave connections.
9
12
  # These operations mimic the methods on a Mongo:Collection.
10
13
  #
11
- # Example:
12
- #
13
- # <tt>collection.save({ :name => "Al" })</tt>
14
+ # @example Proxy the driver save.
15
+ # collection.save({ :name => "Al" })
14
16
  Operations::READ.each do |name|
15
- define_method(name) { |*args| collection.send(name, *args) }
17
+ define_method(name) do |*args|
18
+ retry_on_connection_failure do
19
+ collection.send(name, *args)
20
+ end
21
+ end
16
22
  end
17
23
 
18
24
  # Is the collection of slaves empty or not?
19
25
  #
20
- # Return:
26
+ # @example Is the collection empty?
27
+ # slaves.empty?
21
28
  #
22
- # True is the iterator is not set, false if not.
29
+ # @return [ true, false ] If the iterator is set or not.
23
30
  def empty?
24
- @iterator.nil?
31
+ iterator.nil?
25
32
  end
26
33
 
27
34
  # Create the new database reader. Will create a collection from the
28
35
  # slave databases and cycle through them on each read.
29
36
  #
30
- # Example:
37
+ # @example Create the slaves.
38
+ # Reader.new(slaves, "mongoid_people")
31
39
  #
32
- # <tt>Reader.new(slaves, "mongoid_people")</tt>
40
+ # @param [ Array<Mongo::DB> ] slaves The slave databases.
41
+ # @param [ String ] name The database name.
33
42
  def initialize(slaves, name)
34
43
  unless slaves.blank?
35
44
  @iterator = CyclicIterator.new(slaves.collect { |db| db.collection(name) })
@@ -37,8 +46,15 @@ module Mongoid #:nodoc:
37
46
  end
38
47
 
39
48
  protected
49
+
50
+ # Get the next database in the round-robin.
51
+ #
52
+ # @example Get the next database.
53
+ # slaves.collection
54
+ #
55
+ # @return [ Mongo::DB ] The next slave database to read from.
40
56
  def collection
41
- @iterator.next
57
+ iterator.next
42
58
  end
43
59
  end
44
60
  end
@@ -7,20 +7,20 @@ module Mongoid #:nodoc
7
7
  # module, to keep the document class from getting too cluttered.
8
8
  included do
9
9
  extend ActiveModel::Translation
10
- extend Mongoid::DefaultScope
11
10
  extend Mongoid::Finders
12
- extend Mongoid::NamedScope
13
11
  end
14
12
 
15
13
  include ActiveModel::Conversion
16
14
  include ActiveModel::MassAssignmentSecurity
17
15
  include ActiveModel::Naming
16
+ include ActiveModel::Observing
18
17
  include ActiveModel::Serializers::JSON
19
18
  include ActiveModel::Serializers::Xml
20
19
  include Mongoid::Atomicity
21
20
  include Mongoid::Attributes
22
21
  include Mongoid::Collections
23
22
  include Mongoid::Copyable
23
+ include Mongoid::DefaultScope
24
24
  include Mongoid::Dirty
25
25
  include Mongoid::Extras
26
26
  include Mongoid::Fields
@@ -30,14 +30,14 @@ module Mongoid #:nodoc
30
30
  include Mongoid::JSON
31
31
  include Mongoid::Keys
32
32
  include Mongoid::Matchers
33
- include Mongoid::MultiParameterAttributes
34
- include Mongoid::Modifiers
33
+ include Mongoid::NamedScope
35
34
  include Mongoid::NestedAttributes
36
35
  include Mongoid::Paths
37
36
  include Mongoid::Persistence
38
37
  include Mongoid::Relations
39
38
  include Mongoid::Safety
40
39
  include Mongoid::Serialization
40
+ include Mongoid::Sharding
41
41
  include Mongoid::State
42
42
  include Mongoid::Validations
43
43
  include Mongoid::Callbacks
@@ -11,6 +11,7 @@ module Mongoid #:nodoc
11
11
  # @todo Durran: This module needs an overhaul, remove singleton, etc.
12
12
  module Config
13
13
  extend self
14
+ include ActiveModel::Observing
14
15
 
15
16
  attr_accessor :master, :slaves, :settings
16
17
  @settings = {}
@@ -35,15 +36,17 @@ module Mongoid #:nodoc
35
36
  end
36
37
 
37
38
  option :allow_dynamic_fields, :default => true
39
+ option :autocreate_indexes, :default => false
40
+ option :binding_defaults, :default => { :binding => false, :continue => true }
41
+ option :embedded_object_id, :default => true
38
42
  option :include_root_in_json, :default => false
39
43
  option :parameterize_keys, :default => true
40
44
  option :persist_in_safe_mode, :default => false
45
+ option :preload_models, :default => true
41
46
  option :raise_not_found_error, :default => true
42
- option :reconnect_time, :default => 3
43
- option :autocreate_indexes, :default => false
44
47
  option :skip_version_check, :default => false
45
48
  option :time_zone, :default => nil
46
- option :binding_defaults, :default => { :binding => false, :continue => true }
49
+ option :max_retries_on_connection_failure, :default => 0
47
50
 
48
51
  # Adds a new I18n locale file to the load path.
49
52
  #
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require "mongoid/contexts/ids"
3
2
  require "mongoid/contexts/paging"
4
3
  require "mongoid/contexts/enumerable"
5
4
  require "mongoid/contexts/mongo"
@@ -1,15 +1,16 @@
1
1
  # encoding: utf-8
2
-
3
2
  require 'mongoid/contexts/enumerable/sort'
4
3
 
5
4
  module Mongoid #:nodoc:
6
5
  module Contexts #:nodoc:
7
6
  class Enumerable
8
- include Ids, Paging
9
- attr_accessor :criteria
7
+ include Paging
8
+ include Relations::Embedded::Atomic
9
+
10
+ attr_accessor :collection, :criteria
10
11
 
11
12
  delegate :blank?, :empty?, :first, :last, :to => :execute
12
- delegate :klass, :documents, :options, :selector, :to => :criteria
13
+ delegate :klass, :documents, :options, :field_list, :selector, :to => :criteria
13
14
 
14
15
  # Return aggregation counts of the grouped documents. This will count by
15
16
  # the first field provided in the fields array.
@@ -51,7 +52,10 @@ module Mongoid #:nodoc:
51
52
  #
52
53
  # @since 2.0.0.rc.1
53
54
  def delete_all
54
- count.tap { filter.each(&:delete) }
55
+ atomically(:$pull) do
56
+ set_collection
57
+ count.tap { filter.each(&:delete) }
58
+ end
55
59
  end
56
60
  alias :delete :delete_all
57
61
 
@@ -64,7 +68,10 @@ module Mongoid #:nodoc:
64
68
  #
65
69
  # @since 2.0.0.rc.1
66
70
  def destroy_all
67
- count.tap { filter.each(&:destroy) }
71
+ atomically(:$pull) do
72
+ set_collection
73
+ count.tap { filter.each(&:destroy) }
74
+ end
68
75
  end
69
76
  alias :destroy :destroy_all
70
77
 
@@ -94,7 +101,7 @@ module Mongoid #:nodoc:
94
101
  #
95
102
  # A +Hash+ with field values as keys, arrays of documents as values.
96
103
  def group
97
- field = options[:fields].first
104
+ field = field_list.first
98
105
  execute.group_by { |doc| doc.send(field) }
99
106
  end
100
107
 
@@ -212,6 +219,11 @@ module Mongoid #:nodoc:
212
219
  documents
213
220
  end
214
221
 
222
+ def set_collection
223
+ root = documents.first._root
224
+ @collection = root.collection if root && !root.embedded?
225
+ end
226
+
215
227
  # Sorts the result set if sort options have been set.
216
228
  def sort(documents)
217
229
  return documents if options[:sort].blank?
@@ -2,10 +2,10 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Contexts #:nodoc:
4
4
  class Mongo
5
- include Ids, Paging
5
+ include Paging
6
6
  attr_accessor :criteria
7
7
 
8
- delegate :klass, :options, :selector, :to => :criteria
8
+ delegate :klass, :options, :field_list, :selector, :to => :criteria
9
9
 
10
10
  # Aggregate the context. This will take the internally built selector and options
11
11
  # and pass them on to the Ruby driver's +group()+ method on the collection. The
@@ -21,7 +21,7 @@ module Mongoid #:nodoc:
21
21
  # A +Hash+ with field values as keys, counts as values
22
22
  def aggregate
23
23
  klass.collection.group(
24
- :key => options[:fields],
24
+ :key => field_list,
25
25
  :cond => selector,
26
26
  :initial => { :count => 0 },
27
27
  :reduce => Javascript.aggregate
@@ -146,7 +146,7 @@ module Mongoid #:nodoc:
146
146
  # A +Hash+ with field values as keys, arrays of documents as values.
147
147
  def group
148
148
  klass.collection.group(
149
- :key => options[:fields],
149
+ :key => field_list,
150
150
  :cond => selector,
151
151
  :initial => { :group => [] },
152
152
  :reduce => Javascript.group
@@ -304,7 +304,11 @@ module Mongoid #:nodoc:
304
304
  def process_options
305
305
  fields = options[:fields]
306
306
  if fields && fields.size > 0 && !fields.include?(:_type)
307
- fields << :_type
307
+ if fields.kind_of?(Hash)
308
+ fields[:_type] = 1 if fields.first.last != 0 # Not excluding
309
+ else
310
+ fields << :type
311
+ end
308
312
  options[:fields] = fields
309
313
  end
310
314
  options.dup
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # This module contains the behaviour of Mongoid's clone/dup of documents.
3
5
  module Copyable
4
6
  extend ActiveSupport::Concern
5
7
 
@@ -17,19 +19,19 @@ module Mongoid #:nodoc:
17
19
  # the exception of the document's id and versions, and will reset all the
18
20
  # instance variables.
19
21
  #
20
- # Example:
21
- #
22
- # <tt>document.clone</tt>
23
- # <tt>document.dup</tt>
22
+ # This clone also includes embedded documents.
24
23
  #
25
- # Options:
24
+ # @example Clone the document.
25
+ # document.clone
26
26
  #
27
- # other: The document getting cloned.
27
+ # @example Dup the document.
28
+ # document.dup
28
29
  #
29
- # Returns:
30
+ # @param [ Document ] other The document getting cloned.
30
31
  #
31
- # A new document with all the attributes except id and versions
32
+ # @return [ Document ] The new document.
32
33
  def initialize_copy(other)
34
+ @attributes = other.as_document
33
35
  instance_variables.each { |name| remove_instance_variable(name) }
34
36
  COPYABLES.each do |name|
35
37
  value = other.instance_variable_get(name)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require "mongoid/criterion/builder"
2
3
  require "mongoid/criterion/creational"
3
4
  require "mongoid/criterion/complex"
4
5
  require "mongoid/criterion/exclusion"
@@ -25,13 +26,22 @@ module Mongoid #:nodoc:
25
26
  # <tt>criteria.execute</tt>
26
27
  class Criteria
27
28
  include Enumerable
29
+ include Criterion::Builder
28
30
  include Criterion::Creational
29
31
  include Criterion::Exclusion
30
32
  include Criterion::Inclusion
31
33
  include Criterion::Inspection
32
34
  include Criterion::Optional
33
35
 
34
- attr_accessor :collection, :documents, :embedded, :ids, :klass, :options, :selector
36
+ attr_accessor \
37
+ :collection,
38
+ :documents,
39
+ :embedded,
40
+ :ids,
41
+ :klass,
42
+ :options,
43
+ :selector,
44
+ :field_list
35
45
 
36
46
  delegate \
37
47
  :aggregate,
@@ -47,7 +57,6 @@ module Mongoid #:nodoc:
47
57
  :execute,
48
58
  :first,
49
59
  :group,
50
- :id_criteria,
51
60
  :last,
52
61
  :max,
53
62
  :min,
@@ -118,6 +127,20 @@ module Mongoid #:nodoc:
118
127
  context.count > 0
119
128
  end
120
129
 
130
+ # When freezing a criteria we need to initialize the context first
131
+ # otherwise the setting of the context on attempted iteration will raise a
132
+ # runtime error.
133
+ #
134
+ # @example Freeze the criteria.
135
+ # criteria.freeze
136
+ #
137
+ # @return [ Criteria ] The frozen criteria.
138
+ #
139
+ # @since 2.0.0
140
+ def freeze
141
+ context and super
142
+ end
143
+
121
144
  # Merges the supplied argument hash into a single criteria
122
145
  #
123
146
  # Options:
@@ -160,9 +183,15 @@ module Mongoid #:nodoc:
160
183
  # <tt>criteria.merge({ :conditions => { :title => "Sir" } })</tt>
161
184
  def merge(other)
162
185
  clone.tap do |crit|
163
- crit.selector.update(other.selector)
164
- crit.options.update(other.options)
165
- crit.documents = other.documents
186
+ if other.is_a?(Criteria)
187
+ crit.selector.update(other.selector)
188
+ crit.options.update(other.options)
189
+ crit.documents = other.documents
190
+ else
191
+ duped = other.dup
192
+ crit.selector.update(duped.delete(:conditions) || {})
193
+ crit.options.update(duped)
194
+ end
166
195
  end
167
196
  end
168
197
 
@@ -207,62 +236,53 @@ module Mongoid #:nodoc:
207
236
  entries.as_json(options)
208
237
  end
209
238
 
210
- class << self
211
-
212
- # Encaspulates the behavior of taking arguments and parsing them into a
213
- # finder type and a corresponding criteria object.
214
- #
215
- # Example:
216
- #
217
- # <tt>Criteria.parse!(Person, :all, :conditions => {})</tt>
218
- #
219
- # Options:
220
- #
221
- # klass: The klass to create the criteria for.
222
- # args: An assortment of finder options.
223
- #
224
- # Returns:
225
- #
226
- # An Array with the type and criteria.
227
- def parse!(klass, embedded, *args)
228
- if args[0].nil?
229
- Errors::InvalidOptions.new("Calling Document#find with nil is invalid")
230
- end
231
- type = args.delete_at(0) if args[0].is_a?(Symbol)
232
- criteria = translate(klass, embedded, *args)
233
- return [ type, criteria ]
239
+ # Search for documents based on a variety of args.
240
+ #
241
+ # @example Find by an id.
242
+ # criteria.search(BSON::ObjectId.new)
243
+ #
244
+ # @example Find by multiple ids.
245
+ # criteria.search([ BSON::ObjectId.new, BSON::ObjectId.new ])
246
+ #
247
+ # @example Conditionally find all matching documents.
248
+ # criteria.search(:all, :conditions => { :title => "Sir" })
249
+ #
250
+ # @example Conditionally find the first document.
251
+ # criteria.search(:first, :conditions => { :title => "Sir" })
252
+ #
253
+ # @example Conditionally find the last document.
254
+ # criteria.search(:last, :conditions => { :title => "Sir" })
255
+ #
256
+ # @param [ Symbol, BSON::ObjectId, Array<BSON::ObjectId> ] arg The
257
+ # argument to search with.
258
+ # @param [ Hash ] options The options to search with.
259
+ #
260
+ # @return [ Array<Symbol, Criteria> ] The type and criteria.
261
+ #
262
+ # @since 2.0.0
263
+ def search(*args)
264
+ raise_invalid if args[0].nil?
265
+ type = args[0]
266
+ params = args[1] || {}
267
+ return [ :ids, for_ids(type) ] unless type.is_a?(Symbol)
268
+ conditions = params.delete(:conditions) || {}
269
+ if conditions.include?(:id)
270
+ conditions[:_id] = conditions[:id]
271
+ conditions.delete(:id)
234
272
  end
273
+ return [ type, where(conditions).extras(params) ]
274
+ end
235
275
 
236
- # Translate the supplied arguments into a +Criteria+ object.
237
- #
238
- # If the passed in args is a single +String+, then it will
239
- # construct an id +Criteria+ from it.
240
- #
241
- # If the passed in args are a type and a hash, then it will construct
242
- # the +Criteria+ with the proper selector, options, and type.
243
- #
244
- # Options:
245
- #
246
- # args: either a +String+ or a +Symbol+, +Hash combination.
247
- #
248
- # Example:
249
- #
250
- # <tt>Criteria.translate(Person, "4ab2bc4b8ad548971900005c")</tt>
251
- # <tt>Criteria.translate(Person, :conditions => { :field => "value"}, :limit => 20)</tt>
252
- def translate(*args)
253
- klass = args[0]
254
- embedded = args[1]
255
- params = args[2] || {}
256
- unless params.is_a?(Hash)
257
- return klass.criteria(embedded).id_criteria(params)
258
- end
259
- conditions = params.delete(:conditions) || {}
260
- if conditions.include?(:id)
261
- conditions[:_id] = conditions[:id]
262
- conditions.delete(:id)
263
- end
264
- return klass.criteria(embedded).where(conditions).extras(params)
265
- end
276
+ # Convenience method of raising an invalid options error.
277
+ #
278
+ # @example Raise the error.
279
+ # criteria.raise_invalid
280
+ #
281
+ # @raise [ Errors::InvalidOptions ] The error.
282
+ #
283
+ # @since 2.0.0
284
+ def raise_invalid
285
+ raise Errors::InvalidOptions.new(:calling_document_find_with_nil_is_invalid, {})
266
286
  end
267
287
 
268
288
  protected
@@ -318,7 +338,9 @@ module Mongoid #:nodoc:
318
338
  # Example:
319
339
  #
320
340
  # <tt>criteria.update_selector({ :field => "value" }, "$in")</tt>
321
- def update_selector(attributes, operator)
341
+ #
342
+ # @param [ Symbol ] combine The operator to use when combining sets.
343
+ def update_selector(attributes, operator, combine = :+)
322
344
  clone.tap do |crit|
323
345
  converted = BSON::ObjectId.convert(klass, attributes || {})
324
346
  converted.each do |key, value|
@@ -326,7 +348,7 @@ module Mongoid #:nodoc:
326
348
  crit.selector[key] = { operator => value }
327
349
  else
328
350
  if crit.selector[key].has_key?(operator)
329
- new_value = crit.selector[key].values.first + value
351
+ new_value = crit.selector[key].values.first.send(combine, value)
330
352
  crit.selector[key] = { operator => new_value }
331
353
  else
332
354
  crit.selector[key][operator] = value