diff options
Diffstat (limited to 'activemodel/test')
-rw-r--r-- | activemodel/test/cases/attribute_methods_test.rb | 1 | ||||
-rw-r--r-- | activemodel/test/cases/attributes_test.rb | 14 | ||||
-rw-r--r-- | activemodel/test/cases/error_test.rb | 200 | ||||
-rw-r--r-- | activemodel/test/cases/errors_test.rb | 371 | ||||
-rw-r--r-- | activemodel/test/cases/nested_error_test.rb | 54 | ||||
-rw-r--r-- | activemodel/test/cases/railtie_test.rb | 6 | ||||
-rw-r--r-- | activemodel/test/cases/type/date_time_test.rb | 1 | ||||
-rw-r--r-- | activemodel/test/cases/validations/acceptance_validation_test.rb | 1 | ||||
-rw-r--r-- | activemodel/test/cases/validations/i18n_validation_test.rb | 118 | ||||
-rw-r--r-- | activemodel/test/cases/validations/numericality_validation_test.rb | 1 | ||||
-rw-r--r-- | activemodel/test/cases/validations/validations_context_test.rb | 4 | ||||
-rw-r--r-- | activemodel/test/cases/validations/with_validation_test.rb | 8 | ||||
-rw-r--r-- | activemodel/test/cases/validations_test.rb | 14 | ||||
-rw-r--r-- | activemodel/test/models/person_with_validator.rb | 2 | ||||
-rw-r--r-- | activemodel/test/models/reply.rb | 10 | ||||
-rw-r--r-- | activemodel/test/validators/email_validator.rb | 5 |
16 files changed, 696 insertions, 114 deletions
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 4e228032c3..e3b3b15f25 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -40,7 +40,6 @@ private end protected - def protected_method "O_o O_o" end diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb index 5483fb101d..af0ddcb92f 100644 --- a/activemodel/test/cases/attributes_test.rb +++ b/activemodel/test/cases/attributes_test.rb @@ -67,6 +67,20 @@ module ActiveModel assert_equal expected_attributes, data.attributes end + test "reading attribute names" do + names = [ + "integer_field", + "string_field", + "decimal_field", + "string_with_default", + "date_field", + "boolean_field" + ] + + assert_equal names, ModelForAttributesTest.attribute_names + assert_equal names, ModelForAttributesTest.new.attribute_names + end + test "nonexistent attribute" do assert_raise ActiveModel::UnknownAttributeError do ModelForAttributesTest.new(nonexistent: "nonexistent") diff --git a/activemodel/test/cases/error_test.rb b/activemodel/test/cases/error_test.rb new file mode 100644 index 0000000000..d74321fee5 --- /dev/null +++ b/activemodel/test/cases/error_test.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +require "cases/helper" +require "active_model/error" + +class ErrorTest < ActiveModel::TestCase + class Person + extend ActiveModel::Naming + def initialize + @errors = ActiveModel::Errors.new(self) + end + + attr_accessor :name, :age + attr_reader :errors + + def read_attribute_for_validation(attr) + send(attr) + end + + def self.human_attribute_name(attr, options = {}) + attr + end + + def self.lookup_ancestors + [self] + end + end + + def test_initialize + base = Person.new + error = ActiveModel::Error.new(base, :name, :too_long, foo: :bar) + assert_equal base, error.base + assert_equal :name, error.attribute + assert_equal :too_long, error.type + assert_equal({ foo: :bar }, error.options) + end + + test "initialize without type" do + error = ActiveModel::Error.new(Person.new, :name) + assert_equal :invalid, error.type + assert_equal({}, error.options) + end + + test "initialize without type but with options" do + options = { message: "bar" } + error = ActiveModel::Error.new(Person.new, :name, options) + assert_equal(options, error.options) + end + + # match? + + test "match? handles mixed condition" do + subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2) + assert_not subject.match?(:mineral, :too_coarse) + assert subject.match?(:mineral, :not_enough) + assert subject.match?(:mineral, :not_enough, count: 2) + assert_not subject.match?(:mineral, :not_enough, count: 1) + end + + test "match? handles attribute match" do + subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2) + assert_not subject.match?(:foo) + assert subject.match?(:mineral) + end + + test "match? handles error type match" do + subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2) + assert_not subject.match?(:mineral, :too_coarse) + assert subject.match?(:mineral, :not_enough) + end + + test "match? handles extra options match" do + subject = ActiveModel::Error.new(Person.new, :mineral, :not_enough, count: 2) + assert_not subject.match?(:mineral, :not_enough, count: 1) + assert subject.match?(:mineral, :not_enough, count: 2) + end + + # message + + test "message with type as a symbol" do + error = ActiveModel::Error.new(Person.new, :name, :blank) + assert_equal "can't be blank", error.message + end + + test "message with custom interpolation" do + subject = ActiveModel::Error.new(Person.new, :name, :inclusion, message: "custom message %{value}", value: "name") + assert_equal "custom message name", subject.message + end + + test "message returns plural interpolation" do + subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 10) + assert_equal "is too long (maximum is 10 characters)", subject.message + end + + test "message returns singular interpolation" do + subject = ActiveModel::Error.new(Person.new, :name, :too_long, count: 1) + assert_equal "is too long (maximum is 1 character)", subject.message + end + + test "message returns count interpolation" do + subject = ActiveModel::Error.new(Person.new, :name, :too_long, message: "custom message %{count}", count: 10) + assert_equal "custom message 10", subject.message + end + + test "message handles lambda in messages and option values, and i18n interpolation" do + subject = ActiveModel::Error.new(Person.new, :name, :invalid, + foo: "foo", + bar: "bar", + baz: Proc.new { "baz" }, + message: Proc.new { |model, options| + "%{attribute} %{foo} #{options[:bar]} %{baz}" + } + ) + assert_equal "name foo bar baz", subject.message + end + + test "generate_message works without i18n_scope" do + person = Person.new + error = ActiveModel::Error.new(person, :name, :blank) + assert_not_respond_to Person, :i18n_scope + assert_nothing_raised { + error.message + } + end + + test "message with type as custom message" do + error = ActiveModel::Error.new(Person.new, :name, message: "cannot be blank") + assert_equal "cannot be blank", error.message + end + + test "message with options[:message] as custom message" do + error = ActiveModel::Error.new(Person.new, :name, :blank, message: "cannot be blank") + assert_equal "cannot be blank", error.message + end + + test "message renders lazily using current locale" do + error = nil + + I18n.backend.store_translations(:pl, errors: { messages: { invalid: "jest nieprawidłowe" } }) + + I18n.with_locale(:en) { error = ActiveModel::Error.new(Person.new, :name, :invalid) } + I18n.with_locale(:pl) { + assert_equal "jest nieprawidłowe", error.message + } + end + + test "message uses current locale" do + I18n.backend.store_translations(:en, errors: { messages: { inadequate: "Inadequate %{attribute} found!" } }) + error = ActiveModel::Error.new(Person.new, :name, :inadequate) + assert_equal "Inadequate name found!", error.message + end + + # full_message + + test "full_message returns the given message when attribute is :base" do + error = ActiveModel::Error.new(Person.new, :base, message: "press the button") + assert_equal "press the button", error.full_message + end + + test "full_message returns the given message with the attribute name included" do + error = ActiveModel::Error.new(Person.new, :name, :blank) + assert_equal "name can't be blank", error.full_message + end + + test "full_message uses default format" do + error = ActiveModel::Error.new(Person.new, :name, message: "can't be blank") + + # Use a locale without errors.format + I18n.with_locale(:unknown) { + assert_equal "name can't be blank", error.full_message + } + end + + test "equality by base attribute, type and options" do + person = Person.new + + e1 = ActiveModel::Error.new(person, :name, foo: :bar) + e2 = ActiveModel::Error.new(person, :name, foo: :bar) + e2.instance_variable_set(:@_humanized_attribute, "Name") + + assert_equal(e1, e2) + end + + test "inequality" do + person = Person.new + error = ActiveModel::Error.new(person, :name, foo: :bar) + + assert error != ActiveModel::Error.new(person, :name, foo: :baz) + assert error != ActiveModel::Error.new(person, :name) + assert error != ActiveModel::Error.new(person, :title, foo: :bar) + assert error != ActiveModel::Error.new(Person.new, :name, foo: :bar) + end + + test "comparing against different class would not raise error" do + person = Person.new + error = ActiveModel::Error.new(person, :name, foo: :bar) + + assert error != person + end +end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 947f9bf99b..a6cd1da717 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -10,7 +10,7 @@ class ErrorsTest < ActiveModel::TestCase @errors = ActiveModel::Errors.new(self) end - attr_accessor :name, :age + attr_accessor :name, :age, :gender, :city attr_reader :errors def validate! @@ -31,48 +31,66 @@ class ErrorsTest < ActiveModel::TestCase end def test_delete - errors = ActiveModel::Errors.new(self) - errors[:foo] << "omg" - errors.delete("foo") - assert_empty errors[:foo] + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :blank) + errors.delete("name") + assert_empty errors[:name] end def test_include? - errors = ActiveModel::Errors.new(self) - errors[:foo] << "omg" + errors = ActiveModel::Errors.new(Person.new) + assert_deprecated { errors[:foo] << "omg" } assert_includes errors, :foo, "errors should include :foo" assert_includes errors, "foo", "errors should include 'foo' as :foo" end + def test_each_when_arity_is_negative + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :blank) + errors.add(:gender, :blank) + + assert_equal([:name, :gender], errors.map(&:attribute)) + end + + def test_any? + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name) + assert_not_deprecated { + assert errors.any?, "any? should return true" + } + assert_not_deprecated { + assert errors.any? { |_| true }, "any? should return true" + } + end + def test_dup - errors = ActiveModel::Errors.new(self) - errors[:foo] << "bar" + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name) errors_dup = errors.dup - errors_dup[:bar] << "omg" - assert_not_same errors_dup.messages, errors.messages + assert_not_same errors_dup.errors, errors.errors end def test_has_key? - errors = ActiveModel::Errors.new(self) - errors[:foo] << "omg" + errors = ActiveModel::Errors.new(Person.new) + errors.add(:foo, "omg") assert_equal true, errors.has_key?(:foo), "errors should have key :foo" assert_equal true, errors.has_key?("foo"), "errors should have key 'foo' as :foo" end def test_has_no_key - errors = ActiveModel::Errors.new(self) + errors = ActiveModel::Errors.new(Person.new) assert_equal false, errors.has_key?(:name), "errors should not have key :name" end def test_key? - errors = ActiveModel::Errors.new(self) - errors[:foo] << "omg" + errors = ActiveModel::Errors.new(Person.new) + errors.add(:foo, "omg") assert_equal true, errors.key?(:foo), "errors should have key :foo" assert_equal true, errors.key?("foo"), "errors should have key 'foo' as :foo" end def test_no_key - errors = ActiveModel::Errors.new(self) + errors = ActiveModel::Errors.new(Person.new) assert_equal false, errors.key?(:name), "errors should not have key :name" end @@ -86,42 +104,58 @@ class ErrorsTest < ActiveModel::TestCase end test "error access is indifferent" do - errors = ActiveModel::Errors.new(self) - errors[:foo] << "omg" + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, "omg") - assert_equal ["omg"], errors["foo"] + assert_equal ["omg"], errors["name"] end test "values returns an array of messages" do + errors = ActiveModel::Errors.new(Person.new) + assert_deprecated { errors.messages[:foo] = "omg" } + assert_deprecated { errors.messages[:baz] = "zomg" } + + assert_deprecated do + assert_equal ["omg", "zomg"], errors.values + end + end + + test "[]= overrides values" do errors = ActiveModel::Errors.new(self) - errors.messages[:foo] = "omg" - errors.messages[:baz] = "zomg" + assert_deprecated { errors.messages[:foo] = "omg" } + assert_deprecated { errors.messages[:foo] = "zomg" } - assert_equal ["omg", "zomg"], errors.values + assert_equal ["zomg"], errors[:foo] end test "values returns an empty array after try to get a message only" do - errors = ActiveModel::Errors.new(self) + errors = ActiveModel::Errors.new(Person.new) errors.messages[:foo] errors.messages[:baz] - assert_equal [], errors.values + assert_deprecated do + assert_equal [], errors.values + end end test "keys returns the error keys" do - errors = ActiveModel::Errors.new(self) - errors.messages[:foo] << "omg" - errors.messages[:baz] << "zomg" + errors = ActiveModel::Errors.new(Person.new) + assert_deprecated { errors.messages[:foo] << "omg" } + assert_deprecated { errors.messages[:baz] << "zomg" } - assert_equal [:foo, :baz], errors.keys + assert_deprecated do + assert_equal [:foo, :baz], errors.keys + end end test "keys returns an empty array after try to get a message only" do - errors = ActiveModel::Errors.new(self) + errors = ActiveModel::Errors.new(Person.new) errors.messages[:foo] errors.messages[:baz] - assert_equal [], errors.keys + assert_deprecated do + assert_equal [], errors.keys + end end test "detecting whether there are errors with empty?, blank?, include?" do @@ -146,32 +180,130 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["cannot be nil"], person.errors[:name] end - test "add an error message on a specific attribute" do + test "add an error message on a specific attribute (deprecated)" do person = Person.new person.errors.add(:name, "cannot be blank") assert_equal ["cannot be blank"], person.errors[:name] end - test "add an error message on a specific attribute with a defined type" do + test "add an error message on a specific attribute with a defined type (deprecated)" do person = Person.new person.errors.add(:name, :blank, message: "cannot be blank") assert_equal ["cannot be blank"], person.errors[:name] end - test "add an error with a symbol" do + test "add an error with a symbol (deprecated)" do person = Person.new person.errors.add(:name, :blank) message = person.errors.generate_message(:name, :blank) assert_equal [message], person.errors[:name] end - test "add an error with a proc" do + test "add an error with a proc (deprecated)" do person = Person.new message = Proc.new { "cannot be blank" } person.errors.add(:name, message) assert_equal ["cannot be blank"], person.errors[:name] end + test "add creates an error object and returns it" do + person = Person.new + error = person.errors.add(:name, :blank) + + assert_equal :name, error.attribute + assert_equal :blank, error.type + assert_equal error, person.errors.objects.first + end + + test "add, with type as symbol" do + person = Person.new + person.errors.add(:name, :blank) + + assert_equal :blank, person.errors.objects.first.type + assert_equal ["can't be blank"], person.errors[:name] + end + + test "add, with type as String" do + msg = "custom msg" + + person = Person.new + person.errors.add(:name, msg) + + assert_equal [msg], person.errors[:name] + end + + test "add, with type as nil" do + person = Person.new + person.errors.add(:name) + + assert_equal :invalid, person.errors.objects.first.type + assert_equal ["is invalid"], person.errors[:name] + end + + test "add, with type as Proc, which evaluates to String" do + msg = "custom msg" + type = Proc.new { msg } + + person = Person.new + person.errors.add(:name, type) + + assert_equal [msg], person.errors[:name] + end + + test "add, type being Proc, which evaluates to Symbol" do + type = Proc.new { :blank } + + person = Person.new + person.errors.add(:name, type) + + assert_equal :blank, person.errors.objects.first.type + assert_equal ["can't be blank"], person.errors[:name] + end + + test "initialize options[:message] as Proc, which evaluates to String" do + msg = "custom msg" + type = Proc.new { msg } + + person = Person.new + person.errors.add(:name, :blank, message: type) + + assert_equal :blank, person.errors.objects.first.type + assert_equal [msg], person.errors[:name] + end + + test "add, with options[:message] as Proc, which evaluates to String, where type is nil" do + msg = "custom msg" + type = Proc.new { msg } + + person = Person.new + person.errors.add(:name, message: type) + + assert_equal :invalid, person.errors.objects.first.type + assert_equal [msg], person.errors[:name] + end + + test "added? when attribute was added through a collection" do + person = Person.new + person.errors.add(:"family_members.name", :too_long, count: 25) + assert person.errors.added?(:"family_members.name", :too_long, count: 25) + assert_not person.errors.added?(:"family_members.name", :too_long) + assert_not person.errors.added?(:"family_members.name", :too_long, name: "hello") + end + + test "added? ignores callback option" do + person = Person.new + + person.errors.add(:name, :too_long, if: -> { true }) + assert person.errors.added?(:name, :too_long) + end + + test "added? ignores message option" do + person = Person.new + + person.errors.add(:name, :too_long, message: proc { "foo" }) + assert person.errors.added?(:name, :too_long) + end + test "added? detects indifferent if a specific error was added to the object" do person = Person.new person.errors.add(:name, "cannot be blank") @@ -342,6 +474,17 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a end + test "to_h is deprecated" do + person = Person.new + person.errors.add(:name, "cannot be blank") + person.errors.add(:name, "too long") + + expected_deprecation = "ActiveModel::Errors#to_h is deprecated" + assert_deprecated(expected_deprecation) do + assert_equal({ name: "too long" }, person.errors.to_h) + end + end + test "to_hash returns the error messages hash" do person = Person.new person.errors.add(:name, "cannot be blank") @@ -358,6 +501,27 @@ class ErrorsTest < ActiveModel::TestCase assert_nil person.errors.as_json.default_proc end + test "full_messages doesn't require the base object to respond to `:errors" do + model = Class.new do + def initialize + @errors = ActiveModel::Errors.new(self) + @errors.add(:name, "bar") + end + + def self.human_attribute_name(attr, options = {}) + "foo" + end + + def call + error_wrapper = Struct.new(:model_errors) + + error_wrapper.new(@errors) + end + end + + assert_equal(["foo bar"], model.new.call.model_errors.full_messages) + end + test "full_messages creates a list of error messages with the attribute name included" do person = Person.new person.errors.add(:name, "cannot be blank") @@ -437,6 +601,32 @@ class ErrorsTest < ActiveModel::TestCase assert_equal({ name: [{ error: :invalid }] }, person.errors.details) end + test "details retains original type as error" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, "cannot be nil") + errors.add("foo", "bar") + errors.add(:baz, nil) + errors.add(:age, :invalid, count: 3, message: "%{count} is too low") + + assert_equal( + { + name: [{ error: "cannot be nil" }], + foo: [{ error: "bar" }], + baz: [{ error: nil }], + age: [{ error: :invalid, count: 3 }] + }, + errors.details + ) + end + + test "group_by_attribute" do + person = Person.new + error = person.errors.add(:name, :invalid, message: "is bad") + hash = person.errors.group_by_attribute + + assert_equal({ name: [error] }, hash) + end + test "dup duplicates details" do errors = ActiveModel::Errors.new(Person.new) errors.add(:name, :invalid) @@ -445,11 +635,17 @@ class ErrorsTest < ActiveModel::TestCase assert_not_equal errors_dup.details, errors.details end + test "delete returns nil when no errors were deleted" do + errors = ActiveModel::Errors.new(Person.new) + + assert_nil(errors.delete(:name)) + end + test "delete removes details on given attribute" do errors = ActiveModel::Errors.new(Person.new) errors.add(:name, :invalid) errors.delete(:name) - assert_empty errors.details[:name] + assert_not errors.added?(:name) end test "delete returns the deleted messages" do @@ -467,7 +663,7 @@ class ErrorsTest < ActiveModel::TestCase assert_empty person.errors.details end - test "copy errors" do + test "copy errors (deprecated)" do errors = ActiveModel::Errors.new(Person.new) errors.add(:name, :invalid) person = Person.new @@ -477,7 +673,25 @@ class ErrorsTest < ActiveModel::TestCase assert_equal [:name], person.errors.details.keys end - test "merge errors" do + test "details returns empty array when accessed with non-existent attribute" do + errors = ActiveModel::Errors.new(Person.new) + + assert_equal [], errors.details[:foo] + end + + test "copy errors" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :invalid) + person = Person.new + person.errors.copy!(errors) + + assert person.errors.added?(:name, :invalid) + person.errors.each do |error| + assert_same person, error.base + end + end + + test "merge errors (deprecated)" do errors = ActiveModel::Errors.new(Person.new) errors.add(:name, :invalid) @@ -489,6 +703,18 @@ class ErrorsTest < ActiveModel::TestCase assert_equal({ name: [{ error: :blank }, { error: :invalid }] }, person.errors.details) end + test "merge errors" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :invalid) + + person = Person.new + person.errors.add(:name, :blank) + person.errors.merge!(errors) + + assert(person.errors.added?(:name, :invalid)) + assert(person.errors.added?(:name, :blank)) + end + test "slice! removes all errors except the given keys" do person = Person.new person.errors.add(:name, "cannot be nil") @@ -496,9 +722,9 @@ class ErrorsTest < ActiveModel::TestCase person.errors.add(:gender, "cannot be nil") person.errors.add(:city, "cannot be nil") - person.errors.slice!(:age, "gender") + assert_deprecated { person.errors.slice!(:age, "gender") } - assert_equal [:age, :gender], person.errors.keys + assert_equal [:age, :gender], assert_deprecated { person.errors.keys } end test "slice! returns the deleted errors" do @@ -508,7 +734,7 @@ class ErrorsTest < ActiveModel::TestCase person.errors.add(:gender, "cannot be nil") person.errors.add(:city, "cannot be nil") - removed_errors = person.errors.slice!(:age, "gender") + removed_errors = assert_deprecated { person.errors.slice!(:age, "gender") } assert_equal({ name: ["cannot be nil"], city: ["cannot be nil"] }, removed_errors) end @@ -518,10 +744,23 @@ class ErrorsTest < ActiveModel::TestCase errors.add(:name, :invalid) serialized = Marshal.load(Marshal.dump(errors)) + assert_equal Person, serialized.instance_variable_get(:@base).class assert_equal errors.messages, serialized.messages assert_equal errors.details, serialized.details end + test "errors are compatible with marshal dumped from Rails 5.x" do + # Derived from + # errors = ActiveModel::Errors.new(Person.new) + # errors.add(:name, :invalid) + dump = "\x04\bU:\x18ActiveModel::Errors[\bo:\x17ErrorsTest::Person\x06:\f@errorsU;\x00[\b@\a{\x00{\x00{\x06:\tname[\x06I\"\x0Fis invalid\x06:\x06ET{\x06;\b[\x06{\x06:\nerror:\finvalid" + serialized = Marshal.load(dump) + + assert_equal Person, serialized.instance_variable_get(:@base).class + assert_equal({ name: ["is invalid"] }, serialized.messages) + assert_equal({ name: [{ error: :invalid }] }, serialized.details) + end + test "errors are backward compatible with the Rails 4.2 format" do yaml = <<~CODE --- !ruby/object:ActiveModel::Errors @@ -541,4 +780,54 @@ class ErrorsTest < ActiveModel::TestCase assert_equal({}, errors.messages) assert_equal({}, errors.details) end + + test "errors are compatible with YAML dumped from Rails 5.x" do + yaml = <<~CODE + --- !ruby/object:ActiveModel::Errors + base: &1 !ruby/object:ErrorsTest::Person + errors: !ruby/object:ActiveModel::Errors + base: *1 + messages: {} + details: {} + messages: + :name: + - is invalid + details: + :name: + - :error: :invalid + CODE + + errors = YAML.load(yaml) + assert_equal({ name: ["is invalid"] }, errors.messages) + assert_equal({ name: [{ error: :invalid }] }, errors.details) + + errors.clear + assert_equal({}, errors.messages) + assert_equal({}, errors.details) + end + + test "errors are compatible with YAML dumped from Rails 6.x" do + yaml = <<~CODE + --- !ruby/object:ActiveModel::Errors + base: &1 !ruby/object:ErrorsTest::Person + errors: !ruby/object:ActiveModel::Errors + base: *1 + errors: [] + errors: + - !ruby/object:ActiveModel::Error + base: *1 + attribute: :name + type: :invalid + raw_type: :invalid + options: {} + CODE + + errors = YAML.load(yaml) + assert_equal({ name: ["is invalid"] }, errors.messages) + assert_equal({ name: [{ error: :invalid }] }, errors.details) + + errors.clear + assert_equal({}, errors.messages) + assert_equal({}, errors.details) + end end diff --git a/activemodel/test/cases/nested_error_test.rb b/activemodel/test/cases/nested_error_test.rb new file mode 100644 index 0000000000..6c2458ba83 --- /dev/null +++ b/activemodel/test/cases/nested_error_test.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require "cases/helper" +require "active_model/nested_error" +require "models/topic" +require "models/reply" + +class NestedErrorTest < ActiveModel::TestCase + def test_initialize + topic = Topic.new + inner_error = ActiveModel::Error.new(topic, :title, :not_enough, count: 2) + reply = Reply.new + error = ActiveModel::NestedError.new(reply, inner_error) + + assert_equal reply, error.base + assert_equal inner_error.attribute, error.attribute + assert_equal inner_error.type, error.type + assert_equal(inner_error.options, error.options) + end + + test "initialize with overriding attribute and type" do + topic = Topic.new + inner_error = ActiveModel::Error.new(topic, :title, :not_enough, count: 2) + reply = Reply.new + error = ActiveModel::NestedError.new(reply, inner_error, attribute: :parent, type: :foo) + + assert_equal reply, error.base + assert_equal :parent, error.attribute + assert_equal :foo, error.type + assert_equal(inner_error.options, error.options) + end + + def test_message + topic = Topic.new(author_name: "Bruce") + inner_error = ActiveModel::Error.new(topic, :title, :not_enough, message: Proc.new { |model, options| + "not good enough for #{model.author_name}" + }) + reply = Reply.new(author_name: "Mark") + error = ActiveModel::NestedError.new(reply, inner_error) + + assert_equal "not good enough for Bruce", error.message + end + + def test_full_message + topic = Topic.new(author_name: "Bruce") + inner_error = ActiveModel::Error.new(topic, :title, :not_enough, message: Proc.new { |model, options| + "not good enough for #{model.author_name}" + }) + reply = Reply.new(author_name: "Mark") + error = ActiveModel::NestedError.new(reply, inner_error) + + assert_equal "Title not good enough for Bruce", error.full_message + end +end diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb index 95ee7cace3..f5ff1a3fd7 100644 --- a/activemodel/test/cases/railtie_test.rb +++ b/activemodel/test/cases/railtie_test.rb @@ -35,20 +35,20 @@ class RailtieTest < ActiveModel::TestCase test "i18n customize full message defaults to false" do @app.initialize! - assert_equal false, ActiveModel::Errors.i18n_customize_full_message + assert_equal false, ActiveModel::Error.i18n_customize_full_message end test "i18n customize full message can be disabled" do @app.config.active_model.i18n_customize_full_message = false @app.initialize! - assert_equal false, ActiveModel::Errors.i18n_customize_full_message + assert_equal false, ActiveModel::Error.i18n_customize_full_message end test "i18n customize full message can be enabled" do @app.config.active_model.i18n_customize_full_message = true @app.initialize! - assert_equal true, ActiveModel::Errors.i18n_customize_full_message + assert_equal true, ActiveModel::Error.i18n_customize_full_message end end diff --git a/activemodel/test/cases/type/date_time_test.rb b/activemodel/test/cases/type/date_time_test.rb index 74b47d1b4d..4a63eee0cf 100644 --- a/activemodel/test/cases/type/date_time_test.rb +++ b/activemodel/test/cases/type/date_time_test.rb @@ -37,7 +37,6 @@ module ActiveModel end private - def with_timezone_config(default:) old_zone_default = ::Time.zone_default ::Time.zone_default = ::Time.find_zone(default) diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb index 72baf6e7a7..6bd3d292f8 100644 --- a/activemodel/test/cases/validations/acceptance_validation_test.rb +++ b/activemodel/test/cases/validations/acceptance_validation_test.rb @@ -92,7 +92,6 @@ class AcceptanceValidationTest < ActiveModel::TestCase end private - # Acceptance validator includes anonymous module into class, which cannot # be cleared, so to avoid multiple inclusions we use a named subclass which # we can remove in teardown. diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 35bb918f26..c81649f493 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -13,8 +13,8 @@ class I18nValidationTest < ActiveModel::TestCase I18n.backend = I18n::Backend::Simple.new I18n.backend.store_translations("en", errors: { messages: { custom: nil } }) - @original_i18n_customize_full_message = ActiveModel::Errors.i18n_customize_full_message - ActiveModel::Errors.i18n_customize_full_message = true + @original_i18n_customize_full_message = ActiveModel::Error.i18n_customize_full_message + ActiveModel::Error.i18n_customize_full_message = true end def teardown @@ -24,7 +24,7 @@ class I18nValidationTest < ActiveModel::TestCase I18n.load_path.replace @old_load_path I18n.backend = @old_backend I18n.backend.reload! - ActiveModel::Errors.i18n_customize_full_message = @original_i18n_customize_full_message + ActiveModel::Error.i18n_customize_full_message = @original_i18n_customize_full_message end def test_full_message_encoding @@ -49,7 +49,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_doesnt_use_attribute_format_without_config - ActiveModel::Errors.i18n_customize_full_message = false + ActiveModel::Error.i18n_customize_full_message = false I18n.backend.store_translations("en", activemodel: { errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } }) @@ -59,8 +59,21 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal "Name test cannot be blank", person.errors.full_message(:name_test, "cannot be blank") end + def test_errors_full_messages_on_nested_error_uses_attribute_format + ActiveModel::Error.i18n_customize_full_message = true + I18n.backend.store_translations("en", activemodel: { + errors: { models: { person: { attributes: { gender: "Gender" } } } }, + attributes: { "person/contacts": { gender: "Gender" } } + }) + + person = person_class.new + error = ActiveModel::Error.new(person, :gender, "can't be blank") + person.errors.import(error, attribute: "person[0].contacts.gender") + assert_equal ["Gender can't be blank"], person.errors.full_messages + end + def test_errors_full_messages_uses_attribute_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } }) @@ -71,7 +84,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_model_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { person: { format: "%{message}" } } } }) @@ -82,7 +95,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_deeply_nested_model_attributes_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } }) @@ -93,7 +106,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_deeply_nested_model_model_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } }) @@ -104,7 +117,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_attributes_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } }) @@ -115,7 +128,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_model_format - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } }) @@ -126,7 +139,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_i18n_attribute_name - ActiveModel::Errors.i18n_customize_full_message = true + ActiveModel::Error.i18n_customize_full_message = true I18n.backend.store_translations("en", activemodel: { attributes: { 'person/contacts/addresses': { country: "Country" } } @@ -138,7 +151,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_with_indexed_deeply_nested_attributes_without_i18n_config - ActiveModel::Errors.i18n_customize_full_message = false + ActiveModel::Error.i18n_customize_full_message = false I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } }) @@ -149,7 +162,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_with_i18n_attribute_name_without_i18n_config - ActiveModel::Errors.i18n_customize_full_message = false + ActiveModel::Error.i18n_customize_full_message = false I18n.backend.store_translations("en", activemodel: { attributes: { 'person/contacts[0]/addresses[0]': { country: "Country" } } @@ -169,8 +182,8 @@ class I18nValidationTest < ActiveModel::TestCase # [ case, validation_options, generate_message_options] [ "given no options", {}, {}], [ "given custom message", { message: "custom" }, { message: "custom" }], - [ "given if condition", { if: lambda { true } }, {}], - [ "given unless condition", { unless: lambda { false } }, {}], + [ "given if condition", { if: lambda { true } }, {}], + [ "given unless condition", { unless: lambda { false } }, {}], [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }] ] @@ -178,9 +191,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_confirmation_of on generated message #{name}" do person_class.validates_confirmation_of :title, validation_options @person.title_confirmation = "foo" - call = [:title_confirmation, :confirmation, generate_message_options.merge(attribute: "Title")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title_confirmation, :confirmation, @person, generate_message_options.merge(attribute: "Title")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -188,9 +202,10 @@ class I18nValidationTest < ActiveModel::TestCase COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_acceptance_of on generated message #{name}" do person_class.validates_acceptance_of :title, validation_options.merge(allow_nil: false) - call = [:title, :accepted, generate_message_options] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :accepted, @person, generate_message_options] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -198,9 +213,10 @@ class I18nValidationTest < ActiveModel::TestCase COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_presence_of on generated message #{name}" do person_class.validates_presence_of :title, validation_options - call = [:title, :blank, generate_message_options] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :blank, @person, generate_message_options] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -208,9 +224,10 @@ class I18nValidationTest < ActiveModel::TestCase COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_length_of for :within on generated message when too short #{name}" do person_class.validates_length_of :title, validation_options.merge(within: 3..5) - call = [:title, :too_short, generate_message_options.merge(count: 3)] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :too_short, @person, generate_message_options.merge(count: 3)] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -219,9 +236,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_length_of for :too_long generated message #{name}" do person_class.validates_length_of :title, validation_options.merge(within: 3..5) @person.title = "this title is too long" - call = [:title, :too_long, generate_message_options.merge(count: 5)] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :too_long, @person, generate_message_options.merge(count: 5)] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -229,9 +247,10 @@ class I18nValidationTest < ActiveModel::TestCase COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_length_of for :is on generated message #{name}" do person_class.validates_length_of :title, validation_options.merge(is: 5) - call = [:title, :wrong_length, generate_message_options.merge(count: 5)] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :wrong_length, @person, generate_message_options.merge(count: 5)] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -240,9 +259,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_format_of on generated message #{name}" do person_class.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/) @person.title = "72x" - call = [:title, :invalid, generate_message_options.merge(value: "72x")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :invalid, @person, generate_message_options.merge(value: "72x")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -251,9 +271,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_inclusion_of on generated message #{name}" do person_class.validates_inclusion_of :title, validation_options.merge(in: %w(a b c)) @person.title = "z" - call = [:title, :inclusion, generate_message_options.merge(value: "z")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -262,9 +283,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_inclusion_of using :within on generated message #{name}" do person_class.validates_inclusion_of :title, validation_options.merge(within: %w(a b c)) @person.title = "z" - call = [:title, :inclusion, generate_message_options.merge(value: "z")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :inclusion, @person, generate_message_options.merge(value: "z")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -273,9 +295,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_exclusion_of generated message #{name}" do person_class.validates_exclusion_of :title, validation_options.merge(in: %w(a b c)) @person.title = "a" - call = [:title, :exclusion, generate_message_options.merge(value: "a")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -284,9 +307,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_exclusion_of using :within generated message #{name}" do person_class.validates_exclusion_of :title, validation_options.merge(within: %w(a b c)) @person.title = "a" - call = [:title, :exclusion, generate_message_options.merge(value: "a")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :exclusion, @person, generate_message_options.merge(value: "a")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -295,9 +319,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_numericality_of generated message #{name}" do person_class.validates_numericality_of :title, validation_options @person.title = "a" - call = [:title, :not_a_number, generate_message_options.merge(value: "a")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :not_a_number, @person, generate_message_options.merge(value: "a")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -306,9 +331,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_numericality_of for :only_integer on generated message #{name}" do person_class.validates_numericality_of :title, validation_options.merge(only_integer: true) @person.title = "0.0" - call = [:title, :not_an_integer, generate_message_options.merge(value: "0.0")] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :not_an_integer, @person, generate_message_options.merge(value: "0.0")] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -317,9 +343,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_numericality_of for :odd on generated message #{name}" do person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true) @person.title = 0 - call = [:title, :odd, generate_message_options.merge(value: 0)] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :odd, @person, generate_message_options.merge(value: 0)] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end @@ -328,9 +355,10 @@ class I18nValidationTest < ActiveModel::TestCase test "validates_numericality_of for :less_than on generated message #{name}" do person_class.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0) @person.title = 1 - call = [:title, :less_than, generate_message_options.merge(value: 1, count: 0)] - assert_called_with(@person.errors, :generate_message, call) do + call = [:title, :less_than, @person, generate_message_options.merge(value: 1, count: 0)] + assert_called_with(ActiveModel::Error, :generate_message, call) do @person.valid? + @person.errors.messages end end end diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 16c44762cb..191af033df 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -310,7 +310,6 @@ class NumericalityValidationTest < ActiveModel::TestCase end private - def invalid!(values, error = nil) with_each_topic_approved_value(values) do |topic, value| assert topic.invalid?, "#{value.inspect} not rejected as a number" diff --git a/activemodel/test/cases/validations/validations_context_test.rb b/activemodel/test/cases/validations/validations_context_test.rb index 024eb1882f..3d2dea9828 100644 --- a/activemodel/test/cases/validations/validations_context_test.rb +++ b/activemodel/test/cases/validations/validations_context_test.rb @@ -14,13 +14,13 @@ class ValidationsContextTest < ActiveModel::TestCase class ValidatorThatAddsErrors < ActiveModel::Validator def validate(record) - record.errors[:base] << ERROR_MESSAGE + record.errors.add(:base, ERROR_MESSAGE) end end class AnotherValidatorThatAddsErrors < ActiveModel::Validator def validate(record) - record.errors[:base] << ANOTHER_ERROR_MESSAGE + record.errors.add(:base, ANOTHER_ERROR_MESSAGE) end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 8239792c79..e6ae6603f2 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -14,13 +14,13 @@ class ValidatesWithTest < ActiveModel::TestCase class ValidatorThatAddsErrors < ActiveModel::Validator def validate(record) - record.errors[:base] << ERROR_MESSAGE + record.errors.add(:base, message: ERROR_MESSAGE) end end class OtherValidatorThatAddsErrors < ActiveModel::Validator def validate(record) - record.errors[:base] << OTHER_ERROR_MESSAGE + record.errors.add(:base, message: OTHER_ERROR_MESSAGE) end end @@ -32,14 +32,14 @@ class ValidatesWithTest < ActiveModel::TestCase class ValidatorThatValidatesOptions < ActiveModel::Validator def validate(record) if options[:field] == :first_name - record.errors[:base] << ERROR_MESSAGE + record.errors.add(:base, message: ERROR_MESSAGE) end end end class ValidatorPerEachAttribute < ActiveModel::EachValidator def validate_each(record, attribute, value) - record.errors[attribute] << "Value is #{value}" + record.errors.add(attribute, message: "Value is #{value}") end end diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 7776233db5..0b9e1b7005 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -53,7 +53,7 @@ class ValidationsTest < ActiveModel::TestCase r = Reply.new r.valid? - errors = r.errors.collect { |attr, messages| [attr.to_s, messages] } + errors = assert_deprecated { r.errors.collect { |attr, messages| [attr.to_s, messages] } } assert_includes errors, ["title", "is Empty"] assert_includes errors, ["content", "is Empty"] @@ -74,7 +74,7 @@ class ValidationsTest < ActiveModel::TestCase def test_errors_on_nested_attributes_expands_name t = Topic.new - t.errors["replies.name"] << "can't be blank" + assert_deprecated { t.errors["replies.name"] << "can't be blank" } assert_equal ["Replies name can't be blank"], t.errors.full_messages end @@ -216,7 +216,7 @@ class ValidationsTest < ActiveModel::TestCase t = Topic.new assert_predicate t, :invalid? - xml = t.errors.to_xml + xml = assert_deprecated { t.errors.to_xml } assert_match %r{<errors>}, xml assert_match %r{<error>Title can't be blank</error>}, xml assert_match %r{<error>Content can't be blank</error>}, xml @@ -241,14 +241,14 @@ class ValidationsTest < ActiveModel::TestCase t = Topic.new title: "" assert_predicate t, :invalid? - assert_equal :title, key = t.errors.keys[0] + assert_equal :title, key = assert_deprecated { t.errors.keys[0] } assert_equal "can't be blank", t.errors[key][0] assert_equal "is too short (minimum is 2 characters)", t.errors[key][1] - assert_equal :author_name, key = t.errors.keys[1] + assert_equal :author_name, key = assert_deprecated { t.errors.keys[1] } assert_equal "can't be blank", t.errors[key][0] - assert_equal :author_email_address, key = t.errors.keys[2] + assert_equal :author_email_address, key = assert_deprecated { t.errors.keys[2] } assert_equal "will never be valid", t.errors[key][0] - assert_equal :content, key = t.errors.keys[3] + assert_equal :content, key = assert_deprecated { t.errors.keys[3] } assert_equal "is too short (minimum is 2 characters)", t.errors[key][0] end diff --git a/activemodel/test/models/person_with_validator.rb b/activemodel/test/models/person_with_validator.rb index 44e78cbc29..fbb28d2a0f 100644 --- a/activemodel/test/models/person_with_validator.rb +++ b/activemodel/test/models/person_with_validator.rb @@ -5,7 +5,7 @@ class PersonWithValidator class PresenceValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - record.errors[attribute] << "Local validator#{options[:custom]}" if value.blank? + record.errors.add(attribute, message: "Local validator#{options[:custom]}") if value.blank? end end diff --git a/activemodel/test/models/reply.rb b/activemodel/test/models/reply.rb index 6bb18f95fe..f340b6fb14 100644 --- a/activemodel/test/models/reply.rb +++ b/activemodel/test/models/reply.rb @@ -11,24 +11,24 @@ class Reply < Topic validate :check_wrong_update, on: :update def check_empty_title - errors[:title] << "is Empty" unless title && title.size > 0 + errors.add(:title, "is Empty") unless title && title.size > 0 end def errors_on_empty_content - errors[:content] << "is Empty" unless content && content.size > 0 + errors.add(:content, "is Empty") unless content && content.size > 0 end def check_content_mismatch if title && content && content == "Mismatch" - errors[:title] << "is Content Mismatch" + errors.add(:title, "is Content Mismatch") end end def title_is_wrong_create - errors[:title] << "is Wrong Create" if title && title == "Wrong Create" + errors.add(:title, "is Wrong Create") if title && title == "Wrong Create" end def check_wrong_update - errors[:title] << "is Wrong Update" if title && title == "Wrong Update" + errors.add(:title, "is Wrong Update") if title && title == "Wrong Update" end end diff --git a/activemodel/test/validators/email_validator.rb b/activemodel/test/validators/email_validator.rb index 0c634d8659..774a10b2ba 100644 --- a/activemodel/test/validators/email_validator.rb +++ b/activemodel/test/validators/email_validator.rb @@ -2,7 +2,8 @@ class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) - record.errors[attribute] << (options[:message] || "is not an email") unless - /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value) + unless /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i.match?(value) + record.errors.add(attribute, message: options[:message] || "is not an email") + end end end |