mongoid 7.5.1 → 7.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Rakefile +25 -0
- data/lib/mongoid/association/embedded/batchable.rb +20 -3
- data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
- data/lib/mongoid/cacheable.rb +2 -2
- data/lib/mongoid/config.rb +6 -0
- data/lib/mongoid/contextual/mongo.rb +24 -7
- data/lib/mongoid/criteria/queryable/selector.rb +1 -1
- data/lib/mongoid/criteria/queryable/storable.rb +1 -1
- data/lib/mongoid/document.rb +8 -1
- data/lib/mongoid/persistence_context.rb +57 -6
- data/lib/mongoid/shardable.rb +35 -11
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +21 -0
- data/spec/mongoid/association/embedded/embeds_many_models.rb +121 -0
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +30 -0
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +20 -0
- data/spec/mongoid/attributes_spec.rb +44 -0
- data/spec/mongoid/cacheable_spec.rb +3 -3
- data/spec/mongoid/clients_spec.rb +30 -0
- data/spec/mongoid/config_spec.rb +7 -0
- data/spec/mongoid/contextual/mongo_spec.rb +23 -3
- data/spec/mongoid/criteria/queryable/selector_spec.rb +75 -2
- data/spec/mongoid/criteria/queryable/storable_spec.rb +72 -0
- data/spec/mongoid/persistence_context_spec.rb +26 -1
- data/spec/mongoid/shardable_models.rb +14 -0
- data/spec/mongoid/shardable_spec.rb +157 -51
- data/spec/mongoid_spec.rb +7 -1
- data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
- data/spec/shared/shlib/server.sh +5 -5
- data.tar.gz.sig +0 -0
- metadata +632 -628
- metadata.gz.sig +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce7f228fdc033dc1142f46f4771395d36acafc97e0c144599cd063af537c7ae3
|
4
|
+
data.tar.gz: 15f8f0efbb076eb03f25f23898cbe27d044e7441e4a7ae4bc8d22f036838bb98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91f40b7136ae729e6831c5033c60c0a1e2207cd98d80bf5caaef50844230d6830efe555748571d9bdef62a101f75ffd21d2a7942278f84b68e2a51fc6a8f20df
|
7
|
+
data.tar.gz: 3555b90da32865e1faadb9efccc6db0262adea620b4dd473d5dc1ee533b00451039dfaacd982ef5c8f6be4009ccff589119b2f512971a1b5ac509bb7ae4aa96e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/Rakefile
CHANGED
@@ -11,6 +11,15 @@ $: << File.join(ROOT, 'spec/shared/lib')
|
|
11
11
|
require "rake"
|
12
12
|
require "rspec/core/rake_task"
|
13
13
|
require 'mrss/spec_organizer'
|
14
|
+
require 'rubygems/package'
|
15
|
+
require 'rubygems/security/policies'
|
16
|
+
|
17
|
+
def signed_gem?(path_to_gem)
|
18
|
+
Gem::Package.new(path_to_gem, Gem::Security::HighSecurity).verify
|
19
|
+
true
|
20
|
+
rescue Gem::Security::Exception => e
|
21
|
+
false
|
22
|
+
end
|
14
23
|
|
15
24
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
16
25
|
require "mongoid/version"
|
@@ -103,3 +112,19 @@ namespace :release do
|
|
103
112
|
end
|
104
113
|
end
|
105
114
|
end
|
115
|
+
|
116
|
+
desc 'Verifies that all built gems in pkg/ are valid'
|
117
|
+
task :verify do
|
118
|
+
gems = Dir['pkg/*.gem']
|
119
|
+
if gems.empty?
|
120
|
+
puts 'There are no gems in pkg/ to verify'
|
121
|
+
else
|
122
|
+
gems.each do |gem|
|
123
|
+
if signed_gem?(gem)
|
124
|
+
puts "#{gem} is signed"
|
125
|
+
else
|
126
|
+
abort "#{gem} is not signed"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -80,7 +80,8 @@ module Mongoid
|
|
80
80
|
def batch_replace(docs)
|
81
81
|
if docs.blank?
|
82
82
|
if _assigning? && !empty?
|
83
|
-
_base.delayed_atomic_sets.
|
83
|
+
_base.delayed_atomic_sets.delete(path)
|
84
|
+
clear_atomic_path_cache
|
84
85
|
_base.add_atomic_unset(first)
|
85
86
|
target_duplicate = _target.dup
|
86
87
|
pre_process_batch_remove(target_duplicate, :delete)
|
@@ -92,7 +93,8 @@ module Mongoid
|
|
92
93
|
_base.delayed_atomic_sets.clear unless _assigning?
|
93
94
|
docs = normalize_docs(docs).compact
|
94
95
|
_target.clear and _unscoped.clear
|
95
|
-
_base.delayed_atomic_unsets.
|
96
|
+
_base.delayed_atomic_unsets.delete(path)
|
97
|
+
clear_atomic_path_cache
|
96
98
|
inserts = execute_batch_set(docs)
|
97
99
|
add_atomic_sets(inserts)
|
98
100
|
end
|
@@ -234,7 +236,22 @@ module Mongoid
|
|
234
236
|
#
|
235
237
|
# @return [ String ] The atomic path.
|
236
238
|
def path
|
237
|
-
@path ||= _unscoped.
|
239
|
+
@path ||= if _unscoped.empty?
|
240
|
+
Mongoid::Atomic::Paths::Embedded::Many.position_without_document(_base, _association)
|
241
|
+
else
|
242
|
+
_unscoped.first.atomic_path
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Clear the cache for path and atomic_paths. This method is used when
|
247
|
+
# the path method is used, and the association has not been set on the
|
248
|
+
# document yet, which can cause path and atomic_paths to be calculated
|
249
|
+
# incorrectly later.
|
250
|
+
#
|
251
|
+
# @api private
|
252
|
+
def clear_atomic_path_cache
|
253
|
+
self.path = nil
|
254
|
+
_base.instance_variable_set("@atomic_paths", nil)
|
238
255
|
end
|
239
256
|
|
240
257
|
# Set the atomic path.
|
@@ -34,6 +34,25 @@ module Mongoid
|
|
34
34
|
locator = document.new_record? ? "" : ".#{document._index}"
|
35
35
|
"#{pos}#{"." unless pos.blank?}#{document._association.store_as}#{locator}"
|
36
36
|
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
# Get the position of where the document would go for the given
|
41
|
+
# association. The use case for this function is when trying to
|
42
|
+
# persist an empty list for an embedded association. All of the
|
43
|
+
# existing functions for getting the position to store a document
|
44
|
+
# require passing in a document to store, which we don't have when
|
45
|
+
# trying to store the empty list.
|
46
|
+
#
|
47
|
+
# @param [ Document ] parent The parent document to store in.
|
48
|
+
# @param [ Association ] association The association.
|
49
|
+
#
|
50
|
+
# @return [ String ] The position string.
|
51
|
+
def position_without_document(parent, association)
|
52
|
+
pos = parent.atomic_position
|
53
|
+
"#{pos}#{"." unless pos.blank?}#{association.store_as}"
|
54
|
+
end
|
55
|
+
end
|
37
56
|
end
|
38
57
|
end
|
39
58
|
end
|
data/lib/mongoid/cacheable.rb
CHANGED
@@ -15,7 +15,7 @@ module Mongoid
|
|
15
15
|
# plural model name.
|
16
16
|
#
|
17
17
|
# If new_record? - will append /new
|
18
|
-
# If not - will append /id-updated_at.
|
18
|
+
# If not - will append /id-updated_at.to_formatted_s(cache_timestamp_format)
|
19
19
|
# Without updated_at - will append /id
|
20
20
|
#
|
21
21
|
# This is usually called inside a cache() block
|
@@ -26,7 +26,7 @@ module Mongoid
|
|
26
26
|
# @return [ String ] the string with or without updated_at
|
27
27
|
def cache_key
|
28
28
|
return "#{model_key}/new" if new_record?
|
29
|
-
return "#{model_key}/#{_id}-#{updated_at.utc.
|
29
|
+
return "#{model_key}/#{_id}-#{updated_at.utc.to_formatted_s(cache_timestamp_format)}" if do_or_do_not(:updated_at)
|
30
30
|
"#{model_key}/#{_id}"
|
31
31
|
end
|
32
32
|
end
|
data/lib/mongoid/config.rb
CHANGED
@@ -116,6 +116,12 @@ module Mongoid
|
|
116
116
|
# using and's instead of overwriting them.
|
117
117
|
option :overwrite_chained_operators, default: true
|
118
118
|
|
119
|
+
# When this flag is true, the attributes method on a document will return
|
120
|
+
# a BSON::Document when that document is retrieved from the database, and
|
121
|
+
# a Hash otherwise. When this flag is false, the attributes method will
|
122
|
+
# always return a Hash.
|
123
|
+
option :legacy_attributes, default: true
|
124
|
+
|
119
125
|
# Has Mongoid been configured? This is checking that at least a valid
|
120
126
|
# client config exists.
|
121
127
|
#
|
@@ -260,16 +260,16 @@ module Mongoid
|
|
260
260
|
#
|
261
261
|
# @return [ Document ] The first document.
|
262
262
|
def first(limit_or_opts = nil)
|
263
|
-
limit = limit_or_opts
|
263
|
+
limit, opts = extract_limit_and_opts(limit_or_opts)
|
264
264
|
if cached? && cache_loaded?
|
265
265
|
return limit ? documents.first(limit) : documents.first
|
266
266
|
end
|
267
267
|
try_numbered_cache(:first, limit) do
|
268
|
-
if
|
268
|
+
if opts.key?(:id_sort)
|
269
269
|
Mongoid::Warnings.warn_id_sort_deprecated
|
270
270
|
end
|
271
271
|
sorted_view = view
|
272
|
-
if sort = view.sort || ({ _id: 1 } unless
|
272
|
+
if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
|
273
273
|
sorted_view = view.sort(sort)
|
274
274
|
end
|
275
275
|
if raw_docs = sorted_view.limit(limit || 1).to_a
|
@@ -376,12 +376,12 @@ module Mongoid
|
|
376
376
|
#
|
377
377
|
# @return [ Document ] The last document.
|
378
378
|
def last(limit_or_opts = nil)
|
379
|
-
limit = limit_or_opts
|
379
|
+
limit, opts = extract_limit_and_opts(limit_or_opts)
|
380
380
|
if cached? && cache_loaded?
|
381
381
|
return limit ? documents.last(limit) : documents.last
|
382
382
|
end
|
383
383
|
res = try_numbered_cache(:last, limit) do
|
384
|
-
with_inverse_sorting(
|
384
|
+
with_inverse_sorting(opts) do
|
385
385
|
if raw_docs = view.limit(limit || 1).to_a
|
386
386
|
process_raw_docs(raw_docs, limit)
|
387
387
|
end
|
@@ -612,6 +612,23 @@ module Mongoid
|
|
612
612
|
end
|
613
613
|
end
|
614
614
|
|
615
|
+
# Extract the limit and opts from the given argument, so that code
|
616
|
+
# can operate without having to worry about the current type and
|
617
|
+
# state of the argument.
|
618
|
+
#
|
619
|
+
# @param [ nil | Integer | Hash ] limit_or_opts The value to pull the
|
620
|
+
# limit and option hash from.
|
621
|
+
#
|
622
|
+
# @return [ Array<nil | Integer, Hash> ] A 2-array of the limit and the
|
623
|
+
# option hash.
|
624
|
+
def extract_limit_and_opts(limit_or_opts)
|
625
|
+
case limit_or_opts
|
626
|
+
when nil, Integer then [ limit_or_opts, {} ]
|
627
|
+
when Hash then [ nil, limit_or_opts ]
|
628
|
+
else raise ArgumentError, "expected nil, Integer, or Hash"
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
615
632
|
# Update the documents for the provided method.
|
616
633
|
#
|
617
634
|
# @api private
|
@@ -676,10 +693,10 @@ module Mongoid
|
|
676
693
|
# @example Apply the inverse sorting params to the given block
|
677
694
|
# context.with_inverse_sorting
|
678
695
|
def with_inverse_sorting(opts = {})
|
679
|
-
Mongoid::Warnings.warn_id_sort_deprecated if opts.
|
696
|
+
Mongoid::Warnings.warn_id_sort_deprecated if opts.key?(:id_sort)
|
680
697
|
|
681
698
|
begin
|
682
|
-
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts
|
699
|
+
if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
|
683
700
|
@view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
|
684
701
|
end
|
685
702
|
yield
|
@@ -47,7 +47,7 @@ module Mongoid
|
|
47
47
|
if value.is_a?(Hash) && selector[field].is_a?(Hash) &&
|
48
48
|
value.keys.all? { |key|
|
49
49
|
key_s = key.to_s
|
50
|
-
key_s.start_with?('$') && !selector[field].
|
50
|
+
key_s.start_with?('$') && !selector[field].keys.map(&:to_s).include?(key_s)
|
51
51
|
}
|
52
52
|
then
|
53
53
|
# Multiple operators can be combined on the same field by
|
data/lib/mongoid/document.rb
CHANGED
@@ -277,8 +277,15 @@ module Mongoid
|
|
277
277
|
# criteria.
|
278
278
|
#
|
279
279
|
# @return [ Document ] A new document.
|
280
|
+
#
|
281
|
+
# @api private
|
280
282
|
def instantiate(attrs = nil, selected_fields = nil)
|
281
|
-
attributes =
|
283
|
+
attributes = if Mongoid.legacy_attributes
|
284
|
+
attrs
|
285
|
+
else
|
286
|
+
attrs&.to_h
|
287
|
+
end || {}
|
288
|
+
|
282
289
|
doc = allocate
|
283
290
|
doc.__selected_fields = selected_fields
|
284
291
|
doc.instance_variable_set(:@attributes, attributes)
|
@@ -122,6 +122,21 @@ module Mongoid
|
|
122
122
|
options == other.options
|
123
123
|
end
|
124
124
|
|
125
|
+
# Whether the client of the context can be reused later, and therefore should
|
126
|
+
# not be closed.
|
127
|
+
#
|
128
|
+
# If the persistence context is requested with :client option only, it means
|
129
|
+
# that the context should use a client configured in mongoid.yml.
|
130
|
+
# Such clients should not be closed when the context is cleared since they
|
131
|
+
# will be reused later.
|
132
|
+
#
|
133
|
+
# @return [ true | false ] True if client can be reused, otherwise false.
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def reusable_client?
|
137
|
+
@options.keys == [:client]
|
138
|
+
end
|
139
|
+
|
125
140
|
private
|
126
141
|
|
127
142
|
def set_options!(opts)
|
@@ -172,8 +187,7 @@ module Mongoid
|
|
172
187
|
#
|
173
188
|
# @return [ Mongoid::PersistenceContext ] The persistence context for the object.
|
174
189
|
def set(object, options_or_context)
|
175
|
-
|
176
|
-
existing_context = Thread.current[key]
|
190
|
+
existing_context = get_context(object)
|
177
191
|
existing_options = if existing_context
|
178
192
|
existing_context.options
|
179
193
|
else
|
@@ -184,7 +198,7 @@ module Mongoid
|
|
184
198
|
end
|
185
199
|
new_options = existing_options.merge(options_or_context)
|
186
200
|
context = PersistenceContext.new(object, new_options)
|
187
|
-
|
201
|
+
store_context(object, context)
|
188
202
|
end
|
189
203
|
|
190
204
|
# Get the persistence context for a particular class or model instance.
|
@@ -196,7 +210,7 @@ module Mongoid
|
|
196
210
|
#
|
197
211
|
# @return [ Mongoid::PersistenceContext ] The persistence context for the object.
|
198
212
|
def get(object)
|
199
|
-
|
213
|
+
get_context(object)
|
200
214
|
end
|
201
215
|
|
202
216
|
# Clear the persistence context for a particular class or model instance.
|
@@ -211,11 +225,48 @@ module Mongoid
|
|
211
225
|
def clear(object, cluster = nil, original_context = nil)
|
212
226
|
if context = get(object)
|
213
227
|
unless cluster.nil? || context.cluster.equal?(cluster)
|
214
|
-
context.client.close
|
228
|
+
context.client.close unless context.reusable_client?
|
215
229
|
end
|
216
230
|
end
|
217
231
|
ensure
|
218
|
-
|
232
|
+
store_context(object, original_context)
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
# Key to store persistence contexts in the thread local storage.
|
238
|
+
#
|
239
|
+
# @api private
|
240
|
+
PERSISTENCE_CONTEXT_KEY = :"[mongoid]:persistence_context"
|
241
|
+
|
242
|
+
# Get the persistence context for a given object from the thread local
|
243
|
+
# storage.
|
244
|
+
#
|
245
|
+
# @param [ Object ] object Object to get the persistance context for.
|
246
|
+
#
|
247
|
+
# @return [ Mongoid::PersistenceContext | nil ] The persistence context
|
248
|
+
# for the object if previously stored, otherwise nil.
|
249
|
+
#
|
250
|
+
# @api private
|
251
|
+
def get_context(object)
|
252
|
+
Thread.current[PERSISTENCE_CONTEXT_KEY] ||= {}
|
253
|
+
Thread.current[PERSISTENCE_CONTEXT_KEY][object.object_id]
|
254
|
+
end
|
255
|
+
|
256
|
+
# Store persistence context for a given object in the thread local
|
257
|
+
# storage.
|
258
|
+
#
|
259
|
+
# @param [ Object ] object Object to store the persistance context for.
|
260
|
+
# @param [ Mongoid::PersistenceContext ] context Context to store
|
261
|
+
#
|
262
|
+
# @api private
|
263
|
+
def store_context(object, context)
|
264
|
+
if context.nil?
|
265
|
+
Thread.current[PERSISTENCE_CONTEXT_KEY]&.delete(object.object_id)
|
266
|
+
else
|
267
|
+
Thread.current[PERSISTENCE_CONTEXT_KEY] ||= {}
|
268
|
+
Thread.current[PERSISTENCE_CONTEXT_KEY][object.object_id] = context
|
269
|
+
end
|
219
270
|
end
|
220
271
|
end
|
221
272
|
end
|
data/lib/mongoid/shardable.rb
CHANGED
@@ -47,18 +47,22 @@ module Mongoid
|
|
47
47
|
self.class.shard_key_fields
|
48
48
|
end
|
49
49
|
|
50
|
-
# Returns the selector that would match the
|
51
|
-
#
|
50
|
+
# Returns the selector that would match the defined shard keys. If
|
51
|
+
# `prefer_persisted` is false (the default), it uses the current values
|
52
|
+
# of the specified shard keys, otherwise, it will try to use whatever value
|
53
|
+
# was most recently persisted.
|
54
|
+
#
|
55
|
+
# @param [ true | false ] prefer_persisted Whether to use the current
|
56
|
+
# value of the shard key fields, or to use their most recently persisted
|
57
|
+
# values.
|
52
58
|
#
|
53
59
|
# @return [ Hash ] The shard key selector.
|
54
60
|
#
|
55
61
|
# @api private
|
56
|
-
def shard_key_selector
|
57
|
-
|
58
|
-
|
59
|
-
selector[field.to_s] = send(field)
|
62
|
+
def shard_key_selector(prefer_persisted: false)
|
63
|
+
shard_key_fields.each_with_object({}) do |field, selector|
|
64
|
+
selector[field.to_s] = shard_key_field_value(field.to_s, prefer_persisted: prefer_persisted)
|
60
65
|
end
|
61
|
-
selector
|
62
66
|
end
|
63
67
|
|
64
68
|
# Returns the selector that would match the existing version of this
|
@@ -72,11 +76,31 @@ module Mongoid
|
|
72
76
|
#
|
73
77
|
# @api private
|
74
78
|
def shard_key_selector_in_db
|
75
|
-
|
76
|
-
|
77
|
-
|
79
|
+
shard_key_selector(prefer_persisted: true)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns the value for the named shard key. If the field identifies
|
83
|
+
# an embedded document, the key will be parsed and recursively evaluated.
|
84
|
+
# If `prefer_persisted` is true, the value last persisted to the database
|
85
|
+
# will be returned, regardless of what the current value of the attribute
|
86
|
+
# may be.
|
87
|
+
#
|
88
|
+
# @param [String] field The name of the field to evaluate
|
89
|
+
# @param [ true|false ] prefer_persisted Whether or not to prefer the
|
90
|
+
# persisted value over the current value.
|
91
|
+
#
|
92
|
+
# @return [ Object ] The value of the named field.
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def shard_key_field_value(field, prefer_persisted:)
|
96
|
+
if field.include?(".")
|
97
|
+
relation, remaining = field.split(".", 2)
|
98
|
+
send(relation)&.shard_key_field_value(remaining, prefer_persisted: prefer_persisted)
|
99
|
+
elsif prefer_persisted && !new_record?
|
100
|
+
attribute_was(field)
|
101
|
+
else
|
102
|
+
send(field)
|
78
103
|
end
|
79
|
-
selector
|
80
104
|
end
|
81
105
|
|
82
106
|
module ClassMethods
|
data/lib/mongoid/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
|
+
require_relative '../embeds_many_models.rb'
|
4
5
|
|
5
6
|
describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
|
6
7
|
|
@@ -4649,4 +4650,24 @@ describe Mongoid::Association::Embedded::EmbedsMany::Proxy do
|
|
4649
4650
|
end
|
4650
4651
|
end
|
4651
4652
|
end
|
4653
|
+
|
4654
|
+
context "when using assign_attributes with an already populated array" do
|
4655
|
+
let(:post) { EmmPost.create! }
|
4656
|
+
|
4657
|
+
before do
|
4658
|
+
post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'a'}],
|
4659
|
+
user_tags: [{id: BSON::ObjectId.new, title: 'b'}])
|
4660
|
+
post.save!
|
4661
|
+
post.reload
|
4662
|
+
post.assign_attributes(company_tags: [{id: BSON::ObjectId.new, title: 'c'}],
|
4663
|
+
user_tags: [])
|
4664
|
+
post.save!
|
4665
|
+
post.reload
|
4666
|
+
end
|
4667
|
+
|
4668
|
+
it "has the correct embedded documents" do
|
4669
|
+
expect(post.company_tags.length).to eq(1)
|
4670
|
+
expect(post.company_tags.first.title).to eq("c")
|
4671
|
+
end
|
4672
|
+
end
|
4652
4673
|
end
|
@@ -67,3 +67,124 @@ class EmmOuter
|
|
67
67
|
|
68
68
|
field :level, :type => Integer
|
69
69
|
end
|
70
|
+
|
71
|
+
class EmmCustomerAddress
|
72
|
+
include Mongoid::Document
|
73
|
+
|
74
|
+
embedded_in :addressable, polymorphic: true, inverse_of: :work_address
|
75
|
+
end
|
76
|
+
|
77
|
+
class EmmFriend
|
78
|
+
include Mongoid::Document
|
79
|
+
|
80
|
+
embedded_in :befriendable, polymorphic: true
|
81
|
+
end
|
82
|
+
|
83
|
+
class EmmCustomer
|
84
|
+
include Mongoid::Document
|
85
|
+
|
86
|
+
embeds_one :home_address, class_name: 'EmmCustomerAddress', as: :addressable
|
87
|
+
embeds_one :work_address, class_name: 'EmmCustomerAddress', as: :addressable
|
88
|
+
|
89
|
+
embeds_many :close_friends, class_name: 'EmmFriend', as: :befriendable
|
90
|
+
embeds_many :acquaintances, class_name: 'EmmFriend', as: :befriendable
|
91
|
+
end
|
92
|
+
|
93
|
+
class EmmUser
|
94
|
+
include Mongoid::Document
|
95
|
+
include Mongoid::Timestamps
|
96
|
+
|
97
|
+
embeds_many :orders, class_name: 'EmmOrder'
|
98
|
+
end
|
99
|
+
|
100
|
+
class EmmOrder
|
101
|
+
include Mongoid::Document
|
102
|
+
|
103
|
+
field :amount, type: Integer
|
104
|
+
|
105
|
+
embedded_in :user, class_name: 'EmmUser'
|
106
|
+
end
|
107
|
+
|
108
|
+
module EmmSpec
|
109
|
+
# There is also a top-level Car class defined.
|
110
|
+
class Car
|
111
|
+
include Mongoid::Document
|
112
|
+
|
113
|
+
embeds_many :doors
|
114
|
+
end
|
115
|
+
|
116
|
+
class Door
|
117
|
+
include Mongoid::Document
|
118
|
+
|
119
|
+
embedded_in :car
|
120
|
+
end
|
121
|
+
|
122
|
+
class Tank
|
123
|
+
include Mongoid::Document
|
124
|
+
|
125
|
+
embeds_many :guns
|
126
|
+
embeds_many :emm_turrets
|
127
|
+
# This association references a model that is not in our module,
|
128
|
+
# and it does not define class_name hence Mongoid will not be able to
|
129
|
+
# figure out the inverse for this association.
|
130
|
+
embeds_many :emm_hatches
|
131
|
+
|
132
|
+
# class_name is intentionally unqualified, references a class in the
|
133
|
+
# same module. Rails permits class_name to be unqualified like this.
|
134
|
+
embeds_many :launchers, class_name: 'Launcher'
|
135
|
+
end
|
136
|
+
|
137
|
+
class Gun
|
138
|
+
include Mongoid::Document
|
139
|
+
|
140
|
+
embedded_in :tank
|
141
|
+
end
|
142
|
+
|
143
|
+
class Launcher
|
144
|
+
include Mongoid::Document
|
145
|
+
|
146
|
+
# class_name is intentionally unqualified.
|
147
|
+
embedded_in :tank, class_name: 'Tank'
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# This is intentionally on top level.
|
152
|
+
class EmmTurret
|
153
|
+
include Mongoid::Document
|
154
|
+
|
155
|
+
embedded_in :tank, class_name: 'EmmSpec::Tank'
|
156
|
+
end
|
157
|
+
|
158
|
+
# This is intentionally on top level.
|
159
|
+
class EmmHatch
|
160
|
+
include Mongoid::Document
|
161
|
+
|
162
|
+
# No :class_name option on this association intentionally.
|
163
|
+
embedded_in :tank
|
164
|
+
end
|
165
|
+
|
166
|
+
class EmmPost
|
167
|
+
include Mongoid::Document
|
168
|
+
|
169
|
+
embeds_many :company_tags, class_name: "EmmCompanyTag"
|
170
|
+
embeds_many :user_tags, class_name: "EmmUserTag"
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
class EmmCompanyTag
|
175
|
+
include Mongoid::Document
|
176
|
+
|
177
|
+
field :title, type: String
|
178
|
+
|
179
|
+
embedded_in :post, class_name: "EmmPost"
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
class EmmUserTag
|
184
|
+
include Mongoid::Document
|
185
|
+
|
186
|
+
field :title, type: String
|
187
|
+
|
188
|
+
embedded_in :post, class_name: "EmmPost"
|
189
|
+
end
|
190
|
+
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
|
+
require_relative "../has_and_belongs_to_many_models.rb"
|
4
5
|
|
5
6
|
describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
6
7
|
|
@@ -3770,4 +3771,33 @@ describe Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy do
|
|
3770
3771
|
expect(p2.d_ids).to match_array([d2.id])
|
3771
3772
|
end
|
3772
3773
|
end
|
3774
|
+
|
3775
|
+
# This test is for MONGOID-5344 which tests that the initial call to
|
3776
|
+
# signature_ids refers to the same array as subsequent calls to signature_ids.
|
3777
|
+
# Prior to the change in that ticket, this test broke because the array
|
3778
|
+
# returned from write_attribute (which is triggered the first time the
|
3779
|
+
# foreign key array is referenced, to set the default), refers to a different
|
3780
|
+
# array to the one stored in the attributes hash. This happened because,
|
3781
|
+
# when retrieving a document from the database, the attributes hash is actually
|
3782
|
+
# a BSON::Document, which applies a transformation to the array before
|
3783
|
+
# storing it.
|
3784
|
+
context "when executing concat on foreign key array from the db" do
|
3785
|
+
config_override :legacy_attributes, false
|
3786
|
+
|
3787
|
+
before do
|
3788
|
+
HabtmmContract.create!
|
3789
|
+
HabtmmSignature.create!
|
3790
|
+
end
|
3791
|
+
|
3792
|
+
let!(:contract) { HabtmmContract.first }
|
3793
|
+
let!(:signature) { HabtmmSignature.first }
|
3794
|
+
|
3795
|
+
before do
|
3796
|
+
contract.signature_ids.concat([signature.id])
|
3797
|
+
end
|
3798
|
+
|
3799
|
+
it "works on the first attempt" do
|
3800
|
+
expect(contract.signature_ids).to eq([signature.id])
|
3801
|
+
end
|
3802
|
+
end
|
3773
3803
|
end
|
@@ -4089,4 +4089,24 @@ describe Mongoid::Association::Referenced::HasMany::Proxy do
|
|
4089
4089
|
expect(band.same_name).to eq([agent])
|
4090
4090
|
end
|
4091
4091
|
end
|
4092
|
+
|
4093
|
+
context "when executing concat on foreign key array from the db" do
|
4094
|
+
config_override :legacy_attributes, false
|
4095
|
+
|
4096
|
+
before do
|
4097
|
+
Agent.create!
|
4098
|
+
Basic.create!
|
4099
|
+
end
|
4100
|
+
|
4101
|
+
let!(:agent) { Agent.first }
|
4102
|
+
let!(:basic) { Basic.first }
|
4103
|
+
|
4104
|
+
before do
|
4105
|
+
agent.basic_ids.concat([basic.id])
|
4106
|
+
end
|
4107
|
+
|
4108
|
+
it "works on the first attempt" do
|
4109
|
+
expect(agent.basic_ids).to eq([basic.id])
|
4110
|
+
end
|
4111
|
+
end
|
4092
4112
|
end
|