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

Sign up to get free protection for your applications and to get access to all the features.
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