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
@@ -112,12 +112,12 @@ module Mongoid
112
112
  @metadata ||= options[:metadata]
113
113
  end
114
114
 
115
- # Is the field a BSON::ObjectId?
115
+ # Is the field a Moped::BSON::ObjectId?
116
116
  #
117
- # @example Is the field a BSON::ObjectId?
117
+ # @example Is the field a Moped::BSON::ObjectId?
118
118
  # field.object_id_field?
119
119
  #
120
- # @return [ true, false ] If the field is a BSON::ObjectId.
120
+ # @return [ true, false ] If the field is a Moped::BSON::ObjectId.
121
121
  #
122
122
  # @since 2.2.0
123
123
  def object_id_field?
@@ -52,7 +52,7 @@ module Mongoid
52
52
  # on the conditions provided and the first parameter.
53
53
  #
54
54
  # @example Find a single document by an id.
55
- # Person.find(BSON::ObjectId)
55
+ # Person.find(Moped::BSON::ObjectId)
56
56
  #
57
57
  # @param [ Array ] args An assortment of finder options.
58
58
  #
@@ -112,9 +112,7 @@ module Mongoid
112
112
  # Find the first +Document+ given the conditions.
113
113
  #
114
114
  # @example Find the first document.
115
- # Person.first(:conditions => { :attribute => "value" })
116
- #
117
- # @param [ Array ] args The conditions with options.
115
+ # Person.first
118
116
  #
119
117
  # @return [ Document ] The first matching document.
120
118
  def first
@@ -124,9 +122,7 @@ module Mongoid
124
122
  # Find the last +Document+ given the conditions.
125
123
  #
126
124
  # @example Find the last document.
127
- # Person.last(:conditions => { :attribute => "value" })
128
- #
129
- # @param [ Array ] args The conditions with options.
125
+ # Person.last
130
126
  #
131
127
  # @return [ Document ] The last matching document.
132
128
  def last
@@ -79,7 +79,7 @@ module Mongoid
79
79
  #
80
80
  # @since 2.0.0.beta.1
81
81
  def remove_child(child)
82
- name = child.metadata.name
82
+ name = child.metadata_name
83
83
  if child.embedded_one?
84
84
  remove_ivar(name)
85
85
  else
@@ -128,6 +128,24 @@ module Mongoid
128
128
  def hereditary?
129
129
  Mongoid::Document > superclass
130
130
  end
131
+
132
+ # When inheriting, we want to copy the fields from the parent class and
133
+ # set the on the child to start, mimicking the behaviour of the old
134
+ # class_inheritable_accessor that was deprecated in Rails edge.
135
+ #
136
+ # @example Inherit from this class.
137
+ # Person.inherited(Doctor)
138
+ #
139
+ # @param [ Class ] subclass The inheriting class.
140
+ #
141
+ # @since 2.0.0.rc.6
142
+ def inherited(subclass)
143
+ super
144
+ subclass.fields = fields.dup
145
+ subclass.pre_processed_defaults = pre_processed_defaults.dup
146
+ subclass.post_processed_defaults = post_processed_defaults.dup
147
+ subclass.scopes = scopes.dup
148
+ end
131
149
  end
132
150
  end
133
151
  end
@@ -16,8 +16,7 @@ module Mongoid
16
16
  #
17
17
  # @since 2.4.10
18
18
  def clear_many(klass, selector)
19
- documents = documents_for(klass)[selector]
20
- documents.clear if documents
19
+ documents_for(klass)[selector] = {}
21
20
  end
22
21
 
23
22
  # Get a document from the identity map by its id.
@@ -44,6 +43,23 @@ module Mongoid
44
43
  end
45
44
  end
46
45
 
46
+ # Get many documents from the map via the selector
47
+ #
48
+ # @example Get the document from the map.
49
+ # map.get(Person, { post_id: post })
50
+ #
51
+ # @param [ Class ] klass The class of the document.
52
+ # @param [ Hash ] idenfier The selector.
53
+ #
54
+ # @return [ Array<Document> ] The matching documents.
55
+ #
56
+ # @since 3.0.0
57
+ def get_many(klass, identifier)
58
+ if Mongoid.using_identity_map? && klass
59
+ documents_for(klass)[identifier].try(:values)
60
+ end
61
+ end
62
+
47
63
  # Remove the document from the identity map.
48
64
  #
49
65
  # @example Remove the document.
@@ -88,7 +104,7 @@ module Mongoid
88
104
  #
89
105
  # @since 2.2.0
90
106
  def set_many(document, selector)
91
- (documents_for(document.class)[selector] ||= []).push(document)
107
+ (documents_for(document.class)[selector] ||= {})[document.id] = document
92
108
  end
93
109
 
94
110
  # Set a document in the identity map for the provided selector.
@@ -103,7 +119,7 @@ module Mongoid
103
119
  #
104
120
  # @since 2.2.0
105
121
  def set_one(document, selector)
106
- documents_for(document.class)[selector] = document
122
+ documents_for(document.class)[selector] = document
107
123
  end
108
124
 
109
125
  private
@@ -7,7 +7,7 @@ module Mongoid
7
7
  module Options
8
8
  extend self
9
9
 
10
- VALID_OPTIONS = [ :background, :drop_dups, :name, :sparse, :unique ]
10
+ VALID_OPTIONS = [ :background, :drop_dups, :name, :sparse, :unique, :max, :min ]
11
11
  VALID_TYPES = [ 1, -1, "2d" ]
12
12
 
13
13
  # Validate the index specification.
@@ -55,7 +55,7 @@ module Mongoid
55
55
  multi_parameter_attributes = {}
56
56
 
57
57
  attrs.each_pair do |key, value|
58
- if key =~ /^([^\(]+)\((\d+)([if])\)$/
58
+ if key =~ /\A([^\(]+)\((\d+)([if])\)$/
59
59
  key, index = $1, $2.to_i
60
60
  (multi_parameter_attributes[key] ||= {})[index] = value.empty? ? nil : value.send("to_#{$3}")
61
61
  else
@@ -16,6 +16,9 @@ module Mongoid
16
16
  included do
17
17
  field :deleted_at, type: Time
18
18
  self.paranoid = true
19
+
20
+ default_scope where(deleted_at: nil)
21
+ scope :deleted, ne(deleted_at: nil)
19
22
  end
20
23
 
21
24
  # Delete the paranoid +Document+ from the database completely. This will
@@ -118,37 +121,5 @@ module Mongoid
118
121
  def paranoid_field
119
122
  embedded? ? "#{atomic_position}.deleted_at" : "deleted_at"
120
123
  end
121
-
122
- module ClassMethods
123
-
124
- # Override the default +Criteria+ accessor to only get existing
125
- # documents.
126
- #
127
- # @example Override the criteria.
128
- # Person.queryable
129
- #
130
- # @param [ Array ] args The arguments.
131
- #
132
- # @return [ Criteria ] The paranoid compliant criteria.
133
- #
134
- # @since 3.0.0
135
- def queryable
136
- super.where(deleted_at: nil)
137
- end
138
-
139
- # Find deleted documents
140
- #
141
- # @example Find deleted documents.
142
- # Person.deleted
143
- # Company.first.employees.deleted
144
- # Person.deleted.find("4c188dea7b17235a2a000001").first
145
- #
146
- # @return [ Criteria ] The deleted criteria.
147
- #
148
- # @since 1.0.0
149
- def deleted
150
- where(:deleted_at.ne => nil)
151
- end
152
- end
153
124
  end
154
125
  end
@@ -3,6 +3,7 @@ require "mongoid/persistence/atomic"
3
3
  require "mongoid/persistence/deletion"
4
4
  require "mongoid/persistence/insertion"
5
5
  require "mongoid/persistence/modification"
6
+ require "mongoid/persistence/upsertion"
6
7
  require "mongoid/persistence/operations"
7
8
 
8
9
  module Mongoid
@@ -62,6 +63,25 @@ module Mongoid
62
63
  end
63
64
  alias :delete :remove
64
65
 
66
+ # Save the document - will perform an insert if the document is new, and
67
+ # update if not.
68
+ #
69
+ # @example Save the document.
70
+ # document.save
71
+ #
72
+ # @param [ Hash ] options Options to pass to the save.
73
+ #
74
+ # @return [ true, false ] True is success, false if not.
75
+ #
76
+ # @since 1.0.0
77
+ def save(options = {})
78
+ if new_record?
79
+ insert(options).persisted?
80
+ else
81
+ update(options)
82
+ end
83
+ end
84
+
65
85
  # Save the document - will perform an insert if the document is new, and
66
86
  # update if not. If a validation error occurs an error will get raised.
67
87
  #
@@ -72,8 +92,8 @@ module Mongoid
72
92
  #
73
93
  # @return [ true, false ] True if validation passed.
74
94
  def save!(options = {})
75
- unless upsert(options)
76
- self.class.fail_validate!(self) if errors.any?
95
+ unless save(options)
96
+ self.class.fail_validate!(self) unless errors.empty?
77
97
  self.class.fail_callback!(self, :save!)
78
98
  end
79
99
  return true
@@ -100,7 +120,7 @@ module Mongoid
100
120
  current = Time.now
101
121
  write_attribute(:updated_at, current) if fields["updated_at"]
102
122
  write_attribute(field, current) if field
103
- collection.find(atomic_selector).update(atomic_updates)
123
+ _root.collection.find(atomic_selector).update(atomic_updates)
104
124
  without_autobuild do
105
125
  touchables.each { |name| send(name).try(:touch) }
106
126
  end
@@ -168,29 +188,27 @@ module Mongoid
168
188
  def update_attributes!(attributes = {}, options = {})
169
189
  result = update_attributes(attributes, options)
170
190
  unless result
171
- self.class.fail_validate!(self) if errors.any?
191
+ self.class.fail_validate!(self) unless errors.empty?
172
192
  self.class.fail_callback!(self, :update_attributes!)
173
193
  end
174
194
  result
175
195
  end
176
196
 
177
- # Upsert the document - will perform an insert if the document is new, and
178
- # update if not.
197
+ # Perform an upsert of the document. If the document does not exist in the
198
+ # database, then Mongo will insert a new one, otherwise the fields will get
199
+ # overwritten with new values on the existing document.
179
200
  #
180
201
  # @example Upsert the document.
181
202
  # document.upsert
182
203
  #
183
- # @param [ Hash ] options Options to pass to the upsert.
204
+ # @param [ Hash ] options The validation options.
184
205
  #
185
- # @return [ true, false ] True is success, false if not.
206
+ # @return [ true ] True.
207
+ #
208
+ # @since 3.0.0
186
209
  def upsert(options = {})
187
- if new_record?
188
- insert(options).persisted?
189
- else
190
- update(options)
191
- end
210
+ Operations.upsert(self, options).persist
192
211
  end
193
- alias :save :upsert
194
212
 
195
213
  module ClassMethods #:nodoc:
196
214
 
@@ -230,7 +248,7 @@ module Mongoid
230
248
  def create!(attributes = {}, options = {}, &block)
231
249
  _creating do
232
250
  doc = new(attributes, options, &block)
233
- fail_validate!(doc) if doc.insert.errors.any?
251
+ fail_validate!(doc) unless doc.insert.errors.empty?
234
252
  fail_callback!(doc, :create!) if doc.new_record?
235
253
  doc
236
254
  end
@@ -93,7 +93,7 @@ module Mongoid
93
93
  #
94
94
  # @since 3.0.0
95
95
  def execute(name)
96
- if document.persisted?
96
+ if !document.new_record?
97
97
  collection.find(document.atomic_selector).update(operation(name))
98
98
  document.remove_change(field)
99
99
  end
@@ -2,6 +2,7 @@
2
2
  require "mongoid/persistence/operations/insert"
3
3
  require "mongoid/persistence/operations/remove"
4
4
  require "mongoid/persistence/operations/update"
5
+ require "mongoid/persistence/operations/upsert"
5
6
  require "mongoid/persistence/operations/embedded/insert"
6
7
  require "mongoid/persistence/operations/embedded/remove"
7
8
 
@@ -192,6 +193,21 @@ module Mongoid
192
193
  def update(doc, options = {})
193
194
  Update.new(doc, options)
194
195
  end
196
+
197
+ # Get the appropriate upsert operation based on the document.
198
+ #
199
+ # @example Get the upsert operation.
200
+ # Operations.upsert(doc, options)
201
+ #
202
+ # @param [ Document ] doc The document to persist.
203
+ # @param [ Hash ] options The persistence options.
204
+ #
205
+ # @return [ Operations ] The operation.
206
+ #
207
+ # @since 3.0.0
208
+ def upsert(doc, options = {})
209
+ Upsert.new(doc, options)
210
+ end
195
211
  end
196
212
  end
197
213
  end
@@ -24,7 +24,7 @@ module Mongoid
24
24
  # @return [ true ] Always true.
25
25
  def persist
26
26
  prepare do |doc|
27
- collection.find({ _id: doc.id }).remove
27
+ collection.find(doc.atomic_selector).remove
28
28
  end
29
29
  end
30
30
  end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Persistence
4
+ module Operations
5
+
6
+ # Wraps behaviour for performing upserts in Mongodb. No matter if the
7
+ # document has been modified or not, it will be sent to the db and Mongo
8
+ # will determin whether or not to insert or update.
9
+ class Upsert
10
+ include Upsertion, Operations
11
+
12
+ # Persist the upsert operation.
13
+ #
14
+ # @example Execute the upsert.
15
+ # operation.persist
16
+ #
17
+ # @return [ true ] Always true.
18
+ #
19
+ # @since 3.0.0
20
+ def persist
21
+ prepare do
22
+ collection.find(selector).update(document.as_document, [ :upsert ])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Persistence
4
+
5
+ # Contains common logic for upsert operations.
6
+ module Upsertion
7
+
8
+ # Wrap all the common upsert logic for root docments.
9
+ #
10
+ # @example Execute common upsert logic.
11
+ # prepare do |doc|
12
+ # collection.find({ :_id => 1 }).upsert({ name: "test" }, [ :upsert ])
13
+ # end
14
+ #
15
+ # @param [ Proc ] block The block to call.
16
+ #
17
+ # @return [ true, false ] If the save passed or not.
18
+ #
19
+ # @since 3.0.0
20
+ def prepare(&block)
21
+ return false if validating? && document.invalid?(:upsert)
22
+ result = document.run_callbacks(:upsert) do
23
+ yield(document); true
24
+ end
25
+ document.post_persist unless result == false
26
+ result
27
+ end
28
+ end
29
+ end
30
+ end
@@ -83,6 +83,22 @@ module Mongoid
83
83
  metadata && metadata.macro == :embeds_one
84
84
  end
85
85
 
86
+ # Get the metadata name for this document. If no metadata was defined
87
+ # will raise an error.
88
+ #
89
+ # @example Get the metadata name.
90
+ # document.metadata_name
91
+ #
92
+ # @raise [ Errors::NoMetadata ] If no metadata is present.
93
+ #
94
+ # @return [ Symbol ] The metadata name.
95
+ #
96
+ # @since 3.0.0
97
+ def metadata_name
98
+ raise Errors::NoMetadata.new(self.class.name) unless metadata
99
+ metadata.name
100
+ end
101
+
86
102
  # Determine if the document is part of an references_many relation.
87
103
  #
88
104
  # @example Is the document in a references many?
@@ -15,7 +15,7 @@ module Mongoid
15
15
  # person.__build__(:addresses, { :id => 1 }, metadata)
16
16
  #
17
17
  # @param [ String, Symbol ] name The name of the relation.
18
- # @param [ Hash, BSON::ObjectId ] object The id or attributes to use.
18
+ # @param [ Hash, Moped::BSON::ObjectId ] object The id or attributes to use.
19
19
  # @param [ Metadata ] metadata The relation's metadata.
20
20
  # @param [ true, false ] building If we are in a build operation.
21
21
  #
@@ -25,7 +25,7 @@ module Mongoid
25
25
  bind_polymorphic_inverse_type(base, target.class.model_name)
26
26
  if inverse = metadata.inverse(target)
27
27
  if set_base_metadata
28
- bind_inverse_of_field(base, base.metadata.name)
28
+ bind_inverse_of_field(base, base.metadata_name)
29
29
  if base.referenced_many?
30
30
  target.__send__(inverse).push(base) unless Mongoid.using_identity_map?
31
31
  else