mongoid 9.0.2 → 9.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mongoid/attributes/readonly.rb +8 -3
- data/lib/mongoid/clients/options.rb +14 -1
- data/lib/mongoid/clients/sessions.rb +1 -0
- data/lib/mongoid/criteria/queryable/selectable.rb +1 -1
- data/lib/mongoid/equality.rb +1 -0
- data/lib/mongoid/loadable.rb +72 -8
- data/lib/mongoid/matcher.rb +15 -1
- data/lib/mongoid/persistence_context.rb +14 -9
- data/lib/mongoid/railties/controller_runtime.rb +2 -2
- data/lib/mongoid/serializable.rb +7 -7
- data/lib/mongoid/threaded.rb +96 -28
- data/lib/mongoid/timestamps/timeless.rb +4 -1
- data/lib/mongoid/touchable.rb +1 -1
- data/lib/mongoid/traversable.rb +25 -2
- data/lib/mongoid/validatable/associated.rb +5 -2
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/active_job_spec.rb +24 -20
- data/spec/integration/app_spec.rb +9 -1
- data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +4 -0
- data/spec/mongoid/attributes/readonly_spec.rb +19 -0
- data/spec/mongoid/clients/options_spec.rb +127 -2
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +29 -0
- data/spec/mongoid/equality_spec.rb +6 -0
- data/spec/mongoid/interceptable_spec.rb +12 -0
- data/spec/mongoid/interceptable_spec_models.rb +12 -0
- data/spec/mongoid/loadable_spec.rb +86 -0
- data/spec/mongoid/persistence_context_spec.rb +39 -0
- data/spec/mongoid/railties/bson_object_id_serializer_spec.rb +18 -12
- data/spec/mongoid/threaded_spec.rb +24 -5
- data/spec/mongoid/validatable/associated_spec.rb +14 -4
- data/spec/rails/controller_extension/controller_runtime_spec.rb +14 -14
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ac7ac8e60ba9d953a5a809d17b7fca7a2501988cdec50d12348af3b7022f615
|
4
|
+
data.tar.gz: 27a2bceb132e4f6b33d8be6b70ac8074b8de421c5c1efdb03f3ad2fc97d62364
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 31cea6e77afe05358ff9cdaf5bc861807a93ce9c89e7cfd9209c0f35a2df816fb8d4d595faafbab2665b2dab776f523d0b96eece4b249626f976ec685765d5a5
|
7
|
+
data.tar.gz: 1367fb446dae452b73227c099e72ed2f13bd2702434e8050c09f3c1ae2ad97b5346371252ce3cfa3442151353bd8c8ef9a409cd2de75edb7cdf20ff3322107c7
|
@@ -23,7 +23,7 @@ module Mongoid
|
|
23
23
|
# @return [ true | false ] If the document is new, or if the field is not
|
24
24
|
# readonly.
|
25
25
|
def attribute_writable?(name)
|
26
|
-
new_record? || (!readonly_attributes.include?(name) && _loaded?(name))
|
26
|
+
new_record? || (!self.class.readonly_attributes.include?(name) && _loaded?(name))
|
27
27
|
end
|
28
28
|
|
29
29
|
private
|
@@ -63,12 +63,17 @@ module Mongoid
|
|
63
63
|
# end
|
64
64
|
#
|
65
65
|
# @param [ Symbol... ] *names The names of the fields.
|
66
|
+
# @note When a parent class contains readonly attributes and is then
|
67
|
+
# inherited by a child class, the child class will inherit the
|
68
|
+
# parent's readonly attributes at the time of its creation.
|
69
|
+
# Updating the parent does not propagate down to child classes after wards.
|
66
70
|
def attr_readonly(*names)
|
71
|
+
self.readonly_attributes = self.readonly_attributes.dup
|
67
72
|
names.each do |name|
|
68
|
-
readonly_attributes << database_field_name(name)
|
73
|
+
self.readonly_attributes << database_field_name(name)
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
72
77
|
end
|
73
78
|
end
|
74
|
-
end
|
79
|
+
end
|
@@ -86,7 +86,7 @@ module Mongoid
|
|
86
86
|
else
|
87
87
|
PersistenceContext.get(self) ||
|
88
88
|
PersistenceContext.get(self.class) ||
|
89
|
-
PersistenceContext.new(self.class,
|
89
|
+
PersistenceContext.new(self.class, default_storage_options)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -112,6 +112,19 @@ module Mongoid
|
|
112
112
|
|
113
113
|
private
|
114
114
|
|
115
|
+
def default_storage_options
|
116
|
+
# Nothing is overridden, we use either the default storage_options
|
117
|
+
# or storage_options defined for the document class.
|
118
|
+
return storage_options if Threaded.client_override.nil? && Threaded.database_override.nil?
|
119
|
+
|
120
|
+
storage_options.tap do |opts|
|
121
|
+
# Globally overridden client replaces client defined for the document class.
|
122
|
+
opts[:client] = Threaded.client_override unless Threaded.client_override.nil?
|
123
|
+
# Globally overridden database replaces database defined for the document class.
|
124
|
+
opts[:database] = Threaded.database_override unless Threaded.database_override.nil?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
115
128
|
def set_persistence_context(options_or_context)
|
116
129
|
PersistenceContext.set(self, options_or_context)
|
117
130
|
end
|
@@ -553,7 +553,7 @@ module Mongoid
|
|
553
553
|
# @return [ Selectable ] The new selectable.
|
554
554
|
def not(*criteria)
|
555
555
|
if criteria.empty?
|
556
|
-
dup.tap { |query| query.negating =
|
556
|
+
dup.tap { |query| query.negating = !query.negating }
|
557
557
|
else
|
558
558
|
criteria.compact.inject(self.clone) do |c, new_s|
|
559
559
|
if new_s.is_a?(Selectable)
|
data/lib/mongoid/equality.rb
CHANGED
data/lib/mongoid/loadable.rb
CHANGED
@@ -10,6 +10,13 @@ module Mongoid
|
|
10
10
|
# (See #model_paths.)
|
11
11
|
DEFAULT_MODEL_PATHS = %w( ./app/models ./lib/models ).freeze
|
12
12
|
|
13
|
+
# The default list of glob patterns that match paths to ignore when loading
|
14
|
+
# models. Defaults to '*/models/concerns/*', which Rails uses for extensions
|
15
|
+
# to models (and which cause errors when loaded out of order).
|
16
|
+
#
|
17
|
+
# See #ignore_patterns.
|
18
|
+
DEFAULT_IGNORE_PATTERNS = %w( */models/concerns/* ).freeze
|
19
|
+
|
13
20
|
# Search a list of model paths to get every model and require it, so
|
14
21
|
# that indexing and inheritance work in both development and production
|
15
22
|
# with the same results.
|
@@ -24,17 +31,47 @@ module Mongoid
|
|
24
31
|
# for model files. These must either be absolute paths, or relative to
|
25
32
|
# the current working directory.
|
26
33
|
def load_models(paths = model_paths)
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
files = files_under_paths(paths)
|
35
|
+
|
36
|
+
files.sort.each do |file|
|
37
|
+
load_model(file)
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Given a list of paths, return all ruby files under that path (or, if
|
44
|
+
# `preload_models` is a list of model names, returns only the files for
|
45
|
+
# those named models).
|
46
|
+
#
|
47
|
+
# @param [ Array<String> ] paths the list of paths to search
|
48
|
+
#
|
49
|
+
# @return [ Array<String> ] the normalized file names, suitable for loading
|
50
|
+
# via `require_dependency` or `require`.
|
51
|
+
def files_under_paths(paths)
|
52
|
+
paths.flat_map { |path| files_under_path(path) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# Given a single path, returns all ruby files under that path (or, if
|
56
|
+
# `preload_models` is a list of model names, returns only the files for
|
57
|
+
# those named models).
|
58
|
+
#
|
59
|
+
# @param [ String ] path the path to search
|
60
|
+
#
|
61
|
+
# @return [ Array<String> ] the normalized file names, suitable for loading
|
62
|
+
# via `require_dependency` or `require`.
|
63
|
+
def files_under_path(path)
|
64
|
+
files = if preload_models.resizable?
|
65
|
+
preload_models.
|
66
|
+
map { |model| "#{path}/#{model.underscore}.rb" }.
|
67
|
+
select { |file_name| File.exists?(file_name) }
|
30
68
|
else
|
31
|
-
|
69
|
+
Dir.glob("#{path}/**/*.rb").
|
70
|
+
reject { |file_name| ignored?(file_name) }
|
32
71
|
end
|
33
72
|
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
73
|
+
# strip the path and the suffix from each entry
|
74
|
+
files.map { |file| file.gsub(/^#{path}\// , "").gsub(/\.rb$/, "") }
|
38
75
|
end
|
39
76
|
|
40
77
|
# A convenience method for loading a model's file. If Rails'
|
@@ -71,6 +108,14 @@ module Mongoid
|
|
71
108
|
DEFAULT_MODEL_PATHS
|
72
109
|
end
|
73
110
|
|
111
|
+
# Returns the array of glob patterns that determine whether a given
|
112
|
+
# path should be ignored by the model loader.
|
113
|
+
#
|
114
|
+
# @return [ Array<String> ] the array of ignore patterns
|
115
|
+
def ignore_patterns
|
116
|
+
@ignore_patterns ||= DEFAULT_IGNORE_PATTERNS.dup
|
117
|
+
end
|
118
|
+
|
74
119
|
# Sets the model paths to the given array of paths. These are the paths
|
75
120
|
# where the application's model definitions are located.
|
76
121
|
#
|
@@ -78,6 +123,25 @@ module Mongoid
|
|
78
123
|
def model_paths=(paths)
|
79
124
|
@model_paths = paths
|
80
125
|
end
|
126
|
+
|
127
|
+
# Sets the ignore patterns to the given array of patterns. These are glob
|
128
|
+
# patterns that determine whether a given path should be ignored by the
|
129
|
+
# model loader or not.
|
130
|
+
#
|
131
|
+
# @param [ Array<String> ] patterns The list of glob patterns
|
132
|
+
def ignore_patterns=(patterns)
|
133
|
+
@ignore_patterns = patterns
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns true if the given file path matches any of the ignore patterns.
|
137
|
+
#
|
138
|
+
# @param [ String ] file_path The file path to consider
|
139
|
+
#
|
140
|
+
# @return [ true | false ] whether or not the given file path should be
|
141
|
+
# ignored.
|
142
|
+
def ignored?(file_path)
|
143
|
+
ignore_patterns.any? { |pattern| File.fnmatch?(pattern, file_path) }
|
144
|
+
end
|
81
145
|
end
|
82
146
|
|
83
147
|
end
|
data/lib/mongoid/matcher.rb
CHANGED
@@ -39,11 +39,25 @@ module Mongoid
|
|
39
39
|
# from and behaves identically to association traversal for the purposes
|
40
40
|
# of, for example, subsequent array element retrieval.
|
41
41
|
#
|
42
|
-
# @param [ Document | Hash ] document The document to extract from.
|
42
|
+
# @param [ Document | Hash | String ] document The document to extract from.
|
43
43
|
# @param [ String ] key The key path to extract.
|
44
44
|
#
|
45
45
|
# @return [ Object | Array ] Field value or values.
|
46
46
|
module_function def extract_attribute(document, key)
|
47
|
+
# The matcher system will wind up sending atomic values to this as well,
|
48
|
+
# when attepting to match more complex types. If anything other than a
|
49
|
+
# Document or a Hash is given, we'll short-circuit the logic and just
|
50
|
+
# return an empty array.
|
51
|
+
return [] unless document.is_a?(Hash) || document.is_a?(Document)
|
52
|
+
|
53
|
+
# Performance optimization; if the key does not include a '.' character,
|
54
|
+
# it must reference an immediate attribute of the document.
|
55
|
+
unless key.include?('.')
|
56
|
+
hash = document.respond_to?(:attributes) ? document.attributes : document
|
57
|
+
key = find_exact_key(hash, key)
|
58
|
+
return key ? [ hash[key] ] : []
|
59
|
+
end
|
60
|
+
|
47
61
|
if document.respond_to?(:as_attributes, true)
|
48
62
|
# If a document has hash fields, as_attributes would keep those fields
|
49
63
|
# as Hash instances which do not offer indifferent access.
|
@@ -117,12 +117,15 @@ module Mongoid
|
|
117
117
|
def client
|
118
118
|
@client ||= begin
|
119
119
|
client = Clients.with_name(client_name)
|
120
|
+
options = client_options
|
121
|
+
|
120
122
|
if database_name_option
|
121
123
|
client = client.use(database_name)
|
124
|
+
options = options.except(:database, 'database')
|
122
125
|
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
+
|
127
|
+
client = client.with(options) unless options.empty?
|
128
|
+
|
126
129
|
client
|
127
130
|
end
|
128
131
|
end
|
@@ -135,7 +138,7 @@ module Mongoid
|
|
135
138
|
# @return [ Symbol ] The client name for this persistence
|
136
139
|
# context.
|
137
140
|
def client_name
|
138
|
-
@client_name ||= options[:client] ||
|
141
|
+
@client_name ||= __evaluate__(options[:client]) ||
|
139
142
|
Threaded.client_override ||
|
140
143
|
__evaluate__(storage_options[:client])
|
141
144
|
end
|
@@ -282,6 +285,10 @@ module Mongoid
|
|
282
285
|
# @api private
|
283
286
|
PERSISTENCE_CONTEXT_KEY = :"[mongoid]:persistence_context"
|
284
287
|
|
288
|
+
def context_store
|
289
|
+
Threaded.get(PERSISTENCE_CONTEXT_KEY) { {} }
|
290
|
+
end
|
291
|
+
|
285
292
|
# Get the persistence context for a given object from the thread local
|
286
293
|
# storage.
|
287
294
|
#
|
@@ -292,8 +299,7 @@ module Mongoid
|
|
292
299
|
#
|
293
300
|
# @api private
|
294
301
|
def get_context(object)
|
295
|
-
|
296
|
-
Thread.current[PERSISTENCE_CONTEXT_KEY][object.object_id]
|
302
|
+
context_store[object.object_id]
|
297
303
|
end
|
298
304
|
|
299
305
|
# Store persistence context for a given object in the thread local
|
@@ -305,10 +311,9 @@ module Mongoid
|
|
305
311
|
# @api private
|
306
312
|
def store_context(object, context)
|
307
313
|
if context.nil?
|
308
|
-
|
314
|
+
context_store.delete(object.object_id)
|
309
315
|
else
|
310
|
-
|
311
|
-
Thread.current[PERSISTENCE_CONTEXT_KEY][object.object_id] = context
|
316
|
+
context_store[object.object_id] = context
|
312
317
|
end
|
313
318
|
end
|
314
319
|
end
|
@@ -78,7 +78,7 @@ module Mongoid
|
|
78
78
|
#
|
79
79
|
# @return [ Integer ] The runtime value.
|
80
80
|
def self.runtime
|
81
|
-
|
81
|
+
Threaded.get(VARIABLE_NAME) { 0 }
|
82
82
|
end
|
83
83
|
|
84
84
|
# Set the runtime value on the current thread.
|
@@ -87,7 +87,7 @@ module Mongoid
|
|
87
87
|
#
|
88
88
|
# @return [ Integer ] The runtime value.
|
89
89
|
def self.runtime= value
|
90
|
-
|
90
|
+
Threaded.set(VARIABLE_NAME, value)
|
91
91
|
end
|
92
92
|
|
93
93
|
# Reset the runtime value to zero the current thread.
|
data/lib/mongoid/serializable.rb
CHANGED
@@ -13,13 +13,13 @@ module Mongoid
|
|
13
13
|
included do
|
14
14
|
|
15
15
|
class << self
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
def include_root_in_json
|
17
|
+
@include_root_in_json.nil? ? ::Mongoid.include_root_in_json : @include_root_in_json
|
18
|
+
end
|
19
|
+
|
20
|
+
def include_root_in_json=(new_value)
|
21
|
+
@include_root_in_json = new_value
|
22
|
+
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
data/lib/mongoid/threaded.rb
CHANGED
@@ -18,6 +18,7 @@ module Mongoid
|
|
18
18
|
CURRENT_SCOPE_KEY = '[mongoid]:current-scope'
|
19
19
|
|
20
20
|
AUTOSAVES_KEY = '[mongoid]:autosaves'
|
21
|
+
|
21
22
|
VALIDATIONS_KEY = '[mongoid]:validations'
|
22
23
|
|
23
24
|
STACK_KEYS = Hash.new do |hash, key|
|
@@ -36,6 +37,75 @@ module Mongoid
|
|
36
37
|
|
37
38
|
extend self
|
38
39
|
|
40
|
+
# Queries the thread-local variable with the given name. If a block is
|
41
|
+
# given, and the variable does not already exist, the return value of the
|
42
|
+
# block will be set as the value of the variable before returning it.
|
43
|
+
#
|
44
|
+
# It is very important that applications (and espcially Mongoid)
|
45
|
+
# use this method instead of Thread#[], since Thread#[] is actually for
|
46
|
+
# fiber-local variables, and Mongoid uses Fibers as an implementation
|
47
|
+
# detail in some callbacks. Putting thread-local state in a fiber-local
|
48
|
+
# store will result in the state being invisible when relevant callbacks are
|
49
|
+
# run in a different fiber.
|
50
|
+
#
|
51
|
+
# Affected callbacks are cascading callbacks on embedded children.
|
52
|
+
#
|
53
|
+
# @param [ String | Symbol ] key the name of the variable to query
|
54
|
+
# @param [ Proc ] default an optional block that must return the default
|
55
|
+
# (initial) value of this variable.
|
56
|
+
#
|
57
|
+
# @return [ Object | nil ] the value of the queried variable, or nil if
|
58
|
+
# it is not set and no default was given.
|
59
|
+
def get(key, &default)
|
60
|
+
result = Thread.current.thread_variable_get(key)
|
61
|
+
|
62
|
+
if result.nil? && default
|
63
|
+
result = yield
|
64
|
+
set(key, result)
|
65
|
+
end
|
66
|
+
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sets a thread-local variable with the given name to the given value.
|
71
|
+
# See #get for a discussion of why this method is necessary, and why
|
72
|
+
# Thread#[]= should be avoided in cascading callbacks on embedded children.
|
73
|
+
#
|
74
|
+
# @param [ String | Symbol ] key the name of the variable to set.
|
75
|
+
# @param [ Object | nil ] value the value of the variable to set (or `nil`
|
76
|
+
# if you wish to unset the variable)
|
77
|
+
def set(key, value)
|
78
|
+
Thread.current.thread_variable_set(key, value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Removes the named variable from thread-local storage.
|
82
|
+
#
|
83
|
+
# @param [ String | Symbol ] key the name of the variable to remove.
|
84
|
+
def delete(key)
|
85
|
+
set(key, nil)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Queries the presence of a named variable in thread-local storage.
|
89
|
+
#
|
90
|
+
# @param [ String | Symbol ] key the name of the variable to query.
|
91
|
+
#
|
92
|
+
# @return [ true | false ] whether the given variable is present or not.
|
93
|
+
def has?(key)
|
94
|
+
# Here we have a classic example of JRuby not behaving like MRI. In
|
95
|
+
# MRI, if you set a thread variable to nil, it removes it from the list
|
96
|
+
# and subsequent calls to thread_variable?(key) will return false. Not
|
97
|
+
# so with JRuby. Once set, you cannot unset the thread variable.
|
98
|
+
#
|
99
|
+
# However, because setting a variable to nil is supposed to remove it,
|
100
|
+
# we can assume a nil-valued variable doesn't actually exist.
|
101
|
+
|
102
|
+
# So, instead of this:
|
103
|
+
# Thread.current.thread_variable?(key)
|
104
|
+
|
105
|
+
# We have to do this:
|
106
|
+
!get(key).nil?
|
107
|
+
end
|
108
|
+
|
39
109
|
# Begin entry into a named thread local stack.
|
40
110
|
#
|
41
111
|
# @example Begin entry into the stack.
|
@@ -55,7 +125,7 @@ module Mongoid
|
|
55
125
|
#
|
56
126
|
# @return [ String | Symbol ] The override.
|
57
127
|
def database_override
|
58
|
-
|
128
|
+
get(DATABASE_OVERRIDE_KEY)
|
59
129
|
end
|
60
130
|
|
61
131
|
# Set the global database override.
|
@@ -67,7 +137,7 @@ module Mongoid
|
|
67
137
|
#
|
68
138
|
# @return [ String | Symbol ] The override.
|
69
139
|
def database_override=(name)
|
70
|
-
|
140
|
+
set(DATABASE_OVERRIDE_KEY, name)
|
71
141
|
end
|
72
142
|
|
73
143
|
# Are in the middle of executing the named stack
|
@@ -103,7 +173,7 @@ module Mongoid
|
|
103
173
|
#
|
104
174
|
# @return [ Array ] The stack.
|
105
175
|
def stack(name)
|
106
|
-
|
176
|
+
get(STACK_KEYS[name]) { [] }
|
107
177
|
end
|
108
178
|
|
109
179
|
# Begin autosaving a document on the current thread.
|
@@ -177,7 +247,7 @@ module Mongoid
|
|
177
247
|
#
|
178
248
|
# @return [ String | Symbol ] The override.
|
179
249
|
def client_override
|
180
|
-
|
250
|
+
get(CLIENT_OVERRIDE_KEY)
|
181
251
|
end
|
182
252
|
|
183
253
|
# Set the global client override.
|
@@ -189,7 +259,7 @@ module Mongoid
|
|
189
259
|
#
|
190
260
|
# @return [ String | Symbol ] The override.
|
191
261
|
def client_override=(name)
|
192
|
-
|
262
|
+
set(CLIENT_OVERRIDE_KEY, name)
|
193
263
|
end
|
194
264
|
|
195
265
|
# Get the current Mongoid scope.
|
@@ -202,12 +272,12 @@ module Mongoid
|
|
202
272
|
#
|
203
273
|
# @return [ Criteria ] The scope.
|
204
274
|
def current_scope(klass = nil)
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
]
|
275
|
+
current_scope = get(CURRENT_SCOPE_KEY)
|
276
|
+
|
277
|
+
if klass && current_scope.respond_to?(:keys)
|
278
|
+
current_scope[current_scope.keys.find { |k| k <= klass }]
|
209
279
|
else
|
210
|
-
|
280
|
+
current_scope
|
211
281
|
end
|
212
282
|
end
|
213
283
|
|
@@ -220,7 +290,7 @@ module Mongoid
|
|
220
290
|
#
|
221
291
|
# @return [ Criteria ] The scope.
|
222
292
|
def current_scope=(scope)
|
223
|
-
|
293
|
+
set(CURRENT_SCOPE_KEY, scope)
|
224
294
|
end
|
225
295
|
|
226
296
|
# Set the current Mongoid scope. Safe for multi-model scope chaining.
|
@@ -236,8 +306,8 @@ module Mongoid
|
|
236
306
|
if scope.nil?
|
237
307
|
unset_current_scope(klass)
|
238
308
|
else
|
239
|
-
|
240
|
-
|
309
|
+
current_scope = get(CURRENT_SCOPE_KEY) { {} }
|
310
|
+
current_scope[klass] = scope
|
241
311
|
end
|
242
312
|
end
|
243
313
|
|
@@ -284,7 +354,7 @@ module Mongoid
|
|
284
354
|
#
|
285
355
|
# @return [ Hash ] The current autosaves.
|
286
356
|
def autosaves
|
287
|
-
|
357
|
+
get(AUTOSAVES_KEY) { {} }
|
288
358
|
end
|
289
359
|
|
290
360
|
# Get all validations on the current thread.
|
@@ -294,7 +364,7 @@ module Mongoid
|
|
294
364
|
#
|
295
365
|
# @return [ Hash ] The current validations.
|
296
366
|
def validations
|
297
|
-
|
367
|
+
get(VALIDATIONS_KEY) { {} }
|
298
368
|
end
|
299
369
|
|
300
370
|
# Get all autosaves on the current thread for the class.
|
@@ -376,9 +446,7 @@ module Mongoid
|
|
376
446
|
# @return [ Set<Mongoid::Document> ] Collection of modified documents before
|
377
447
|
# it was cleared.
|
378
448
|
def clear_modified_documents(session)
|
379
|
-
modified_documents[
|
380
|
-
ensure
|
381
|
-
modified_documents[session].clear
|
449
|
+
modified_documents.delete(session) || []
|
382
450
|
end
|
383
451
|
|
384
452
|
# Queries whether document callbacks should be executed by default for the
|
@@ -390,8 +458,8 @@ module Mongoid
|
|
390
458
|
# @return [ true | false ] Whether or not document callbacks should be
|
391
459
|
# executed by default.
|
392
460
|
def execute_callbacks?
|
393
|
-
if
|
394
|
-
|
461
|
+
if has?(EXECUTE_CALLBACKS)
|
462
|
+
get(EXECUTE_CALLBACKS)
|
395
463
|
else
|
396
464
|
true
|
397
465
|
end
|
@@ -404,7 +472,7 @@ module Mongoid
|
|
404
472
|
# @param flag [ true | false ] Whether or not document callbacks should be
|
405
473
|
# executed by default.
|
406
474
|
def execute_callbacks=(flag)
|
407
|
-
|
475
|
+
set(EXECUTE_CALLBACKS, flag)
|
408
476
|
end
|
409
477
|
|
410
478
|
# Returns the thread store of sessions.
|
@@ -413,7 +481,7 @@ module Mongoid
|
|
413
481
|
#
|
414
482
|
# @api private
|
415
483
|
def sessions
|
416
|
-
|
484
|
+
get(SESSIONS_KEY) { {}.compare_by_identity }
|
417
485
|
end
|
418
486
|
|
419
487
|
# Returns the thread store of modified documents.
|
@@ -423,9 +491,7 @@ module Mongoid
|
|
423
491
|
#
|
424
492
|
# @api private
|
425
493
|
def modified_documents
|
426
|
-
|
427
|
-
h[k] = Set.new
|
428
|
-
end
|
494
|
+
get(MODIFIED_DOCUMENTS_KEY) { Hash.new { |h, k| h[k] = Set.new } }
|
429
495
|
end
|
430
496
|
|
431
497
|
private
|
@@ -435,10 +501,12 @@ module Mongoid
|
|
435
501
|
#
|
436
502
|
# @param klass [ Class ] the class to remove from the current scope.
|
437
503
|
def unset_current_scope(klass)
|
438
|
-
return unless
|
504
|
+
return unless has?(CURRENT_SCOPE_KEY)
|
505
|
+
|
506
|
+
scope = get(CURRENT_SCOPE_KEY)
|
507
|
+
scope.delete(klass)
|
439
508
|
|
440
|
-
|
441
|
-
Thread.current[CURRENT_SCOPE_KEY] = nil if Thread.current[CURRENT_SCOPE_KEY].empty?
|
509
|
+
delete(CURRENT_SCOPE_KEY) if scope.empty?
|
442
510
|
end
|
443
511
|
end
|
444
512
|
end
|
@@ -46,6 +46,9 @@ module Mongoid
|
|
46
46
|
class << self
|
47
47
|
extend Forwardable
|
48
48
|
|
49
|
+
# The key to use to store the timeless table
|
50
|
+
TIMELESS_TABLE_KEY = '[mongoid]:timeless'
|
51
|
+
|
49
52
|
# Returns the in-memory thread cache of classes
|
50
53
|
# for which to skip timestamping.
|
51
54
|
#
|
@@ -53,7 +56,7 @@ module Mongoid
|
|
53
56
|
#
|
54
57
|
# @api private
|
55
58
|
def timeless_table
|
56
|
-
|
59
|
+
Threaded.get(TIMELESS_TABLE_KEY) { Hash.new }
|
57
60
|
end
|
58
61
|
|
59
62
|
def_delegators :timeless_table, :[]=, :[]
|
data/lib/mongoid/touchable.rb
CHANGED
@@ -195,7 +195,7 @@ module Mongoid
|
|
195
195
|
# @return [ Hash ] The hash that contains touch callback suppression
|
196
196
|
# statuses
|
197
197
|
def touch_callback_statuses
|
198
|
-
|
198
|
+
Threaded.get(SUPPRESS_TOUCH_CALLBACKS_KEY) { {} }
|
199
199
|
end
|
200
200
|
|
201
201
|
# Define the method that will get called for touching belongs_to
|
data/lib/mongoid/traversable.rb
CHANGED
@@ -8,6 +8,29 @@ module Mongoid
|
|
8
8
|
# around traversing the document graph.
|
9
9
|
module Traversable
|
10
10
|
extend ActiveSupport::Concern
|
11
|
+
# This code is extracted from ActiveSupport so that we do not depend on
|
12
|
+
# their private API that may change at any time.
|
13
|
+
# This code should be reviewed and maybe removed when implementing
|
14
|
+
# https://jira.mongodb.org/browse/MONGOID-5832
|
15
|
+
class << self
|
16
|
+
# @api private
|
17
|
+
def __redefine(owner, name, value)
|
18
|
+
if owner.singleton_class?
|
19
|
+
owner.redefine_method(name) { value }
|
20
|
+
owner.send(:public, name)
|
21
|
+
end
|
22
|
+
owner.redefine_singleton_method(name) { value }
|
23
|
+
owner.singleton_class.send(:public, name)
|
24
|
+
owner.redefine_singleton_method("#{name}=") do |new_value|
|
25
|
+
if owner.equal?(self)
|
26
|
+
value = new_value
|
27
|
+
else
|
28
|
+
::Mongoid::Traversable.redefine(self, name, new_value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
owner.singleton_class.send(:public, "#{name}=")
|
32
|
+
end
|
33
|
+
end
|
11
34
|
|
12
35
|
# Class-level methods for the Traversable behavior.
|
13
36
|
module ClassMethods
|
@@ -105,7 +128,7 @@ module Mongoid
|
|
105
128
|
if value
|
106
129
|
Mongoid::Fields::Validators::Macro.validate_field_name(self, value)
|
107
130
|
value = value.to_s
|
108
|
-
|
131
|
+
::Mongoid::Traversable.__redefine(self, 'discriminator_key', value)
|
109
132
|
else
|
110
133
|
# When discriminator key is set to nil, replace the class's definition
|
111
134
|
# of the discriminator key reader (provided by class_attribute earlier)
|
@@ -119,7 +142,7 @@ module Mongoid
|
|
119
142
|
# an existing field.
|
120
143
|
# This condition also checks if the class has any descendants, because
|
121
144
|
# if it doesn't then it doesn't need a discriminator key.
|
122
|
-
return
|
145
|
+
return if fields.key?(discriminator_key) || descendants.empty?
|
123
146
|
|
124
147
|
default_proc = -> { self.class.discriminator_value }
|
125
148
|
field(discriminator_key, default: default_proc, type: String)
|