Skip to content

Commit e1e7981

Browse files
austenmaddenghiculescurafal-brize
committed
Pass validation_context to validates_associated
The docs imply that when specified the `on` option of `validates_associated` should be respected by the validated association. Prior to this change the validation context used will simply be the default behavior as if no `on` option were specified. With this change, the validation_context of the parent record is passed to the `valid?` check. When associations are created or updated allow them to use their own default context according to their own persistence status rather than overriding with the context of their associated object. Fixes rails#46239 Co-authored-by: Alex Ghiculescu <[email protected]> Co-authored-by: Rafał Brize <[email protected]>
1 parent 8397eb2 commit e1e7981

File tree

5 files changed

+43
-8
lines changed

5 files changed

+43
-8
lines changed

activerecord/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
* Validate using `:on` option when using `validates_associated`
2+
3+
Fixes an issue where `validates_associated` `:on` option wasn't respected
4+
when validated associated records.
5+
6+
*Austen Madden*, *Alex Ghiculescu*, *Rafał Brize*
7+
18
* Allow overriding SQLite defaults from `database.yml`
29

310
Any PRAGMA configuration set under the `pragmas` key in the configuration file take precedence over Rails' defaults, and additional PRAGMAs can be set as well.

activerecord/lib/active_record/autosave_association.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,10 +547,6 @@ def compute_primary_key(reflection, record)
547547
end
548548
end
549549

550-
def custom_validation_context?
551-
validation_context && [:create, :update].exclude?(validation_context)
552-
end
553-
554550
def _ensure_no_duplicate_errors
555551
errors.uniq!
556552
end

activerecord/lib/active_record/validations.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def valid?(context = nil)
7474

7575
alias_method :validate, :valid?
7676

77+
def custom_validation_context? # :nodoc:
78+
validation_context && [:create, :update].exclude?(validation_context)
79+
end
80+
7781
private
7882
def default_validation_context
7983
new_record? ? :create : :update

activerecord/lib/active_record/validations/associated.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@ module ActiveRecord
44
module Validations
55
class AssociatedValidator < ActiveModel::EachValidator # :nodoc:
66
def validate_each(record, attribute, value)
7-
if Array(value).reject { |r| valid_object?(r) }.any?
7+
context = record_validation_context_for_association(record)
8+
9+
if Array(value).reject { |association| valid_object?(association, context) }.any?
810
record.errors.add(attribute, :invalid, **options.merge(value: value))
911
end
1012
end
1113

1214
private
13-
def valid_object?(record)
14-
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
15+
def valid_object?(record, context)
16+
(record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?(context)
17+
end
18+
19+
def record_validation_context_for_association(record)
20+
record.custom_validation_context? ? record.validation_context : nil
1521
end
1622
end
1723

activerecord/test/cases/validations/association_validation_test.rb

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def test_validates_associated_marked_for_destruction
4949

5050
def test_validates_associated_without_marked_for_destruction
5151
reply = Class.new do
52-
def valid?
52+
def valid?(context = nil)
5353
true
5454
end
5555
end
@@ -96,4 +96,26 @@ def test_validates_presence_of_belongs_to_association__existing_parent
9696
assert_predicate interest, :valid?, "Expected interest to be valid, but was not. Interest should have a human object associated"
9797
end
9898
end
99+
100+
def test_validates_associated_with_custom_context
101+
Reply.validates_associated :topic, on: :custom
102+
Topic.validates_presence_of :content, on: :custom
103+
r = Reply.create("title" => "A reply", "content" => "with content!")
104+
r.topic = Topic.create("title" => "uhohuhoh")
105+
assert_predicate r, :valid?
106+
assert_not r.valid?(:custom)
107+
assert_equal ["is invalid"], r.errors[:topic]
108+
end
109+
110+
def test_validates_associated_with_create_context
111+
Reply.validates_associated :topic, on: :create
112+
Topic.validates_presence_of :content, on: :create
113+
t = Topic.create(title: "uhoh", content: "stuff")
114+
t.update!(content: nil)
115+
# NOTE: Does not pass along :create context from reply to Topic validation.
116+
r = t.replies.create(title: "A reply", content: "with content!")
117+
118+
assert_predicate t, :valid?
119+
assert_predicate r, :valid?
120+
end
99121
end

0 commit comments

Comments
 (0)