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