mongoid 8.0.8 → 8.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +65 -41
- data/lib/mongoid/association/accessors.rb +5 -1
- data/lib/mongoid/association/eager_loadable.rb +3 -0
- data/lib/mongoid/config.rb +10 -0
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +15 -1
- data/lib/mongoid/document.rb +8 -1
- data/lib/mongoid/fields.rb +11 -6
- data/lib/mongoid/interceptable.rb +10 -8
- data/lib/mongoid/timestamps/created.rb +8 -1
- data/lib/mongoid/traversable.rb +12 -0
- data/lib/mongoid/validatable/associated.rb +6 -3
- data/lib/mongoid/version.rb +1 -1
- data/spec/integration/callbacks_models.rb +37 -0
- data/spec/integration/callbacks_spec.rb +27 -0
- data/spec/mongoid/association/eager_spec.rb +24 -2
- data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +4 -0
- data/spec/mongoid/association_spec.rb +60 -0
- data/spec/mongoid/document_spec.rb +27 -0
- data/spec/mongoid/interceptable_spec.rb +100 -0
- data/spec/mongoid/interceptable_spec_models.rb +51 -111
- data/spec/mongoid/serializable_spec.rb +14 -14
- data/spec/mongoid/timestamps/created_spec.rb +23 -0
- data/spec/mongoid/validatable/associated_spec.rb +14 -4
- metadata +4 -80
- checksums.yaml.gz.sig +0 -0
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/bin/s3-copy +0 -45
- data/spec/shared/bin/s3-upload +0 -69
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -231
- data/spec/shared/lib/mrss/constraints.rb +0 -378
- data/spec/shared/lib/mrss/docker_runner.rb +0 -298
- data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
- data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
- data/spec/shared/lib/mrss/session_registry.rb +0 -69
- data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
- data/spec/shared/lib/mrss/utils.rb +0 -37
- data/spec/shared/share/Dockerfile.erb +0 -321
- data/spec/shared/share/haproxy-1.conf +0 -16
- data/spec/shared/share/haproxy-2.conf +0 -17
- data/spec/shared/shlib/config.sh +0 -27
- data/spec/shared/shlib/distro.sh +0 -74
- data/spec/shared/shlib/server.sh +0 -416
- data/spec/shared/shlib/set_env.sh +0 -169
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49662a14c4c7135e3403cab365f7e46928baf2efca9ede4e673269d39c5a7292
|
4
|
+
data.tar.gz: a69c22af01dbba7be588e2b4a9a63a1f58323ddd3395771969652323b2d7ecaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cdde64cee0c2f085b6f250aae4e0f31340a9b79d04177e69f9c287170d8bb2eaba435873d680f77181587624244c1ba5885eff2d3964f3ebbe5f0924a70b1347
|
7
|
+
data.tar.gz: 2d065d480c992dc3a2e772b7c33a0b50805d981c4f150f06b93f0ad9c89fd6b9c138c64354f8353c1a9849d9ddef45eae382bfc3ca1e4bb6aead94e60c5e6324
|
data/Rakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler"
|
4
|
-
require "bundler/gem_tasks"
|
5
4
|
Bundler.setup
|
6
5
|
|
7
6
|
ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
|
@@ -10,34 +9,53 @@ $: << File.join(ROOT, 'spec/shared/lib')
|
|
10
9
|
|
11
10
|
require "rake"
|
12
11
|
require "rspec/core/rake_task"
|
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
|
23
|
-
|
24
|
-
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
25
|
-
require "mongoid/version"
|
26
|
-
|
27
|
-
tasks = Rake.application.instance_variable_get('@tasks')
|
28
|
-
tasks['release:do'] = tasks.delete('release')
|
29
12
|
|
30
|
-
task
|
13
|
+
# stands in for the Bundler-provided `build` task, which builds the
|
14
|
+
# gem for this project. Our release process builds the gems in a
|
15
|
+
# particular way, in a GitHub action. This task is just to help remind
|
16
|
+
# developers of that fact.
|
31
17
|
task :build do
|
32
|
-
|
18
|
+
abort <<~WARNING
|
19
|
+
`rake build` does nothing in this project. The gem must be built via
|
20
|
+
the `Mongoid Release` action on GitHub, which is triggered manually when
|
21
|
+
a new release is ready.
|
22
|
+
WARNING
|
33
23
|
end
|
34
24
|
|
35
|
-
|
36
|
-
|
25
|
+
# `rake version` is used by the deployment system so get the release version
|
26
|
+
# of the product beng deployed. It must do nothing more than just print the
|
27
|
+
# product version number.
|
28
|
+
#
|
29
|
+
# See the mongodb-labs/driver-github-tools/ruby/publish Github action.
|
30
|
+
desc "Print the current value of Mongoid::VERSION"
|
31
|
+
task :version do
|
32
|
+
require 'mongoid/version'
|
33
|
+
|
34
|
+
puts Mongoid::VERSION
|
37
35
|
end
|
38
36
|
|
37
|
+
# overrides the default Bundler-provided `release` task, which also
|
38
|
+
# builds the gem. Our release process assumes the gem has already
|
39
|
+
# been built (and signed via GPG), so we just need `rake release` to
|
40
|
+
# push the gem to rubygems.
|
39
41
|
task :release do
|
40
|
-
|
42
|
+
require 'mongoid/version'
|
43
|
+
|
44
|
+
if ENV['GITHUB_ACTION'].nil?
|
45
|
+
abort <<~WARNING
|
46
|
+
`rake release` must be invoked from the `Mongoid Release` GitHub action,
|
47
|
+
and must not be invoked locally. This ensures the gem is properly signed
|
48
|
+
and distributed by the appropriate user.
|
49
|
+
|
50
|
+
Note that it is the `rubygems/release-gem@v1` step in the `Mongoid Release`
|
51
|
+
action that invokes this task. Do not rename or remove this task, or the
|
52
|
+
release-gem step will fail. Reimplement this task with caution.
|
53
|
+
|
54
|
+
mongoid-#{Mongoid::VERSION}.gem was NOT pushed to RubyGems.
|
55
|
+
WARNING
|
56
|
+
end
|
57
|
+
|
58
|
+
system 'gem', 'push', "mongoid-#{Mongoid::VERSION}.gem"
|
41
59
|
end
|
42
60
|
|
43
61
|
RSpec::Core::RakeTask.new("spec") do |spec|
|
@@ -64,6 +82,8 @@ RUN_PRIORITY = %i(
|
|
64
82
|
)
|
65
83
|
|
66
84
|
def spec_organizer
|
85
|
+
require 'mrss/spec_organizer'
|
86
|
+
|
67
87
|
Mrss::SpecOrganizer.new(
|
68
88
|
root: ROOT,
|
69
89
|
classifiers: CLASSIFIERS,
|
@@ -99,32 +119,36 @@ task :docs => 'docs:yard'
|
|
99
119
|
namespace :docs do
|
100
120
|
desc "Generate yard documention"
|
101
121
|
task :yard do
|
122
|
+
require "mongoid/version"
|
123
|
+
|
102
124
|
out = File.join('yard-docs', Mongoid::VERSION)
|
103
125
|
FileUtils.rm_rf(out)
|
104
126
|
system "yardoc -o #{out} --title mongoid-#{Mongoid::VERSION}"
|
105
127
|
end
|
106
128
|
end
|
107
129
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
130
|
+
desc 'Build and validate the evergreen config'
|
131
|
+
task eg: %w[ eg:build eg:validate ]
|
132
|
+
|
133
|
+
# 'eg' == 'evergreen', but evergreen is too many letters for convenience
|
134
|
+
namespace :eg do
|
135
|
+
desc 'Builds the .evergreen/config.yml file from the templates'
|
136
|
+
task :build do
|
137
|
+
ruby '.evergreen/update-evergreen-configs'
|
113
138
|
end
|
114
|
-
end
|
115
139
|
|
116
|
-
desc '
|
117
|
-
task :
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
140
|
+
desc 'Validates the .evergreen/config.yml file'
|
141
|
+
task :validate do
|
142
|
+
system 'evergreen validate --project mongoid .evergreen/config.yml'
|
143
|
+
end
|
144
|
+
|
145
|
+
desc 'Updates the evergreen executable to the latest available version'
|
146
|
+
task :update do
|
147
|
+
system 'evergreen get-update --install'
|
148
|
+
end
|
149
|
+
|
150
|
+
desc 'Runs the current branch as an evergreen patch'
|
151
|
+
task :patch do
|
152
|
+
system 'evergreen patch --uncommitted --project mongoid --browse --auto-description --yes'
|
129
153
|
end
|
130
154
|
end
|
@@ -115,7 +115,11 @@ module Mongoid
|
|
115
115
|
# during binding or when cascading callbacks. Whenever we retrieve
|
116
116
|
# associations within the codebase, we use without_autobuild.
|
117
117
|
if !without_autobuild? && association.embedded? && attribute_missing?(field_name)
|
118
|
-
|
118
|
+
# We always allow accessing the parent document of an embedded one.
|
119
|
+
try_get_parent = association.is_a?(
|
120
|
+
Mongoid::Association::Embedded::EmbeddedIn
|
121
|
+
) && field_name == association.key
|
122
|
+
raise ActiveModel::MissingAttributeError, "Missing attribute: '#{field_name}'" unless try_get_parent
|
119
123
|
end
|
120
124
|
|
121
125
|
if !reload && (value = ivar(name)) != false
|
@@ -31,6 +31,9 @@ module Mongoid
|
|
31
31
|
docs_map = {}
|
32
32
|
queue = [ klass.to_s ]
|
33
33
|
|
34
|
+
# account for single-collection inheritance
|
35
|
+
queue.push(klass.root_class.to_s) if klass != klass.root_class
|
36
|
+
|
34
37
|
while klass = queue.shift
|
35
38
|
if as = assoc_map.delete(klass)
|
36
39
|
as.each do |assoc|
|
data/lib/mongoid/config.rb
CHANGED
@@ -142,6 +142,16 @@ module Mongoid
|
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
145
|
+
# When this flag is true, callbacks for every embedded document will be
|
146
|
+
# called only once, even if the embedded document is embedded in multiple
|
147
|
+
# documents in the root document's dependencies graph.
|
148
|
+
# This will be the default in 9.0. Setting this flag to false restores the
|
149
|
+
# pre-9.0 behavior, where callbacks are called for every occurrence of an
|
150
|
+
# embedded document. The pre-9.0 behavior leads to a problem that for multi
|
151
|
+
# level nested documents callbacks are called multiple times.
|
152
|
+
# See https://jira.mongodb.org/browse/MONGOID-5542
|
153
|
+
option :prevent_multiple_calls_of_embedded_callbacks, default: false
|
154
|
+
|
145
155
|
# When this flag is true, callbacks for embedded documents will not be
|
146
156
|
# called. This is the default in 8.x, but will be changed to false in 9.0.
|
147
157
|
#
|
@@ -43,7 +43,21 @@ module Mongoid
|
|
43
43
|
#
|
44
44
|
# @return [ Object ] The converted number.
|
45
45
|
def __numeric__(object)
|
46
|
-
|
46
|
+
str = object.to_s
|
47
|
+
raise ArgumentError if str.empty?
|
48
|
+
|
49
|
+
# These requirements seem a bit odd, but they're explicitly specified in the tests,
|
50
|
+
# so we're obligated to keep them, for now. (This code was rewritten from a one-line
|
51
|
+
# regex, due to security concerns with a polynomial regex being used on uncontrolled
|
52
|
+
# data).
|
53
|
+
|
54
|
+
str = str.chop if str.end_with?('.')
|
55
|
+
return 0 if str.empty?
|
56
|
+
|
57
|
+
result = Integer(str) rescue Float(object)
|
58
|
+
|
59
|
+
integer = result.to_i
|
60
|
+
integer == result ? integer : result
|
47
61
|
end
|
48
62
|
|
49
63
|
# Evolve the object to an integer.
|
data/lib/mongoid/document.rb
CHANGED
@@ -133,7 +133,14 @@ module Mongoid
|
|
133
133
|
#
|
134
134
|
# @return [ Hash ] A hash of all attributes in the hierarchy.
|
135
135
|
def as_document
|
136
|
-
|
136
|
+
attrs = as_attributes
|
137
|
+
|
138
|
+
# legacy attributes have a tendency to leak internal state via
|
139
|
+
# `as_document`; we have to deep_dup the attributes here to prevent
|
140
|
+
# that.
|
141
|
+
attrs = attrs.deep_dup if Mongoid.legacy_attributes
|
142
|
+
|
143
|
+
BSON::Document.new(attrs)
|
137
144
|
end
|
138
145
|
|
139
146
|
# Calls #as_json on the document with additional, Mongoid-specific options.
|
data/lib/mongoid/fields.rb
CHANGED
@@ -47,6 +47,11 @@ module Mongoid
|
|
47
47
|
# @api private
|
48
48
|
INVALID_BSON_CLASSES = [ BSON::Decimal128, BSON::Int32, BSON::Int64 ].freeze
|
49
49
|
|
50
|
+
# The suffix for generated translated fields.
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
TRANSLATIONS_SFX = '_translations'
|
54
|
+
|
50
55
|
module ClassMethods
|
51
56
|
# Returns the list of id fields for this model class, as both strings
|
52
57
|
# and symbols.
|
@@ -99,8 +104,8 @@ module Mongoid
|
|
99
104
|
ar.each_with_index do |fn, i|
|
100
105
|
key = fn
|
101
106
|
unless klass.fields.key?(fn) || klass.relations.key?(fn)
|
102
|
-
if
|
103
|
-
key =
|
107
|
+
if fn.end_with?(TRANSLATIONS_SFX)
|
108
|
+
key = fn.delete_suffix(TRANSLATIONS_SFX)
|
104
109
|
else
|
105
110
|
key = fn
|
106
111
|
end
|
@@ -708,11 +713,11 @@ module Mongoid
|
|
708
713
|
# @param [ String ] meth The name of the method.
|
709
714
|
def create_translations_getter(name, meth)
|
710
715
|
generated_methods.module_eval do
|
711
|
-
re_define_method("#{meth}
|
716
|
+
re_define_method("#{meth}#{TRANSLATIONS_SFX}") do
|
712
717
|
attributes[name] ||= {}
|
713
718
|
attributes[name].with_indifferent_access
|
714
719
|
end
|
715
|
-
alias_method :"#{meth}_t", :"#{meth}
|
720
|
+
alias_method :"#{meth}_t", :"#{meth}#{TRANSLATIONS_SFX}"
|
716
721
|
end
|
717
722
|
end
|
718
723
|
|
@@ -726,14 +731,14 @@ module Mongoid
|
|
726
731
|
# @param [ Field ] field The field.
|
727
732
|
def create_translations_setter(name, meth, field)
|
728
733
|
generated_methods.module_eval do
|
729
|
-
re_define_method("#{meth}
|
734
|
+
re_define_method("#{meth}#{TRANSLATIONS_SFX}=") do |value|
|
730
735
|
attribute_will_change!(name)
|
731
736
|
value&.transform_values! do |_value|
|
732
737
|
field.type.mongoize(_value)
|
733
738
|
end
|
734
739
|
attributes[name] = value
|
735
740
|
end
|
736
|
-
alias_method :"#{meth}_t=", :"#{meth}
|
741
|
+
alias_method :"#{meth}_t=", :"#{meth}#{TRANSLATIONS_SFX}="
|
737
742
|
end
|
738
743
|
end
|
739
744
|
|
@@ -141,9 +141,13 @@ module Mongoid
|
|
141
141
|
# @api private
|
142
142
|
def _mongoid_run_child_callbacks(kind, children: nil, &block)
|
143
143
|
if Mongoid::Config.around_callbacks_for_embeds
|
144
|
-
_mongoid_run_child_callbacks_with_around(kind,
|
144
|
+
_mongoid_run_child_callbacks_with_around(kind,
|
145
|
+
children: children,
|
146
|
+
&block)
|
145
147
|
else
|
146
|
-
_mongoid_run_child_callbacks_without_around(kind,
|
148
|
+
_mongoid_run_child_callbacks_without_around(kind,
|
149
|
+
children: children,
|
150
|
+
&block)
|
147
151
|
end
|
148
152
|
end
|
149
153
|
|
@@ -163,13 +167,14 @@ module Mongoid
|
|
163
167
|
# @api private
|
164
168
|
def _mongoid_run_child_callbacks_with_around(kind, children: nil, &block)
|
165
169
|
child, *tail = (children || cascadable_children(kind))
|
170
|
+
with_children = !Mongoid::Config.prevent_multiple_calls_of_embedded_callbacks
|
166
171
|
if child.nil?
|
167
172
|
block&.call
|
168
173
|
elsif tail.empty?
|
169
|
-
child.run_callbacks(child_callback_type(kind, child), &block)
|
174
|
+
child.run_callbacks(child_callback_type(kind, child), with_children: with_children, &block)
|
170
175
|
else
|
171
|
-
child.run_callbacks(child_callback_type(kind, child)) do
|
172
|
-
|
176
|
+
child.run_callbacks(child_callback_type(kind, child), with_children: with_children) do
|
177
|
+
_mongoid_run_child_callbacks(kind, children: tail, &block)
|
173
178
|
end
|
174
179
|
end
|
175
180
|
end
|
@@ -218,9 +223,6 @@ module Mongoid
|
|
218
223
|
return false if env.halted
|
219
224
|
env.value = !env.halted
|
220
225
|
callback_list << [next_sequence, env]
|
221
|
-
if (grandchildren = child.send(:cascadable_children, kind))
|
222
|
-
_mongoid_run_child_before_callbacks(kind, children: grandchildren, callback_list: callback_list)
|
223
|
-
end
|
224
226
|
end
|
225
227
|
callback_list
|
226
228
|
end
|
@@ -22,13 +22,20 @@ module Mongoid
|
|
22
22
|
# @example Set the created at time.
|
23
23
|
# person.set_created_at
|
24
24
|
def set_created_at
|
25
|
-
if
|
25
|
+
if able_to_set_created_at?
|
26
26
|
time = Time.configured.now
|
27
27
|
self.updated_at = time if is_a?(Updated) && !updated_at_changed?
|
28
28
|
self.created_at = time
|
29
29
|
end
|
30
30
|
clear_timeless_option
|
31
31
|
end
|
32
|
+
|
33
|
+
# Is the created timestamp able to be set?
|
34
|
+
#
|
35
|
+
# @return [ true, false ] If the timestamp can be set.
|
36
|
+
def able_to_set_created_at?
|
37
|
+
!frozen? && !timeless? && !created_at
|
38
|
+
end
|
32
39
|
end
|
33
40
|
end
|
34
41
|
end
|
data/lib/mongoid/traversable.rb
CHANGED
@@ -300,6 +300,18 @@ module Mongoid
|
|
300
300
|
!!(Mongoid::Document > superclass)
|
301
301
|
end
|
302
302
|
|
303
|
+
# Returns the root class of the STI tree that the current
|
304
|
+
# class participates in. If the class is not an STI subclass, this
|
305
|
+
# returns the class itself.
|
306
|
+
#
|
307
|
+
# @return [ Mongoid::Document ] the root of the STI tree
|
308
|
+
def root_class
|
309
|
+
root = self
|
310
|
+
root = root.superclass while root.hereditary?
|
311
|
+
|
312
|
+
root
|
313
|
+
end
|
314
|
+
|
303
315
|
# When inheriting, we want to copy the fields from the parent class and
|
304
316
|
# set the on the child to start, mimicking the behavior of the old
|
305
317
|
# class_inheritable_accessor that was deprecated in Rails edge.
|
@@ -69,13 +69,16 @@ module Mongoid
|
|
69
69
|
# Now, treating the target as an array, look at each element
|
70
70
|
# and see if it is valid, but only if it has already been
|
71
71
|
# persisted, or changed, and hasn't been flagged for destroy.
|
72
|
-
|
73
|
-
|
72
|
+
#
|
73
|
+
# use map.all? instead of just all?, because all? will do short-circuit
|
74
|
+
# evaluation and terminate on the first failed validation.
|
75
|
+
list.map do |value|
|
76
|
+
if value && !value.flagged_for_destroy?
|
74
77
|
value.validated? ? true : value.valid?
|
75
78
|
else
|
76
79
|
true
|
77
80
|
end
|
78
|
-
end
|
81
|
+
end.all?
|
79
82
|
end
|
80
83
|
|
81
84
|
document.errors.add(attribute, :invalid) unless valid
|
data/lib/mongoid/version.rb
CHANGED
@@ -153,3 +153,40 @@ class Building
|
|
153
153
|
|
154
154
|
has_and_belongs_to_many :architects, dependent: :nullify
|
155
155
|
end
|
156
|
+
|
157
|
+
class Root
|
158
|
+
include Mongoid::Document
|
159
|
+
embeds_many :embedded_once, cascade_callbacks: true
|
160
|
+
after_save :trace
|
161
|
+
|
162
|
+
attr_accessor :logger
|
163
|
+
|
164
|
+
def trace
|
165
|
+
logger << :root
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class EmbeddedOnce
|
170
|
+
include Mongoid::Document
|
171
|
+
embeds_many :embedded_twice, cascade_callbacks: true
|
172
|
+
embedded_in :root
|
173
|
+
after_save :trace
|
174
|
+
|
175
|
+
attr_accessor :logger
|
176
|
+
|
177
|
+
def trace
|
178
|
+
logger << :embedded_once
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class EmbeddedTwice
|
183
|
+
include Mongoid::Document
|
184
|
+
embedded_in :embedded_once
|
185
|
+
after_save :trace
|
186
|
+
|
187
|
+
attr_accessor :logger
|
188
|
+
|
189
|
+
def trace
|
190
|
+
logger << :embedded_twice
|
191
|
+
end
|
192
|
+
end
|
@@ -467,4 +467,31 @@ describe 'callbacks integration tests' do
|
|
467
467
|
expect { book.save! }.not_to raise_error(SystemStackError)
|
468
468
|
end
|
469
469
|
end
|
470
|
+
|
471
|
+
context 'nested embedded documents' do
|
472
|
+
config_override :prevent_multiple_calls_of_embedded_callbacks, true
|
473
|
+
|
474
|
+
let(:logger) { Array.new }
|
475
|
+
|
476
|
+
let(:root) do
|
477
|
+
Root.new(
|
478
|
+
embedded_once: [
|
479
|
+
EmbeddedOnce.new(
|
480
|
+
embedded_twice: [EmbeddedTwice.new]
|
481
|
+
)
|
482
|
+
]
|
483
|
+
)
|
484
|
+
end
|
485
|
+
|
486
|
+
before(:each) do
|
487
|
+
root.logger = logger
|
488
|
+
root.embedded_once.first.logger = logger
|
489
|
+
root.embedded_once.first.embedded_twice.first.logger = logger
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'runs callbacks in the correct order' do
|
493
|
+
root.save!
|
494
|
+
expect(logger).to eq(%i[embedded_twice embedded_once root])
|
495
|
+
end
|
496
|
+
end
|
470
497
|
end
|
@@ -14,14 +14,36 @@ describe Mongoid::Association::EagerLoadable do
|
|
14
14
|
Mongoid::Contextual::Mongo.new(criteria)
|
15
15
|
end
|
16
16
|
|
17
|
+
let(:association_host) { Account }
|
18
|
+
|
17
19
|
let(:inclusions) do
|
18
20
|
includes.map do |key|
|
19
|
-
|
21
|
+
association_host.reflect_on_association(key)
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
let(:doc) { criteria.first }
|
24
26
|
|
27
|
+
context 'when root is an STI subclass' do
|
28
|
+
# Driver has_one Vehicle
|
29
|
+
# Vehicle belongs_to Driver
|
30
|
+
# Truck is a Vehicle
|
31
|
+
|
32
|
+
before do
|
33
|
+
Driver.create!(vehicle: Truck.new)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:criteria) { Truck.all }
|
37
|
+
let(:includes) { %i[ driver ] }
|
38
|
+
let(:association_host) { Truck }
|
39
|
+
|
40
|
+
it 'preloads the driver' do
|
41
|
+
expect(doc.ivar(:driver)).to be false
|
42
|
+
context.preload(inclusions, [ doc ])
|
43
|
+
expect(doc.ivar(:driver)).to be == Driver.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
25
47
|
context "when belongs_to" do
|
26
48
|
|
27
49
|
let!(:account) do
|
@@ -42,7 +64,7 @@ describe Mongoid::Association::EagerLoadable do
|
|
42
64
|
it "preloads the parent" do
|
43
65
|
expect(doc.ivar(:person)).to be false
|
44
66
|
context.preload(inclusions, [doc])
|
45
|
-
expect(doc.ivar(:person)).to
|
67
|
+
expect(doc.ivar(:person)).to be == person
|
46
68
|
end
|
47
69
|
end
|
48
70
|
|
@@ -27,6 +27,10 @@ describe Mongoid::Association::Embedded::EmbedsMany do
|
|
27
27
|
expect(legislator.attributes.keys).to eq(['_id', 'a'])
|
28
28
|
end
|
29
29
|
|
30
|
+
it 'allows accessing the parent' do
|
31
|
+
expect { legislator.congress }.not_to raise_error
|
32
|
+
end
|
33
|
+
|
30
34
|
context 'when using only with $' do
|
31
35
|
before do
|
32
36
|
Patient.destroy_all
|
@@ -100,6 +100,66 @@ describe Mongoid::Association do
|
|
100
100
|
expect(name).to_not be_an_embedded_many
|
101
101
|
end
|
102
102
|
end
|
103
|
+
|
104
|
+
context "when validation depends on association" do
|
105
|
+
before(:all) do
|
106
|
+
class Author
|
107
|
+
include Mongoid::Document
|
108
|
+
embeds_many :books, cascade_callbacks: true
|
109
|
+
field :condition, type: Boolean
|
110
|
+
end
|
111
|
+
|
112
|
+
class Book
|
113
|
+
include Mongoid::Document
|
114
|
+
embedded_in :author
|
115
|
+
validate :parent_condition_is_not_true
|
116
|
+
|
117
|
+
def parent_condition_is_not_true
|
118
|
+
return unless author&.condition
|
119
|
+
errors.add :base, "Author condition is true."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Author.delete_all
|
124
|
+
Book.delete_all
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:author) { Author.new }
|
128
|
+
let(:book) { Book.new }
|
129
|
+
|
130
|
+
context "when author is not persisted" do
|
131
|
+
it "is valid without books" do
|
132
|
+
expect(author.valid?).to be true
|
133
|
+
end
|
134
|
+
|
135
|
+
it "is valid with a book" do
|
136
|
+
author.books << book
|
137
|
+
expect(author.valid?).to be true
|
138
|
+
end
|
139
|
+
|
140
|
+
it "is not valid when condition is true with a book" do
|
141
|
+
author.condition = true
|
142
|
+
author.books << book
|
143
|
+
expect(author.valid?).to be false
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "when author is persisted" do
|
148
|
+
before do
|
149
|
+
author.books << book
|
150
|
+
author.save
|
151
|
+
end
|
152
|
+
|
153
|
+
it "remains valid initially" do
|
154
|
+
expect(author.valid?).to be true
|
155
|
+
end
|
156
|
+
|
157
|
+
it "becomes invalid when condition is set to true" do
|
158
|
+
author.update_attributes(condition: true)
|
159
|
+
expect(author.valid?).to be false
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
103
163
|
end
|
104
164
|
|
105
165
|
describe "#embedded_one?" do
|
@@ -591,6 +591,33 @@ describe Mongoid::Document do
|
|
591
591
|
expect(person.as_document["addresses"].first).to have_key(:locations)
|
592
592
|
end
|
593
593
|
|
594
|
+
context 'when modifying the returned object' do
|
595
|
+
let(:record) do
|
596
|
+
RootCategory.create(categories: [{ name: 'tests' }]).reload
|
597
|
+
end
|
598
|
+
|
599
|
+
shared_examples_for 'an object with protected internal state' do
|
600
|
+
it 'does not expose internal state' do
|
601
|
+
before_change = record.as_document.dup
|
602
|
+
record.categories.first.name = 'things'
|
603
|
+
after_change = record.as_document
|
604
|
+
expect(before_change['categories'].first['name']).not_to eq('things')
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
context 'when legacy_attributes is true' do
|
609
|
+
config_override :legacy_attributes, true
|
610
|
+
|
611
|
+
it_behaves_like 'an object with protected internal state'
|
612
|
+
end
|
613
|
+
|
614
|
+
context 'when legacy_attributes is false' do
|
615
|
+
config_override :legacy_attributes, false
|
616
|
+
|
617
|
+
it_behaves_like 'an object with protected internal state'
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
594
621
|
context "with relation define store_as option in embeded_many" do
|
595
622
|
|
596
623
|
let!(:phone) do
|