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.
- 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
@@ -28,12 +28,10 @@ module Mongoid
|
|
28
28
|
docs = args.flatten
|
29
29
|
return concat(docs) if docs.size > 1
|
30
30
|
if doc = docs.first
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
doc.save
|
36
|
-
end
|
31
|
+
append(doc)
|
32
|
+
base.add_to_set(foreign_key, doc.id)
|
33
|
+
if persistable? || _creating?
|
34
|
+
doc.save
|
37
35
|
end
|
38
36
|
end
|
39
37
|
unsynced(base, foreign_key) and self
|
@@ -52,21 +50,24 @@ module Mongoid
|
|
52
50
|
#
|
53
51
|
# @since 2.4.0
|
54
52
|
def concat(documents)
|
55
|
-
ids, inserts = [], []
|
53
|
+
ids, docs, inserts = {}, [], []
|
56
54
|
documents.each do |doc|
|
57
|
-
next
|
55
|
+
next unless doc
|
58
56
|
append(doc)
|
59
57
|
if persistable? || _creating?
|
60
|
-
ids
|
61
|
-
save_or_delay(doc, inserts)
|
58
|
+
ids[doc.id] = true
|
59
|
+
save_or_delay(doc, docs, inserts)
|
62
60
|
else
|
63
|
-
base.send(foreign_key)
|
61
|
+
existing = base.send(foreign_key)
|
62
|
+
unless existing.include?(doc.id)
|
63
|
+
existing.push(doc.id) and unsynced(base, foreign_key)
|
64
|
+
end
|
64
65
|
end
|
65
66
|
end
|
66
67
|
if persistable? || _creating?
|
67
|
-
base.push_all(foreign_key, ids)
|
68
|
+
base.push_all(foreign_key, ids.keys)
|
68
69
|
end
|
69
|
-
persist_delayed(inserts)
|
70
|
+
persist_delayed(docs, inserts)
|
70
71
|
self
|
71
72
|
end
|
72
73
|
|
data/lib/mongoid/scoping.rb
CHANGED
@@ -284,23 +284,6 @@ module Mongoid
|
|
284
284
|
SCOPE
|
285
285
|
end
|
286
286
|
|
287
|
-
# When inheriting, we want to copy the scopes from the parent class and
|
288
|
-
# set the on the child to start, mimicking the behaviour of the old
|
289
|
-
# class_inheritable_accessor that was deprecated in Rails edge.
|
290
|
-
#
|
291
|
-
# @api private
|
292
|
-
#
|
293
|
-
# @example Inherit from this class.
|
294
|
-
# Person.inherited(Doctor)
|
295
|
-
#
|
296
|
-
# @param [ Class ] subclass The inheriting class.
|
297
|
-
#
|
298
|
-
# @since 2.0.0.rc.6
|
299
|
-
def inherited(subclass)
|
300
|
-
super
|
301
|
-
subclass.scopes = scopes.dup
|
302
|
-
end
|
303
|
-
|
304
287
|
# Strip the default scope from the provided value, if it is a criteria.
|
305
288
|
# This is used by named scopes - they should not have the default scoping
|
306
289
|
# applied to them.
|
@@ -27,43 +27,112 @@ module Mongoid
|
|
27
27
|
# @since 2.0.0.rc.6
|
28
28
|
def serializable_hash(options = nil)
|
29
29
|
options ||= {}
|
30
|
+
attrs = {}
|
30
31
|
|
31
|
-
|
32
|
-
except = Array.wrap(options[:except]).map(&:to_s)
|
33
|
-
|
34
|
-
except |= ['_type'] unless Mongoid.include_type_for_serialization
|
35
|
-
|
36
|
-
field_names = self.class.attribute_names
|
37
|
-
attribute_names = (as_document.keys + field_names).sort
|
38
|
-
if only.any?
|
39
|
-
attribute_names &= only
|
40
|
-
elsif except.any?
|
41
|
-
attribute_names -= except
|
42
|
-
end
|
32
|
+
names = field_names(options)
|
43
33
|
|
44
|
-
|
45
|
-
|
46
|
-
|
34
|
+
_serializing do
|
35
|
+
method_names = Array.wrap(options[:methods]).map do |name|
|
36
|
+
name.to_s if respond_to?(name)
|
37
|
+
end.compact
|
47
38
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
if relations.has_key?(name)
|
52
|
-
value = send(name)
|
53
|
-
attrs[name] = value ? value.serializable_hash(options) : nil
|
54
|
-
elsif attribute_names.include?(name) && !fields.has_key?(name)
|
55
|
-
attrs[name] = read_attribute(name)
|
56
|
-
else
|
57
|
-
attrs[name] = send(name)
|
39
|
+
(names + method_names).each do |name|
|
40
|
+
without_autobuild do
|
41
|
+
serialize_attribute(attrs, name, names, options)
|
58
42
|
end
|
59
43
|
end
|
44
|
+
serialize_relations(attrs, options) if options[:include]
|
60
45
|
end
|
61
|
-
serialize_relations(attrs, options) if options[:include]
|
62
46
|
attrs
|
63
47
|
end
|
64
48
|
|
65
49
|
private
|
66
50
|
|
51
|
+
# Enter the serialization block.
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
# @example Begin serialization.
|
56
|
+
# document._serializing do
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# @return [ Object ] The result of the yield.
|
60
|
+
#
|
61
|
+
# @since 3.0.0
|
62
|
+
def _serializing
|
63
|
+
Threaded.begin("serialization")
|
64
|
+
yield
|
65
|
+
ensure
|
66
|
+
Threaded.exit("serialization")
|
67
|
+
end
|
68
|
+
|
69
|
+
# Are we in a serialization block? We use this to protect multiple
|
70
|
+
# unnecessary calls to #as_document.
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
# @example Are we in serialization?
|
75
|
+
# document._serializing?
|
76
|
+
#
|
77
|
+
# @return [ true, false ] If we are serializing.
|
78
|
+
#
|
79
|
+
# @since 3.0.0
|
80
|
+
def _serializing?
|
81
|
+
Threaded.executing?("serialization")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the names of all fields that will be serialized.
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
#
|
88
|
+
# @example Get all the field names.
|
89
|
+
# document.send(:field_names)
|
90
|
+
#
|
91
|
+
# @return [ Array<String> ] The names of the fields.
|
92
|
+
#
|
93
|
+
# @since 3.0.0
|
94
|
+
def field_names(options)
|
95
|
+
names = (_serializing? ? attribute_names : as_document.keys + attribute_names).uniq.sort
|
96
|
+
|
97
|
+
only = Array.wrap(options[:only]).map(&:to_s)
|
98
|
+
except = Array.wrap(options[:except]).map(&:to_s)
|
99
|
+
except |= ['_type'] unless Mongoid.include_type_for_serialization
|
100
|
+
|
101
|
+
if !only.empty?
|
102
|
+
names &= only
|
103
|
+
elsif !except.empty?
|
104
|
+
names -= except
|
105
|
+
end
|
106
|
+
names
|
107
|
+
end
|
108
|
+
|
109
|
+
# Serialize a single attribute. Handles relations, fields, and dynamic
|
110
|
+
# attributes.
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
#
|
114
|
+
# @example Serialize the attribute.
|
115
|
+
# document.serialize_attribute({}, "id" , [ "id" ])
|
116
|
+
#
|
117
|
+
# @param [ Hash ] attrs The attributes.
|
118
|
+
# @param [ String ] name The attribute name.
|
119
|
+
# @param [ Array<String> ] names The names of all attributes.
|
120
|
+
# @param [ Hash ] options The options.
|
121
|
+
#
|
122
|
+
# @return [ Object ] The attribute.
|
123
|
+
#
|
124
|
+
# @since 3.0.0
|
125
|
+
def serialize_attribute(attrs, name, names, options)
|
126
|
+
if relations.has_key?(name)
|
127
|
+
value = send(name)
|
128
|
+
attrs[name] = value ? value.serializable_hash(options) : nil
|
129
|
+
elsif names.include?(name) && !fields.has_key?(name)
|
130
|
+
attrs[name] = read_attribute(name)
|
131
|
+
else
|
132
|
+
attrs[name] = send(name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
67
136
|
# For each of the provided include options, get the relation needed and
|
68
137
|
# provide it in the hash.
|
69
138
|
#
|
data/lib/mongoid/sessions.rb
CHANGED
@@ -184,6 +184,20 @@ module Mongoid
|
|
184
184
|
@database_name ||= __database_name__
|
185
185
|
end
|
186
186
|
|
187
|
+
# Get the overridden database name. This either can be overridden by
|
188
|
+
# using +Model.with+ or by overriding at the global level via
|
189
|
+
# +Mongoid.override_database(:name)+.
|
190
|
+
#
|
191
|
+
# @example Get the overridden database name.
|
192
|
+
# Model.database_override
|
193
|
+
#
|
194
|
+
# @return [ String, Symbol ] The overridden database name.
|
195
|
+
#
|
196
|
+
# @since 3.0.0
|
197
|
+
def database_override
|
198
|
+
persistence_options.try { |opts| opts[:database] } || Threaded.database_override
|
199
|
+
end
|
200
|
+
|
187
201
|
# Get the session for this model. This is determined in the following order:
|
188
202
|
#
|
189
203
|
# 1. Any custom configuration provided by the 'store_in' macro.
|
@@ -197,11 +211,7 @@ module Mongoid
|
|
197
211
|
# @since 3.0.0
|
198
212
|
def mongo_session
|
199
213
|
session = __session__
|
200
|
-
|
201
|
-
session.use(name)
|
202
|
-
else
|
203
|
-
session.use(database_name)
|
204
|
-
end
|
214
|
+
session.use(database_override || database_name)
|
205
215
|
session
|
206
216
|
end
|
207
217
|
|
@@ -217,6 +227,20 @@ module Mongoid
|
|
217
227
|
Threaded.persistence_options(self)
|
218
228
|
end
|
219
229
|
|
230
|
+
# Get the overridden session name. This either can be overridden by
|
231
|
+
# using +Model.with+ or by overriding at the global level via
|
232
|
+
# +Mongoid.override_session(:name)+.
|
233
|
+
#
|
234
|
+
# @example Get the overridden session name.
|
235
|
+
# Model.session_override
|
236
|
+
#
|
237
|
+
# @return [ String, Symbol ] The overridden session name.
|
238
|
+
#
|
239
|
+
# @since 3.0.0
|
240
|
+
def session_override
|
241
|
+
persistence_options.try { |opts| opts[:session] } || Threaded.session_override
|
242
|
+
end
|
243
|
+
|
220
244
|
# Give this model specific custom default storage options.
|
221
245
|
#
|
222
246
|
# @example Store this model by default in "artists"
|
@@ -346,7 +370,7 @@ module Mongoid
|
|
346
370
|
#
|
347
371
|
# @since 3.0.0
|
348
372
|
def __session__
|
349
|
-
if
|
373
|
+
if name = session_override
|
350
374
|
Sessions.with_name(name)
|
351
375
|
elsif storage_options && name = storage_options[:session]
|
352
376
|
Sessions.with_name(name)
|
@@ -58,7 +58,9 @@ module Mongoid
|
|
58
58
|
#
|
59
59
|
# @since 3.0.0
|
60
60
|
def create_session(configuration)
|
61
|
+
raise Errors::NoSessionsConfig.new unless configuration
|
61
62
|
config, options = parse(configuration)
|
63
|
+
configuration.merge!(config) if configuration.delete(:uri)
|
62
64
|
session = Moped::Session.new(config[:hosts], options)
|
63
65
|
session.use(config[:database])
|
64
66
|
if authenticated?(config)
|
data/lib/mongoid/threaded.rb
CHANGED
@@ -22,6 +22,32 @@ module Mongoid
|
|
22
22
|
stack(name).push(true)
|
23
23
|
end
|
24
24
|
|
25
|
+
# Get the global database override.
|
26
|
+
#
|
27
|
+
# @example Get the global database override.
|
28
|
+
# Threaded.database_override
|
29
|
+
#
|
30
|
+
# @return [ String, Symbol ] The override.
|
31
|
+
#
|
32
|
+
# @since 3.0.0
|
33
|
+
def database_override
|
34
|
+
Thread.current["[mongoid]:db-override"]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set the global database override.
|
38
|
+
#
|
39
|
+
# @example Set the global database override.
|
40
|
+
# Threaded.database_override = :testing
|
41
|
+
#
|
42
|
+
# @param [ String, Symbol ] The global override name.
|
43
|
+
#
|
44
|
+
# @return [ String, Symbol ] The override.
|
45
|
+
#
|
46
|
+
# @since 3.0.0
|
47
|
+
def database_override=(name)
|
48
|
+
Thread.current["[mongoid]:db-override"] = name
|
49
|
+
end
|
50
|
+
|
25
51
|
# Get the database sessions from the current thread.
|
26
52
|
#
|
27
53
|
# @example Get the database sessions.
|
@@ -296,6 +322,32 @@ module Mongoid
|
|
296
322
|
Thread.current["[mongoid]:selection"] = value
|
297
323
|
end
|
298
324
|
|
325
|
+
# Get the global session override.
|
326
|
+
#
|
327
|
+
# @example Get the global session override.
|
328
|
+
# Threaded.session_override
|
329
|
+
#
|
330
|
+
# @return [ String, Symbol ] The override.
|
331
|
+
#
|
332
|
+
# @since 3.0.0
|
333
|
+
def session_override
|
334
|
+
Thread.current["[mongoid]:session-override"]
|
335
|
+
end
|
336
|
+
|
337
|
+
# Set the global session override.
|
338
|
+
#
|
339
|
+
# @example Set the global session override.
|
340
|
+
# Threaded.session_override = :testing
|
341
|
+
#
|
342
|
+
# @param [ String, Symbol ] The global override name.
|
343
|
+
#
|
344
|
+
# @return [ String, Symbol ] The override.
|
345
|
+
#
|
346
|
+
# @since 3.0.0
|
347
|
+
def session_override=(name)
|
348
|
+
Thread.current["[mongoid]:session-override"] = name
|
349
|
+
end
|
350
|
+
|
299
351
|
# Get the mongoid scope stack for chained criteria.
|
300
352
|
#
|
301
353
|
# @example Get the scope stack.
|
@@ -8,7 +8,8 @@ module Mongoid
|
|
8
8
|
|
9
9
|
included do
|
10
10
|
field :updated_at, type: Time
|
11
|
-
set_callback :
|
11
|
+
set_callback :create, :before, :set_updated_at, if: :able_to_set_updated_at?
|
12
|
+
set_callback :update, :before, :set_updated_at, if: :able_to_set_updated_at?
|
12
13
|
end
|
13
14
|
|
14
15
|
# Update the updated_at field on the Document to the current time.
|
@@ -145,7 +145,7 @@ module Mongoid
|
|
145
145
|
#
|
146
146
|
# @since 2.3.0
|
147
147
|
def filter(value)
|
148
|
-
!case_sensitive? && value ?
|
148
|
+
!case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}$/i : value
|
149
149
|
end
|
150
150
|
|
151
151
|
# Scope the criteria to the scope options provided.
|
@@ -165,6 +165,7 @@ module Mongoid
|
|
165
165
|
Array.wrap(options[:scope]).each do |item|
|
166
166
|
criteria = criteria.where(item => document.attributes[item.to_s])
|
167
167
|
end
|
168
|
+
criteria = criteria.where(deleted_at: nil) if document.paranoid?
|
168
169
|
criteria
|
169
170
|
end
|
170
171
|
|
@@ -242,7 +243,7 @@ module Mongoid
|
|
242
243
|
# @since 2.4.10
|
243
244
|
def validate_embedded(document, attribute, value)
|
244
245
|
return if skip_validation?(document)
|
245
|
-
relation = document._parent.send(document.
|
246
|
+
relation = document._parent.send(document.metadata_name)
|
246
247
|
criteria = create_criteria(relation, document, attribute, value)
|
247
248
|
add_error(document, attribute, value) if criteria.count > 1
|
248
249
|
end
|
data/lib/mongoid/version.rb
CHANGED
data/lib/rails/mongoid.rb
CHANGED
@@ -20,9 +20,12 @@ module Rails
|
|
20
20
|
next if model.index_options.empty?
|
21
21
|
unless model.embedded?
|
22
22
|
model.create_indexes
|
23
|
-
logger.info("
|
23
|
+
logger.info("MONGOID: Created indexes on #{model}:")
|
24
|
+
model.index_options.each_pair do |index, options|
|
25
|
+
logger.info("MONGOID: Index: #{index}, Options: #{options}")
|
26
|
+
end
|
24
27
|
else
|
25
|
-
logger.info("Index ignored on: #{model}, please define in the root model.")
|
28
|
+
logger.info("MONGOID: Index ignored on: #{model}, please define in the root model.")
|
26
29
|
end
|
27
30
|
end
|
28
31
|
end
|
@@ -44,7 +47,7 @@ module Rails
|
|
44
47
|
indexes = model.collection.indexes.map{ |doc| doc["name"] }
|
45
48
|
indexes.delete_one("_id_")
|
46
49
|
model.remove_indexes
|
47
|
-
logger.info("Removing indexes on: #{model} for: #{indexes.join(', ')}.")
|
50
|
+
logger.info("MONGOID: Removing indexes on: #{model} for: #{indexes.join(', ')}.")
|
48
51
|
end
|
49
52
|
end
|
50
53
|
|
@@ -63,7 +66,7 @@ module Rails
|
|
63
66
|
begin
|
64
67
|
determine_model(file, logger)
|
65
68
|
rescue => e
|
66
|
-
logger.error(%Q{Failed to determine model from #{file}:
|
69
|
+
logger.error(%Q{MONGOID: Failed to determine model from #{file}:
|
67
70
|
#{e.class}:#{e.message}
|
68
71
|
#{e.backtrace.join("\n")}
|
69
72
|
})
|
@@ -138,7 +141,7 @@ module Rails
|
|
138
141
|
name = parts.join("::")
|
139
142
|
klass = name.constantize
|
140
143
|
rescue NameError, LoadError
|
141
|
-
logger.info("Attempted to constantize #{name}, trying without namespacing.")
|
144
|
+
logger.info("MONGOID: Attempted to constantize #{name}, trying without namespacing.")
|
142
145
|
klass = parts.last.constantize
|
143
146
|
end
|
144
147
|
klass if klass.ancestors.include?(::Mongoid::Document)
|