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
@@ -17,7 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @param [ Document ] base The base document.
19
19
  # @param [ Metdata ] metadata The metadata for the relation.
20
- # @param [ Hash, BSON::ObjectId ] object The attributes to build from or
20
+ # @param [ Hash, Moped::BSON::ObjectId ] object The attributes to build from or
21
21
  # id to query with.
22
22
  #
23
23
  # @since 2.0.0.rc.1
@@ -27,7 +27,7 @@ module Mongoid
27
27
  if update?
28
28
  existing.attributes = attributes
29
29
  elsif replace?
30
- parent.send(metadata.setter, Mongoid::Factory.build(metadata.klass, attributes))
30
+ parent.send(metadata.setter, Factory.build(metadata.klass, attributes))
31
31
  elsif delete?
32
32
  parent.send(metadata.setter, nil)
33
33
  end
@@ -18,7 +18,7 @@ module Mongoid
18
18
  return object unless query?
19
19
  return [] if object.is_a?(Array)
20
20
  crit = metadata.criteria(Conversions.flag(object, metadata), base.class)
21
- IdentityMap.get(crit.klass, crit.selector) || crit
21
+ IdentityMap.get_many(crit.klass, crit.selector) || crit
22
22
  end
23
23
  end
24
24
  end
@@ -27,9 +27,10 @@ module Mongoid
27
27
  def cascade!
28
28
  cascades.each do |name|
29
29
  if !metadata || !metadata.versioned?
30
- meta = relations[name]
31
- strategy = meta.cascade_strategy
32
- strategy.new(self, meta).cascade
30
+ if meta = relations[name]
31
+ strategy = meta.cascade_strategy
32
+ strategy.new(self, meta).cascade
33
+ end
33
34
  end
34
35
  end
35
36
  end
@@ -36,7 +36,7 @@ module Mongoid
36
36
  def convert(object)
37
37
  return object if metadata.polymorphic?
38
38
  klass = metadata.klass
39
- klass.using_object_ids? ? BSON::ObjectId.mongoize(object) : object
39
+ klass.using_object_ids? ? Moped::BSON::ObjectId.mongoize(object) : object
40
40
  end
41
41
  end
42
42
  end
@@ -20,7 +20,7 @@ module Mongoid
20
20
  # @since 2.3.0
21
21
  def flag(object, metadata)
22
22
  inverse = metadata.inverse_klass
23
- if inverse.using_object_ids? || object.is_a?(BSON::ObjectId)
23
+ if inverse.using_object_ids? || object.is_a?(Moped::BSON::ObjectId)
24
24
  object
25
25
  else
26
26
  if object.is_a?(String)
@@ -74,11 +74,11 @@ module Mongoid
74
74
  def batch_replace(docs)
75
75
  if docs.blank?
76
76
  if _assigning? && !empty?
77
- base.atomic_unsets.push(first.atomic_path)
77
+ base.add_atomic_unset(first)
78
78
  end
79
79
  batch_remove(target.dup)
80
80
  else
81
- base.delayed_atomic_sets.clear
81
+ base.delayed_atomic_sets.clear unless _assigning?
82
82
  docs = normalize_docs(docs).compact
83
83
  target.clear and _unscoped.clear
84
84
  inserts = execute_batch_insert(docs, "$set")
@@ -100,6 +100,7 @@ module Mongoid
100
100
  # @since 3.0.0
101
101
  def add_atomic_sets(sets)
102
102
  if _assigning?
103
+ base.delayed_atomic_sets[path].try(:clear)
103
104
  base.collect_children.each do |child|
104
105
  child.delayed_atomic_sets.clear
105
106
  end
@@ -131,8 +131,8 @@ module Mongoid
131
131
  # @since 2.0.0.rc.1
132
132
  def delete(document)
133
133
  doc = target.delete_one(document)
134
- _unscoped.delete_one(doc)
135
134
  if doc && !_binding?
135
+ _unscoped.delete_one(doc) unless doc.paranoid?
136
136
  if _assigning?
137
137
  if doc.paranoid?
138
138
  doc.destroy(suppress: true)
@@ -141,8 +141,8 @@ module Mongoid
141
141
  end
142
142
  else
143
143
  doc.delete(suppress: true)
144
+ unbind_one(doc)
144
145
  end
145
- unbind_one(doc)
146
146
  end
147
147
  reindex
148
148
  doc
@@ -182,10 +182,10 @@ module Mongoid
182
182
  # methods.
183
183
  #
184
184
  # @example Find a document by its id.
185
- # person.addresses.find(BSON::ObjectId.new)
185
+ # person.addresses.find(Moped::BSON::ObjectId.new)
186
186
  #
187
187
  # @example Find documents for multiple ids.
188
- # person.addresses.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
188
+ # person.addresses.find([ Moped::BSON::ObjectId.new, Moped::BSON::ObjectId.new ])
189
189
  #
190
190
  # @param [ Array<Object> ] args Various arguments.
191
191
  #
@@ -36,7 +36,7 @@ module Mongoid
36
36
  # @since 2.0.0.rc.1
37
37
  def substitute(replacement)
38
38
  if _assigning?
39
- base.atomic_unsets.push(atomic_path)
39
+ base.add_atomic_unset(target)
40
40
  else
41
41
  delete if persistable?
42
42
  end
@@ -123,7 +123,7 @@ module Mongoid
123
123
  #
124
124
  # @since 2.0.0.rc.1
125
125
  def class_name
126
- @class_name ||= (self[:class_name] || classify).sub(/^::/,"")
126
+ @class_name ||= (self[:class_name] || classify).sub(/\A::/,"")
127
127
  end
128
128
 
129
129
  # Get the foreign key contraint for the metadata.
@@ -263,7 +263,7 @@ module Mongoid
263
263
  #
264
264
  # @since 2.3.3
265
265
  def forced_nil_inverse?
266
- has_key?(:inverse_of) && inverse_of.nil?
266
+ @forced_nil_inverse ||= has_key?(:inverse_of) && inverse_of.nil?
267
267
  end
268
268
 
269
269
  # Handles all the logic for figuring out what the foreign_key is for each
@@ -389,9 +389,11 @@ module Mongoid
389
389
  #
390
390
  # @return [ Array<Symbol> ] The inverse name.
391
391
  def inverses(other = nil)
392
- return [self[:inverse_of]] if has_key?(:inverse_of)
393
- return self[:as] ? [self[:as]] : lookup_inverses(other) if polymorphic?
394
- @inverse ||= [(cyclic? ? cyclic_inverse : inverse_relation)]
392
+ if self[:polymorphic]
393
+ lookup_inverses(other)
394
+ else
395
+ @inverses ||= determine_inverses
396
+ end
395
397
  end
396
398
 
397
399
  # Get the name of the inverse relation if it exists. If this is a
@@ -862,6 +864,23 @@ module Mongoid
862
864
  relation.stores_foreign_key? && polymorphic? ? "#{name}_#{field}" : nil
863
865
  end
864
866
 
867
+ # Deterimene the inverses that can be memoized.
868
+ #
869
+ # @api private
870
+ #
871
+ # @example Determin the inverses.
872
+ # metadata.determine_inverses
873
+ #
874
+ # @return [ Array<Symbol> ] The inverses.
875
+ #
876
+ # @since 3.0.0
877
+ def determine_inverses
878
+ return [ inverse_of ] if has_key?(:inverse_of)
879
+ return [ as ] if has_key?(:as)
880
+ return [ cyclic_inverse ] if self[:cyclic]
881
+ [ inverse_relation ]
882
+ end
883
+
865
884
  # Find the module the class with the specific name is in.
866
885
  # This is done by starting at the inverse_class_name's
867
886
  # module and stepping down to see where it is defined.
@@ -993,17 +1012,11 @@ module Mongoid
993
1012
  def determine_inverse_relation
994
1013
  default = foreign_key_match || klass.relations[inverse_klass.name.underscore]
995
1014
  return default.name if default
996
- candidates = inverse_relation_candidates
997
-
998
- if candidates.size > 1
999
- raise Errors::AmbiguousRelationship.new(
1000
- klass,
1001
- inverse_klass,
1002
- name,
1003
- candidates
1004
- )
1015
+ names = inverse_relation_candidate_names
1016
+ if names.size > 1
1017
+ raise Errors::AmbiguousRelationship.new(klass, inverse_klass, name, names)
1005
1018
  end
1006
- candidates.first
1019
+ names.first
1007
1020
  end
1008
1021
 
1009
1022
  # Return metadata where the foreign key matches the foreign key on this
@@ -1019,27 +1032,41 @@ module Mongoid
1019
1032
  # @since 2.4.11
1020
1033
  def foreign_key_match
1021
1034
  if fk = self[:foreign_key]
1022
- klass.relations.values.detect do |meta|
1035
+ relations_metadata.detect do |meta|
1023
1036
  fk == meta.foreign_key if meta.stores_foreign_key?
1024
1037
  end
1025
1038
  end
1026
1039
  end
1027
1040
 
1028
- # Get the candidates for inverse relations.
1041
+ # Get the inverse relation candidates.
1029
1042
  #
1030
1043
  # @api private
1031
1044
  #
1032
- # @example Get the candidates.
1045
+ # @example Get the inverse relation candidates.
1033
1046
  # metadata.inverse_relation_candidates
1034
1047
  #
1035
- # @return [ Array<Symbol> ] The candidates.
1048
+ # @return [ Array<Metdata> ] The candidates.
1036
1049
  #
1037
1050
  # @since 3.0.0
1038
1051
  def inverse_relation_candidates
1039
- klass.relations.select do |_, meta|
1052
+ relations_metadata.select do |meta|
1040
1053
  next if meta.versioned? || meta.name == name
1041
1054
  meta.class_name == inverse_class_name
1042
- end.keys.map(&:to_sym)
1055
+ end
1056
+ end
1057
+
1058
+ # Get the candidates for inverse relations.
1059
+ #
1060
+ # @api private
1061
+ #
1062
+ # @example Get the candidates.
1063
+ # metadata.inverse_relation_candidates
1064
+ #
1065
+ # @return [ Array<Symbol> ] The candidates.
1066
+ #
1067
+ # @since 3.0.0
1068
+ def inverse_relation_candidate_names
1069
+ @candidate_names ||= inverse_relation_candidates.map(&:name)
1043
1070
  end
1044
1071
 
1045
1072
  # Determine the key for the relation in the attributes.
@@ -1091,8 +1118,11 @@ module Mongoid
1091
1118
  # @return [ Array<String> ] The inverse names.
1092
1119
  def lookup_inverses(other)
1093
1120
  if other
1094
- matching_metas = other.class.relations.find_all { |key, meta| meta.as == name }
1095
- matching_metas.map { |meta| meta[1].name }
1121
+ matches = []
1122
+ other.class.relations.values.each do |meta|
1123
+ matches.push(meta.name) if meta.as == name
1124
+ end
1125
+ matches
1096
1126
  end
1097
1127
  end
1098
1128
 
@@ -1113,6 +1143,20 @@ module Mongoid
1113
1143
  invs.first
1114
1144
  end
1115
1145
  end
1146
+
1147
+ # Get the relation metadata only.
1148
+ #
1149
+ # @api private
1150
+ #
1151
+ # @example Get the relation metadata.
1152
+ # metadata.relations_metadata
1153
+ #
1154
+ # @return [ Array<Metadata> ] The metadata.
1155
+ #
1156
+ # @since 3.0.0
1157
+ def relations_metadata
1158
+ klass.relations.values
1159
+ end
1116
1160
  end
1117
1161
  end
1118
1162
  end
@@ -63,11 +63,11 @@ module Mongoid
63
63
  # @param [ Class ] klass The class we're trying to convert for.
64
64
  # @param [ String ] id The id, usually coming from the form.
65
65
  #
66
- # @return [ BSON::ObjectId, String, Object ] The converted id.
66
+ # @return [ Moped::BSON::ObjectId, String, Object ] The converted id.
67
67
  #
68
68
  # @since 2.0.0.rc.6
69
69
  def convert_id(klass, id)
70
- klass.using_object_ids? ? BSON::ObjectId.mongoize(id) : id
70
+ klass.using_object_ids? ? Moped::BSON::ObjectId.mongoize(id) : id
71
71
  end
72
72
  end
73
73
  end
@@ -160,15 +160,17 @@ module Mongoid
160
160
  #
161
161
  # @since 2.2.0
162
162
  def eager_load_ids(metadata, ids)
163
- cleared = false
164
163
  klass, foreign_key = metadata.klass, metadata.foreign_key
165
- klass.any_in(foreign_key => ids).each do |doc|
166
- selector = { foreign_key => doc.send(foreign_key) }
167
- unless cleared
168
- IdentityMap.clear_many(klass, selector)
169
- cleared = true
164
+ eager_loaded = klass.any_in(foreign_key => ids).entries
165
+ unless eager_loaded.empty?
166
+ eager_loaded.each do |doc|
167
+ base_id = doc.__send__(foreign_key)
168
+ yield(doc, { foreign_key => base_id })
169
+ end
170
+ else
171
+ ids.each do |id|
172
+ IdentityMap.clear_many(klass, { foreign_key => id })
170
173
  end
171
- yield(doc, foreign_key => doc.send(foreign_key))
172
174
  end
173
175
  end
174
176
  end
@@ -50,13 +50,13 @@ module Mongoid
50
50
  #
51
51
  # @since 2.4.0
52
52
  def concat(documents)
53
- inserts = []
53
+ docs, inserts = [], []
54
54
  documents.each do |doc|
55
55
  next unless doc
56
56
  append(doc)
57
- save_or_delay(doc, inserts) if persistable?
57
+ save_or_delay(doc, docs, inserts) if persistable?
58
58
  end
59
- persist_delayed(inserts)
59
+ persist_delayed(docs, inserts)
60
60
  self
61
61
  end
62
62
 
@@ -170,12 +170,12 @@ module Mongoid
170
170
  # conditions.
171
171
  #
172
172
  # @example Find by an id.
173
- # person.posts.find(BSON::ObjectId.new)
173
+ # person.posts.find(Moped::BSON::ObjectId.new)
174
174
  #
175
175
  # @example Find by multiple ids.
176
- # person.posts.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
176
+ # person.posts.find([ Moped::BSON::ObjectId.new, Moped::BSON::ObjectId.new ])
177
177
  #
178
- # @param [ BSON::ObjectId, Array<BSON::ObjectId> ] arg The ids.
178
+ # @param [ Moped::BSON::ObjectId, Array<Moped::BSON::ObjectId> ] arg The ids.
179
179
  #
180
180
  # @return [ Document, Criteria ] The matching document(s).
181
181
  #
@@ -387,13 +387,14 @@ module Mongoid
387
387
  # @example Persist the delayed batch inserts.
388
388
  # relation.persist_delayed([ doc ])
389
389
  #
390
- # @param [ Array<Document> ] inserts The delayed inserts.
390
+ # @param [ Array<Document> ] docs The delayed inserts.
391
+ # @param [ Array<Hash> ] inserts The raw insert document.
391
392
  #
392
393
  # @since 3.0.0
393
- def persist_delayed(inserts)
394
- if inserts.any?
395
- collection.insert(inserts.map(&:as_document))
396
- inserts.each do |doc|
394
+ def persist_delayed(docs, inserts)
395
+ unless docs.empty?
396
+ collection.insert(inserts)
397
+ docs.each do |doc|
397
398
  doc.new_record = false
398
399
  doc.run_after_callbacks(:create, :save)
399
400
  doc.post_persist
@@ -478,10 +479,11 @@ module Mongoid
478
479
  # @param [ Array<Document> ] inserts The inserts.
479
480
  #
480
481
  # @since 3.0.0
481
- def save_or_delay(doc, inserts)
482
+ def save_or_delay(doc, docs, inserts)
482
483
  if doc.new_record? && doc.valid?(:create)
483
- inserts.push(doc)
484
484
  doc.run_before_callbacks(:save, :create)
485
+ docs.push(doc)
486
+ inserts.push(doc.as_document)
485
487
  else
486
488
  doc.save
487
489
  end
@@ -520,18 +522,14 @@ module Mongoid
520
522
  #
521
523
  # @since 2.1.0
522
524
  def criteria(metadata, object, type = nil)
523
- crit = metadata.klass.where(metadata.foreign_key => object)
524
-
525
- if metadata.polymorphic?
526
- crit = crit.where(metadata.type => type.name)
527
- end
528
-
529
- inverse_metadata = metadata.inverse_metadata(metadata.klass)
530
- if inverse_metadata && inverse_metadata.inverse_of_field
531
- crit = crit.any_in(inverse_metadata.inverse_of_field => [metadata.name, nil])
532
- end
533
-
534
- crit
525
+ with_inverse_field_criterion(
526
+ with_polymorphic_criterion(
527
+ metadata.klass.where(metadata.foreign_key => object),
528
+ metadata,
529
+ type
530
+ ),
531
+ metadata
532
+ )
535
533
  end
536
534
 
537
535
  # Eager load the relation based on the criteria.
@@ -688,6 +686,52 @@ module Mongoid
688
686
  def validation_default
689
687
  true
690
688
  end
689
+
690
+ private
691
+
692
+ # Decorate the criteria with polymorphic criteria, if applicable.
693
+ #
694
+ # @api private
695
+ #
696
+ # @example Get the criteria with polymorphic criterion.
697
+ # Proxy.with_polymorphic_criterion(criteria, metadata)
698
+ #
699
+ # @param [ Criteria ] criteria The criteria to decorate.
700
+ # @param [ Metadata ] metadata The metadata.
701
+ # @param [ Class ] type The optional type.
702
+ #
703
+ # @return [ Criteria ] The criteria.
704
+ #
705
+ # @since 3.0.0
706
+ def with_polymorphic_criterion(criteria, metadata, type = nil)
707
+ if metadata.polymorphic?
708
+ criteria.where(metadata.type => type.name)
709
+ else
710
+ criteria
711
+ end
712
+ end
713
+
714
+ # Decorate the criteria with inverse field criteria, if applicable.
715
+ #
716
+ # @api private
717
+ #
718
+ # @example Get the criteria with polymorphic criterion.
719
+ # Proxy.with_inverse_field_criterion(criteria, metadata)
720
+ #
721
+ # @param [ Criteria ] criteria The criteria to decorate.
722
+ # @param [ Metadata ] metadata The metadata.
723
+ #
724
+ # @return [ Criteria ] The criteria.
725
+ #
726
+ # @since 3.0.0
727
+ def with_inverse_field_criterion(criteria, metadata)
728
+ inverse_metadata = metadata.inverse_metadata(metadata.klass)
729
+ if inverse_metadata.try(:inverse_of_field)
730
+ criteria.any_in(inverse_metadata.inverse_of_field => [ metadata.name, nil ])
731
+ else
732
+ criteria
733
+ end
734
+ end
691
735
  end
692
736
  end
693
737
  end