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