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.
- data/CHANGELOG.md +109 -4
- data/README.md +1 -1
- data/Rakefile +1 -0
- data/lib/config/locales/en.yml +15 -1
- data/lib/mongoid.rb +17 -2
- data/lib/mongoid/atomic.rb +54 -7
- data/lib/mongoid/attributes.rb +1 -1
- data/lib/mongoid/attributes/processing.rb +1 -1
- data/lib/mongoid/callbacks.rb +6 -1
- data/lib/mongoid/components.rb +2 -1
- data/lib/mongoid/config.rb +42 -17
- data/lib/mongoid/config/environment.rb +3 -1
- data/lib/mongoid/contextual/aggregable/memory.rb +21 -10
- data/lib/mongoid/contextual/find_and_modify.rb +14 -12
- data/lib/mongoid/contextual/memory.rb +24 -1
- data/lib/mongoid/contextual/mongo.rb +148 -29
- data/lib/mongoid/copyable.rb +6 -24
- data/lib/mongoid/criteria.rb +116 -34
- data/lib/mongoid/document.rb +7 -7
- data/lib/mongoid/errors.rb +1 -0
- data/lib/mongoid/errors/no_metadata.rb +21 -0
- data/lib/mongoid/evolvable.rb +19 -0
- data/lib/mongoid/extensions.rb +1 -1
- data/lib/mongoid/extensions/array.rb +38 -1
- data/lib/mongoid/extensions/big_decimal.rb +1 -1
- data/lib/mongoid/extensions/date_time.rb +6 -1
- data/lib/mongoid/extensions/false_class.rb +12 -0
- data/lib/mongoid/extensions/float.rb +12 -0
- data/lib/mongoid/extensions/hash.rb +33 -1
- data/lib/mongoid/extensions/integer.rb +12 -0
- data/lib/mongoid/extensions/object.rb +51 -1
- data/lib/mongoid/extensions/object_id.rb +2 -1
- data/lib/mongoid/extensions/range.rb +24 -0
- data/lib/mongoid/extensions/string.rb +31 -5
- data/lib/mongoid/extensions/true_class.rb +12 -0
- data/lib/mongoid/fields.rb +20 -21
- data/lib/mongoid/fields/foreign_key.rb +23 -7
- data/lib/mongoid/fields/standard.rb +3 -3
- data/lib/mongoid/finders.rb +3 -7
- data/lib/mongoid/hierarchy.rb +19 -1
- data/lib/mongoid/identity_map.rb +20 -4
- data/lib/mongoid/indexes/validators/options.rb +1 -1
- data/lib/mongoid/multi_parameter_attributes.rb +1 -1
- data/lib/mongoid/paranoia.rb +3 -32
- data/lib/mongoid/persistence.rb +33 -15
- data/lib/mongoid/persistence/atomic/operation.rb +1 -1
- data/lib/mongoid/persistence/operations.rb +16 -0
- data/lib/mongoid/persistence/operations/remove.rb +1 -1
- data/lib/mongoid/persistence/operations/upsert.rb +28 -0
- data/lib/mongoid/persistence/upsertion.rb +30 -0
- data/lib/mongoid/relations.rb +16 -0
- data/lib/mongoid/relations/accessors.rb +1 -1
- data/lib/mongoid/relations/bindings/referenced/in.rb +1 -1
- data/lib/mongoid/relations/builder.rb +1 -1
- data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
- data/lib/mongoid/relations/builders/referenced/many.rb +1 -1
- data/lib/mongoid/relations/cascading.rb +4 -3
- data/lib/mongoid/relations/constraint.rb +1 -1
- data/lib/mongoid/relations/conversions.rb +1 -1
- data/lib/mongoid/relations/embedded/batchable.rb +3 -2
- data/lib/mongoid/relations/embedded/many.rb +4 -4
- data/lib/mongoid/relations/embedded/one.rb +1 -1
- data/lib/mongoid/relations/metadata.rb +67 -23
- data/lib/mongoid/relations/nested_builder.rb +2 -2
- data/lib/mongoid/relations/proxy.rb +9 -7
- data/lib/mongoid/relations/referenced/many.rb +69 -25
- data/lib/mongoid/relations/referenced/many_to_many.rb +14 -13
- data/lib/mongoid/scoping.rb +0 -17
- data/lib/mongoid/serialization.rb +95 -26
- data/lib/mongoid/sessions.rb +30 -6
- data/lib/mongoid/sessions/factory.rb +2 -0
- data/lib/mongoid/threaded.rb +52 -0
- data/lib/mongoid/timestamps/created.rb +1 -1
- data/lib/mongoid/timestamps/updated.rb +2 -1
- data/lib/mongoid/validations/uniqueness.rb +3 -2
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +8 -0
- data/lib/rails/mongoid.rb +8 -5
- metadata +30 -13
- data/lib/mongoid/collections/retry.rb +0 -58
- data/lib/mongoid/javascript.rb +0 -20
- 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?
|
data/lib/mongoid/finders.rb
CHANGED
@@ -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
|
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
|
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
|
data/lib/mongoid/hierarchy.rb
CHANGED
@@ -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.
|
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
|
data/lib/mongoid/identity_map.rb
CHANGED
@@ -16,8 +16,7 @@ module Mongoid
|
|
16
16
|
#
|
17
17
|
# @since 2.4.10
|
18
18
|
def clear_many(klass, selector)
|
19
|
-
|
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] ||= []
|
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
|
-
|
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 =~
|
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
|
data/lib/mongoid/paranoia.rb
CHANGED
@@ -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
|
data/lib/mongoid/persistence.rb
CHANGED
@@ -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
|
76
|
-
self.class.fail_validate!(self)
|
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)
|
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
|
-
#
|
178
|
-
#
|
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
|
204
|
+
# @param [ Hash ] options The validation options.
|
184
205
|
#
|
185
|
-
# @return [ true
|
206
|
+
# @return [ true ] True.
|
207
|
+
#
|
208
|
+
# @since 3.0.0
|
186
209
|
def upsert(options = {})
|
187
|
-
|
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)
|
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
|
@@ -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
|
@@ -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
|
data/lib/mongoid/relations.rb
CHANGED
@@ -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.
|
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
|