mongoid 3.0.0.rc → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/CHANGELOG.md +109 -4
  2. data/README.md +1 -1
  3. data/Rakefile +1 -0
  4. data/lib/config/locales/en.yml +15 -1
  5. data/lib/mongoid.rb +17 -2
  6. data/lib/mongoid/atomic.rb +54 -7
  7. data/lib/mongoid/attributes.rb +1 -1
  8. data/lib/mongoid/attributes/processing.rb +1 -1
  9. data/lib/mongoid/callbacks.rb +6 -1
  10. data/lib/mongoid/components.rb +2 -1
  11. data/lib/mongoid/config.rb +42 -17
  12. data/lib/mongoid/config/environment.rb +3 -1
  13. data/lib/mongoid/contextual/aggregable/memory.rb +21 -10
  14. data/lib/mongoid/contextual/find_and_modify.rb +14 -12
  15. data/lib/mongoid/contextual/memory.rb +24 -1
  16. data/lib/mongoid/contextual/mongo.rb +148 -29
  17. data/lib/mongoid/copyable.rb +6 -24
  18. data/lib/mongoid/criteria.rb +116 -34
  19. data/lib/mongoid/document.rb +7 -7
  20. data/lib/mongoid/errors.rb +1 -0
  21. data/lib/mongoid/errors/no_metadata.rb +21 -0
  22. data/lib/mongoid/evolvable.rb +19 -0
  23. data/lib/mongoid/extensions.rb +1 -1
  24. data/lib/mongoid/extensions/array.rb +38 -1
  25. data/lib/mongoid/extensions/big_decimal.rb +1 -1
  26. data/lib/mongoid/extensions/date_time.rb +6 -1
  27. data/lib/mongoid/extensions/false_class.rb +12 -0
  28. data/lib/mongoid/extensions/float.rb +12 -0
  29. data/lib/mongoid/extensions/hash.rb +33 -1
  30. data/lib/mongoid/extensions/integer.rb +12 -0
  31. data/lib/mongoid/extensions/object.rb +51 -1
  32. data/lib/mongoid/extensions/object_id.rb +2 -1
  33. data/lib/mongoid/extensions/range.rb +24 -0
  34. data/lib/mongoid/extensions/string.rb +31 -5
  35. data/lib/mongoid/extensions/true_class.rb +12 -0
  36. data/lib/mongoid/fields.rb +20 -21
  37. data/lib/mongoid/fields/foreign_key.rb +23 -7
  38. data/lib/mongoid/fields/standard.rb +3 -3
  39. data/lib/mongoid/finders.rb +3 -7
  40. data/lib/mongoid/hierarchy.rb +19 -1
  41. data/lib/mongoid/identity_map.rb +20 -4
  42. data/lib/mongoid/indexes/validators/options.rb +1 -1
  43. data/lib/mongoid/multi_parameter_attributes.rb +1 -1
  44. data/lib/mongoid/paranoia.rb +3 -32
  45. data/lib/mongoid/persistence.rb +33 -15
  46. data/lib/mongoid/persistence/atomic/operation.rb +1 -1
  47. data/lib/mongoid/persistence/operations.rb +16 -0
  48. data/lib/mongoid/persistence/operations/remove.rb +1 -1
  49. data/lib/mongoid/persistence/operations/upsert.rb +28 -0
  50. data/lib/mongoid/persistence/upsertion.rb +30 -0
  51. data/lib/mongoid/relations.rb +16 -0
  52. data/lib/mongoid/relations/accessors.rb +1 -1
  53. data/lib/mongoid/relations/bindings/referenced/in.rb +1 -1
  54. data/lib/mongoid/relations/builder.rb +1 -1
  55. data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
  56. data/lib/mongoid/relations/builders/referenced/many.rb +1 -1
  57. data/lib/mongoid/relations/cascading.rb +4 -3
  58. data/lib/mongoid/relations/constraint.rb +1 -1
  59. data/lib/mongoid/relations/conversions.rb +1 -1
  60. data/lib/mongoid/relations/embedded/batchable.rb +3 -2
  61. data/lib/mongoid/relations/embedded/many.rb +4 -4
  62. data/lib/mongoid/relations/embedded/one.rb +1 -1
  63. data/lib/mongoid/relations/metadata.rb +67 -23
  64. data/lib/mongoid/relations/nested_builder.rb +2 -2
  65. data/lib/mongoid/relations/proxy.rb +9 -7
  66. data/lib/mongoid/relations/referenced/many.rb +69 -25
  67. data/lib/mongoid/relations/referenced/many_to_many.rb +14 -13
  68. data/lib/mongoid/scoping.rb +0 -17
  69. data/lib/mongoid/serialization.rb +95 -26
  70. data/lib/mongoid/sessions.rb +30 -6
  71. data/lib/mongoid/sessions/factory.rb +2 -0
  72. data/lib/mongoid/threaded.rb +52 -0
  73. data/lib/mongoid/timestamps/created.rb +1 -1
  74. data/lib/mongoid/timestamps/updated.rb +2 -1
  75. data/lib/mongoid/validations/uniqueness.rb +3 -2
  76. data/lib/mongoid/version.rb +1 -1
  77. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +8 -0
  78. data/lib/rails/mongoid.rb +8 -5
  79. metadata +30 -13
  80. data/lib/mongoid/collections/retry.rb +0 -58
  81. data/lib/mongoid/javascript.rb +0 -20
  82. data/lib/mongoid/javascript/functions.yml +0 -63
@@ -30,11 +30,8 @@ module Mongoid
30
30
  #
31
31
  # @since 1.0.0
32
32
  def ==(other)
33
- case other
34
- when Criteria then super
35
- when Enumerable then entries == other
36
- else false
37
- end
33
+ return super if other.respond_to?(:selector)
34
+ entries == other
38
35
  end
39
36
 
40
37
  # Needed to properly get a criteria back as json
@@ -64,6 +61,7 @@ module Mongoid
64
61
  def build(attrs = {})
65
62
  create_document(:new, attrs)
66
63
  end
64
+ alias :new :build
67
65
 
68
66
  # Tells the criteria that the cursor that gets returned needs to be
69
67
  # cached. This is so multiple iterations don't hit the database multiple
@@ -150,6 +148,18 @@ module Mongoid
150
148
  @documents = docs
151
149
  end
152
150
 
151
+ # Is the criteria for embedded documents?
152
+ #
153
+ # @example Is the criteria for embedded documents?
154
+ # criteria.embedded?
155
+ #
156
+ # @return [ true, false ] If the criteria is embedded.
157
+ #
158
+ # @since 3.0.0
159
+ def embedded?
160
+ !!@embedded
161
+ end
162
+
153
163
  # Execute the criteria or raise an error if no documents found.
154
164
  #
155
165
  # @example Execute or raise
@@ -164,9 +174,7 @@ module Mongoid
164
174
  # @since 2.0.0
165
175
  def execute_or_raise(ids, multi)
166
176
  result = multiple_from_map_or_db(ids)
167
- if (result.size < ids.size) && Mongoid.raise_not_found_error
168
- raise Errors::DocumentNotFound.new(klass, ids, ids - result.map(&:_id))
169
- end
177
+ check_for_missing_documents!(result, ids)
170
178
  multi ? result : result.first
171
179
  end
172
180
 
@@ -222,7 +230,7 @@ module Mongoid
222
230
  # @since 2.0.0
223
231
  def field_list
224
232
  if options[:fields]
225
- options[:fields].keys.reject!{ |key| key == "_type" }
233
+ options[:fields].keys.reject{ |key| key == "_type" }
226
234
  else
227
235
  []
228
236
  end
@@ -231,23 +239,20 @@ module Mongoid
231
239
  # Find the matchind document(s) in the criteria for the provided ids.
232
240
  #
233
241
  # @example Find by an id.
234
- # criteria.find(BSON::ObjectId.new)
242
+ # criteria.find(Moped::BSON::ObjectId.new)
235
243
  #
236
244
  # @example Find by multiple ids.
237
- # criteria.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
245
+ # criteria.find([ Moped::BSON::ObjectId.new, Moped::BSON::ObjectId.new ])
238
246
  #
239
- # @param [ Array<BSON::ObjectId> ] args The ids to search for.
247
+ # @param [ Array<Moped::BSON::ObjectId> ] args The ids to search for.
240
248
  #
241
249
  # @return [ Array<Document>, Document ] The matching document(s).
242
250
  #
243
251
  # @since 1.0.0
244
252
  def find(*args)
245
- multi = args.first.is_a?(::Array) || args.first.is_a?(::Range) || args.size > 1
246
- ids = *args.flat_map do |arg|
247
- arg.is_a?(::Range) ? arg.to_a : arg
248
- end
253
+ ids = args.__find_args__
249
254
  raise_invalid if ids.any?(&:nil?)
250
- for_ids(ids).execute_or_raise(ids, multi)
255
+ for_ids(ids).execute_or_raise(ids, args.multi_arged?)
251
256
  end
252
257
 
253
258
  # Adds a criterion to the +Criteria+ that specifies an id that must be matched.
@@ -262,12 +267,12 @@ module Mongoid
262
267
  #
263
268
  # @return [ Criteria ] The cloned criteria.
264
269
  def for_ids(ids)
265
- field = klass.fields["_id"]
270
+ ids = mongoize_ids(ids)
266
271
  method = extract_id ? :all_of : :where
267
272
  if ids.size > 1
268
- send(method, { _id: { "$in" => ids.map{ |id| field.mongoize(id) }}})
273
+ send(method, { _id: { "$in" => ids }})
269
274
  else
270
- send(method, { _id: field.mongoize(ids.first) })
275
+ send(method, { _id: ids.first })
271
276
  end
272
277
  end
273
278
 
@@ -295,7 +300,9 @@ module Mongoid
295
300
  #
296
301
  # @since 2.2.1
297
302
  def from_map_or_db
298
- doc = IdentityMap.get(klass, extract_id || selector)
303
+ id = extract_id
304
+ id = klass.fields["_id"].mongoize(id) if id
305
+ doc = IdentityMap.get(klass, id || selector.except("_type"))
299
306
  doc && doc.matches?(selector) ? doc : first
300
307
  end
301
308
 
@@ -309,13 +316,10 @@ module Mongoid
309
316
  #
310
317
  # @return [ Array<Document> ] The found documents.
311
318
  def multiple_from_map_or_db(ids)
312
- return entries if klass.embedded?
313
- result = []
314
- ids.reject! do |id|
315
- doc = IdentityMap.get(klass, id)
316
- doc && doc.matches?(selector) ? result.push(doc) : false
317
- end
318
- ids.empty? ? result : result + any_in(id: ids).entries
319
+ return entries if embedded?
320
+ ids = mongoize_ids(ids)
321
+ result = from_identity_map(ids)
322
+ ids.empty? ? result : result + from_database(ids)
319
323
  end
320
324
 
321
325
  # Initialize the new criteria.
@@ -418,9 +422,9 @@ module Mongoid
418
422
  # @since 3.0.0
419
423
  def merge!(other)
420
424
  criteria = other.to_criteria
421
- selector.update(criteria.selector)
422
- options.update(criteria.options)
423
- self.documents = criteria.documents.dup if criteria.documents.any?
425
+ selector.merge!(criteria.selector)
426
+ options.merge!(criteria.options)
427
+ self.documents = criteria.documents.dup unless criteria.documents.empty?
424
428
  self.scoping_options = criteria.scoping_options
425
429
  self.inclusions = (inclusions + criteria.inclusions.dup).uniq
426
430
  self
@@ -438,7 +442,12 @@ module Mongoid
438
442
  # @since 1.0.0
439
443
  def only(*args)
440
444
  return clone if args.empty?
441
- super(*(args + [:_type]))
445
+ args = args.flatten
446
+ if klass.hereditary?
447
+ super(*args.push(:_type))
448
+ else
449
+ super(*args)
450
+ end
442
451
  end
443
452
 
444
453
  # Returns true if criteria responds to the given method.
@@ -491,8 +500,7 @@ module Mongoid
491
500
  #
492
501
  # @return [ Criteria ] The cloned criteria.
493
502
  def type(types)
494
- types = [types] unless types.is_a?(Array)
495
- any_in(_type: types)
503
+ any_in(_type: Array(types))
496
504
  end
497
505
 
498
506
  # This is the general entry point for most MongoDB queries. This either
@@ -522,6 +530,27 @@ module Mongoid
522
530
 
523
531
  private
524
532
 
533
+ # Are documents in the query missing, and are we configured to raise an
534
+ # error?
535
+ #
536
+ # @api private
537
+ #
538
+ # @example Check for missing documents.
539
+ # criteria.check_for_missing_documents!([], [ 1 ])
540
+ #
541
+ # @param [ Array<Document> ] result The result.
542
+ # @param [ Array<Object> ] ids The ids.
543
+ #
544
+ # @raise [ Errors::DocumentNotFound ] If none are found and raising an
545
+ # error.
546
+ #
547
+ # @since 3.0.0
548
+ def check_for_missing_documents!(result, ids)
549
+ if (result.size < ids.size) && Mongoid.raise_not_found_error
550
+ raise Errors::DocumentNotFound.new(klass, ids, ids - result.map(&:_id))
551
+ end
552
+ end
553
+
525
554
  # Create a document given the provided method and attributes from the
526
555
  # existing selector.
527
556
  #
@@ -572,6 +601,43 @@ module Mongoid
572
601
  @context = nil
573
602
  end
574
603
 
604
+ # Get documents from the database only.
605
+ #
606
+ # @api private
607
+ #
608
+ # @example Get documents from the database.
609
+ # criteria.from_database(ids)
610
+ #
611
+ # @param [ Array<Object> ] ids The ids to fetch with.
612
+ #
613
+ # @return [ Array<Document> ] The matching documents.
614
+ #
615
+ # @since 3.0.0
616
+ def from_database(ids)
617
+ (ids.size > 1 ? any_in(id: ids) : where(id: ids.first)).entries
618
+ end
619
+
620
+ # Get documents from the identity map only.
621
+ #
622
+ # @api private
623
+ #
624
+ # @example Get documents from the identity map.
625
+ # criteria.from_identity_map(ids)
626
+ #
627
+ # @param [ Array<Object> ] ids The ids to fetch with.
628
+ #
629
+ # @return [ Array<Document> ] The matching documents.
630
+ #
631
+ # @since 3.0.0
632
+ def from_identity_map(ids)
633
+ result = []
634
+ ids.reject! do |id|
635
+ doc = IdentityMap.get(klass, id)
636
+ doc && doc.matches?(selector) ? result.push(doc) : false
637
+ end
638
+ result
639
+ end
640
+
575
641
  # Used for chaining +Criteria+ scopes together in the for of class methods
576
642
  # on the +Document+ the criteria is for.
577
643
  #
@@ -594,6 +660,22 @@ module Mongoid
594
660
  end
595
661
  end
596
662
 
663
+ # Convert all the ids to their proper types.
664
+ #
665
+ # @api private
666
+ #
667
+ # @example Convert the ids.
668
+ # criteria.mongoize_ids(ids)
669
+ #
670
+ # @param [ Array<Object> ] ids The ids to convert.
671
+ #
672
+ # @return [ Array<Object> ] The converted ids.
673
+ #
674
+ # @since 3.0.0
675
+ def mongoize_ids(ids)
676
+ ids.map{ |id| klass.fields["_id"].mongoize(id) }
677
+ end
678
+
597
679
  # Convenience method of raising an invalid options error.
598
680
  #
599
681
  # @example Raise the error.
@@ -109,7 +109,7 @@ module Mongoid
109
109
 
110
110
  # A Document's is identified absolutely by it's class and database id:
111
111
  #
112
- # Person.first.identity #=> [Person, BSON::ObjectId('4f775130a04745933a000003')]
112
+ # Person.first.identity #=> [Person, Moped::BSON::ObjectId('4f775130a04745933a000003')]
113
113
  #
114
114
  # @example Get the identity
115
115
  # document.identity
@@ -126,7 +126,7 @@ module Mongoid
126
126
  # an empty +Hash+.
127
127
  #
128
128
  # If a primary key is defined, the document's id will be set to that key,
129
- # otherwise it will be set to a fresh +BSON::ObjectId+ string.
129
+ # otherwise it will be set to a fresh +Moped::BSON::ObjectId+ string.
130
130
  #
131
131
  # @example Create a new document.
132
132
  # Person.new(:title => "Sir")
@@ -148,7 +148,7 @@ module Mongoid
148
148
  yield(self) if block_given?
149
149
  end
150
150
  apply_post_processed_defaults
151
- run_callbacks(:initialize) if _initialize_callbacks.any?
151
+ run_callbacks(:initialize) unless _initialize_callbacks.empty?
152
152
  end
153
153
  end
154
154
 
@@ -197,8 +197,8 @@ module Mongoid
197
197
  attributes
198
198
  end
199
199
 
200
- # Returns an instance of the specified class with the attributes
201
- # and errors of the current document.
200
+ # Returns an instance of the specified class with the attributes,
201
+ # errors, and embedded documents of the current document.
202
202
  #
203
203
  # @example Return a subclass document as a superclass instance.
204
204
  # manager.becomes(Person)
@@ -214,7 +214,7 @@ module Mongoid
214
214
  unless klass.include?(Mongoid::Document)
215
215
  raise ArgumentError, "A class which includes Mongoid::Document is expected"
216
216
  end
217
- became = klass.instantiate(frozen? ? attributes.dup : attributes)
217
+ became = klass.instantiate(as_document.__deep_copy__)
218
218
  became.instance_variable_set(:@errors, errors)
219
219
  became.instance_variable_set(:@new_record, new_record?)
220
220
  became.instance_variable_set(:@destroyed, destroyed?)
@@ -311,7 +311,7 @@ module Mongoid
311
311
  doc.instance_variable_set(:@attributes, attributes)
312
312
  doc.apply_defaults
313
313
  IdentityMap.set(doc) unless _loading_revision?
314
- doc.run_callbacks(:initialize) if doc._initialize_callbacks.any?
314
+ doc.run_callbacks(:initialize) unless doc._initialize_callbacks.empty?
315
315
  doc
316
316
  end
317
317
 
@@ -23,6 +23,7 @@ require "mongoid/errors/nested_attributes_metadata_not_found"
23
23
  require "mongoid/errors/no_default_session"
24
24
  require "mongoid/errors/no_environment"
25
25
  require "mongoid/errors/no_map_reduce_output"
26
+ require "mongoid/errors/no_metadata"
26
27
  require "mongoid/errors/no_parent"
27
28
  require "mongoid/errors/no_session_config"
28
29
  require "mongoid/errors/no_sessions_config"
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Errors
4
+
5
+ # Used when trying to persist data when metadata has not been set.
6
+ class NoMetadata < MongoidError
7
+
8
+ # Create the new error.
9
+ #
10
+ # @example Create the error.
11
+ # NoMetadata.new(Address)
12
+ #
13
+ # @param [ Class ] klass The document class.
14
+ #
15
+ # @since 3.0.0
16
+ def initialize(klass)
17
+ super(compose_message("no_metadata", { klass: klass }))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+
4
+ # Contains behaviour specific to evolving for origin queries.
5
+ module Evolvable
6
+
7
+ # Evolve the document into an object id.
8
+ #
9
+ # @example Evolve the document.
10
+ # document.__evolve_object_id__
11
+ #
12
+ # @return [ Object ] The document's id.
13
+ #
14
+ # @since 3.0.0
15
+ def __evolve_object_id__
16
+ id
17
+ end
18
+ end
19
+ end
@@ -3,7 +3,7 @@ unless defined?(Boolean)
3
3
  class Boolean; end
4
4
  end
5
5
 
6
- class BSON::ObjectId
6
+ class Moped::BSON::ObjectId
7
7
  undef :as_json
8
8
  def as_json(options = nil)
9
9
  to_s
@@ -12,7 +12,32 @@ module Mongoid
12
12
  #
13
13
  # @since 3.0.0
14
14
  def __evolve_object_id__
15
- map!(&:__evolve_object_id__).compact!
15
+ map!(&:__evolve_object_id__)
16
+ self
17
+ end
18
+
19
+ # Get the array of args as arguments for a find query.
20
+ #
21
+ # @example Get the array as find args.
22
+ # [ 1, 2, 3 ].__find_args__
23
+ #
24
+ # @return [ Array ] The array of args.
25
+ #
26
+ # @since 3.0.0
27
+ def __find_args__
28
+ flat_map{ |a| a.__find_args__ }.uniq_by{ |a| a.to_s }
29
+ end
30
+
31
+ # Mongoize the array into an array of object ids.
32
+ #
33
+ # @example Evolve the array to object ids.
34
+ # [ id ].__mongoize_object_id__
35
+ #
36
+ # @return [ Array<Moped::BSON::ObjectId> ] The converted array.
37
+ #
38
+ # @since 3.0.0
39
+ def __mongoize_object_id__
40
+ map!(&:__mongoize_object_id__).compact!
16
41
  self
17
42
  end
18
43
 
@@ -28,6 +53,18 @@ module Mongoid
28
53
  ::Time.configured.local(*self)
29
54
  end
30
55
 
56
+ # Is the array a set of multiple arguments in a method?
57
+ #
58
+ # @example Is this multi args?
59
+ # [ 1, 2, 3 ].multi_arged?
60
+ #
61
+ # @return [ true, false ] If the array is multi args.
62
+ #
63
+ # @since 3.0.0
64
+ def multi_arged?
65
+ first.resizable? || size > 1
66
+ end
67
+
31
68
  # Turn the object from the ruby type we deal with to a Mongo friendly
32
69
  # type.
33
70
  #
@@ -30,7 +30,7 @@ module Mongoid
30
30
  # @since 3.0.0
31
31
  def demongoize(object)
32
32
  if object
33
- object.numeric? ? ::BigDecimal.new(object) : object
33
+ object.numeric? ? ::BigDecimal.new(object.to_s) : object
34
34
  end
35
35
  end
36
36
  end
@@ -13,7 +13,12 @@ module Mongoid
13
13
  # @since 3.0.0
14
14
  def __mongoize_time__
15
15
  return self if utc? && Mongoid.use_utc?
16
- ::Time.configured.local(year, month, day, hour, min, sec)
16
+ if Mongoid.use_activesupport_time_zone?
17
+ in_time_zone(::Time.zone)
18
+ else
19
+ time = to_time
20
+ time.respond_to?(:getlocal) ? time.getlocal : time
21
+ end
17
22
  end
18
23
 
19
24
  # Turn the object from the ruby type we deal with to a Mongo friendly