Skip to content

Commit 02b6c6f

Browse files
authored
MONGOID-5945 don't try to valid destroyed children (#6154)
1 parent c76082f commit 02b6c6f

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

lib/mongoid/validatable/associated.rb

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,13 @@ def validate_association(document, attribute)
6767
valid = document.validating do
6868
# Now, treating the target as an array, look at each element
6969
# and see if it is valid, but only if it has already been
70-
# persisted, or changed, and hasn't been flagged for destroy.
70+
# persisted, or changed, and hasn't been flagged for destroy
71+
# or already destroyed.
7172
#
7273
# use map.all? instead of just all?, because all? will do short-circuit
7374
# evaluation and terminate on the first failed validation.
7475
list.map do |value|
75-
if value && !value.flagged_for_destroy? && (!value.persisted? || value.changed?)
76+
if needs_validation?(value)
7677
value.validated? || value.valid?
7778
else
7879
true
@@ -120,6 +121,21 @@ def get_target_documents_for_has_many(target)
120121
def get_target_documents_for_other(target)
121122
Array.wrap(target)
122123
end
124+
125+
# Returns true if the given value should be validated as part of
126+
# an associated validation. Destroyed and flagged-for-destroy
127+
# documents are skipped, as are persisted documents that haven't
128+
# changed.
129+
#
130+
# @param [ Mongoid::Document | nil ] value The value to check.
131+
#
132+
# @return [ true | false ] Whether the value needs validation.
133+
def needs_validation?(value)
134+
value &&
135+
!value.flagged_for_destroy? &&
136+
!value.destroyed? &&
137+
(!value.persisted? || value.changed?)
138+
end
123139
end
124140
end
125141
end

spec/mongoid/validatable/associated_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@
8282
expect(user).to be_valid
8383
end
8484
end
85+
86+
context 'when a child document has been destroyed' do
87+
let(:user) do
88+
User.new(name: 'test')
89+
end
90+
91+
let(:description) do
92+
Description.new
93+
end
94+
95+
before do
96+
user.descriptions << description
97+
description.destroyed = true
98+
end
99+
100+
it 'does not run validation on the destroyed child' do
101+
expect(user).to be_valid
102+
end
103+
end
85104
end
86105
end
87106

0 commit comments

Comments
 (0)