mongoid 3.0.0.rc → 3.0.0

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 (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