mirror of https://github.com/rails/rails
Dup options in validates_with
Some validators, such as validators that inherit from `EachValidator`, mutate the options they receive. This can cause problems when passing multiple validators and options to `validates_with`. This can also be a problem if a validator deletes standard options such as `:if` and `:on`, because the validation callback would then not receive them. This commit modifies `validates_with` to `dup` options before passing them to validators, thus preventing these issues. Fixes #44460. Closes #44476. Co-authored-by: Dieter Späth <dieter.spaeth@lanes-planes.com>
This commit is contained in:
parent
8a610a53a3
commit
5389c56292
|
@ -83,7 +83,7 @@ module ActiveModel
|
|||
options[:class] = self
|
||||
|
||||
args.each do |klass|
|
||||
validator = klass.new(options, &block)
|
||||
validator = klass.new(options.dup, &block)
|
||||
|
||||
if validator.respond_to?(:attributes) && !validator.attributes.empty?
|
||||
validator.attributes.each do |attribute|
|
||||
|
@ -139,7 +139,7 @@ module ActiveModel
|
|||
options[:class] = self.class
|
||||
|
||||
args.each do |klass|
|
||||
validator = klass.new(options, &block)
|
||||
validator = klass.new(options.dup, &block)
|
||||
validator.validate(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,13 @@ class ValidatesWithTest < ActiveModel::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class ValidatorThatClearsOptions < ValidatorThatDoesNotAddErrors
|
||||
def initialize(options)
|
||||
super
|
||||
options.clear
|
||||
end
|
||||
end
|
||||
|
||||
class ValidatorThatValidatesOptions < ActiveModel::Validator
|
||||
def validate(record)
|
||||
if options[:field] == :first_name
|
||||
|
@ -89,6 +96,25 @@ class ValidatesWithTest < ActiveModel::TestCase
|
|||
assert_includes topic.errors[:base], ERROR_MESSAGE
|
||||
end
|
||||
|
||||
test "validates_with preserves standard options" do
|
||||
Topic.validates_with(ValidatorThatClearsOptions, ValidatorThatAddsErrors, on: :specific_context)
|
||||
topic = Topic.new
|
||||
assert topic.invalid?(:specific_context), "validation should work"
|
||||
assert topic.valid?, "Standard options should be preserved"
|
||||
end
|
||||
|
||||
test "validates_with preserves validator options" do
|
||||
Topic.validates_with(ValidatorThatClearsOptions, ValidatorThatValidatesOptions, field: :first_name)
|
||||
topic = Topic.new
|
||||
assert topic.invalid?, "Validator options should be preserved"
|
||||
end
|
||||
|
||||
test "instance validates_with method preserves validator options" do
|
||||
topic = Topic.new
|
||||
topic.validates_with(ValidatorThatClearsOptions, ValidatorThatValidatesOptions, field: :first_name)
|
||||
assert_includes topic.errors[:base], ERROR_MESSAGE, "Validator options should be preserved"
|
||||
end
|
||||
|
||||
test "validates_with each validator" do
|
||||
Topic.validates_with(ValidatorPerEachAttribute, attributes: [:title, :content])
|
||||
topic = Topic.new title: "Title", content: "Content"
|
||||
|
|
Loading…
Reference in New Issue