aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/CHANGELOG.md25
-rw-r--r--activemodel/lib/active_model/errors.rb7
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb59
3 files changed, 76 insertions, 15 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md
index 0b2fa37787..6048911217 100644
--- a/activemodel/CHANGELOG.md
+++ b/activemodel/CHANGELOG.md
@@ -6,17 +6,17 @@
* Fix `ActiveModel::Serializers::JSON#as_json` method for timestamps.
- Before:
- ```
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
- contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
- ```
-
- After:
- ```
- contact = Contact.new(created_at: Time.utc(2006, 8, 1))
- contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
- ```
+ Before:
+ ```
+ contact = Contact.new(created_at: Time.utc(2006, 8, 1))
+ contact.as_json["created_at"] # => 2006-08-01 00:00:00 UTC
+ ```
+
+ After:
+ ```
+ contact = Contact.new(created_at: Time.utc(2006, 8, 1))
+ contact.as_json["created_at"] # => "2006-08-01T00:00:00.000Z"
+ ```
*Bogdan Gusiev*
@@ -25,6 +25,7 @@
change. There is a new method `#authenticate_XXX` where XXX is the
configured attribute name, making the existing `#authenticate` now an
alias for this when the attribute is the default 'password'.
+
Example:
class User < ActiveRecord::Base
@@ -36,7 +37,7 @@
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
user.authenticate_recovery_password('42password') # => user
- *Unathi Chonco*
+ *Unathi Chonco*
* Add `config.active_model.i18n_full_message` in order to control whether
the `full_message` error format can be overridden at the attribute or model
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index edc30ee64d..af94d52d45 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -379,9 +379,11 @@ module ActiveModel
# * <tt>errors.format</tt>
def full_message(attribute, message)
return message if attribute == :base
+ attribute = attribute.to_s
if self.class.i18n_full_message && @base.class.respond_to?(:i18n_scope)
- parts = attribute.to_s.split(".")
+ attribute = attribute.remove(/\[\d\]/)
+ parts = attribute.split(".")
attribute_name = parts.pop
namespace = parts.join("/") unless parts.empty?
attributes_scope = "#{@base.class.i18n_scope}.errors.models"
@@ -410,8 +412,9 @@ module ActiveModel
defaults << :"errors.format"
defaults << "%{attribute} %{message}"
- attr_name = attribute.to_s.tr(".", "_").humanize
+ attr_name = attribute.tr(".", "_").humanize
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
+
I18n.t(defaults.shift,
default: defaults,
attribute: attr_name,
diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb
index 1c62e750de..ccb565c5bd 100644
--- a/activemodel/test/cases/validations/i18n_validation_test.rb
+++ b/activemodel/test/cases/validations/i18n_validation_test.rb
@@ -35,7 +35,7 @@ class I18nValidationTest < ActiveModel::TestCase
def test_errors_full_messages_translates_human_attribute_name_for_model_attributes
@person.errors.add(:name, "not found")
- assert_called_with(Person, :human_attribute_name, [:name, default: "Name"], returns: "Person's name") do
+ assert_called_with(Person, :human_attribute_name, ["name", default: "Name"], returns: "Person's name") do
assert_equal ["Person's name not found"], @person.errors.full_messages
end
end
@@ -101,6 +101,63 @@ class I18nValidationTest < ActiveModel::TestCase
assert_equal "cannot be blank", person.errors.full_message(:'contacts/addresses.country', "cannot be blank")
end
+ def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_attributes_format
+ ActiveModel::Errors.i18n_full_message = true
+
+ I18n.backend.store_translations("en", activemodel: {
+ errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
+
+ person = Person.new
+ assert_equal "cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].street', "cannot be blank")
+ assert_equal "Contacts/addresses country cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].country', "cannot be blank")
+ end
+
+ def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_model_format
+ ActiveModel::Errors.i18n_full_message = true
+
+ I18n.backend.store_translations("en", activemodel: {
+ errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } })
+
+ person = Person.new
+ assert_equal "cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].street', "cannot be blank")
+ assert_equal "cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].country', "cannot be blank")
+ end
+
+ def test_errors_full_messages_with_indexed_deeply_nested_attributes_and_i18n_attribute_name
+ ActiveModel::Errors.i18n_full_message = true
+
+ I18n.backend.store_translations("en", activemodel: {
+ attributes: { 'person/contacts/addresses': { country: "Country" } }
+ })
+
+ person = Person.new
+ assert_equal "Contacts/addresses street cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].street', "cannot be blank")
+ assert_equal "Country cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].country', "cannot be blank")
+ end
+
+ def test_errors_full_messages_with_indexed_deeply_nested_attributes_without_i18n_config
+ ActiveModel::Errors.i18n_full_message = false
+
+ I18n.backend.store_translations("en", activemodel: {
+ errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } })
+
+ person = Person.new
+ assert_equal "Contacts[0]/addresses[0] street cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].street', "cannot be blank")
+ assert_equal "Contacts[0]/addresses[0] country cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].country', "cannot be blank")
+ end
+
+ def test_errors_full_messages_with_i18n_attribute_name_without_i18n_config
+ ActiveModel::Errors.i18n_full_message = false
+
+ I18n.backend.store_translations("en", activemodel: {
+ attributes: { 'person/contacts[0]/addresses[0]': { country: "Country" } }
+ })
+
+ person = Person.new
+ assert_equal "Contacts[0]/addresses[0] street cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].street', "cannot be blank")
+ assert_equal "Country cannot be blank", person.errors.full_message(:'contacts[0]/addresses[0].country', "cannot be blank")
+ end
+
# ActiveModel::Validations
# A set of common cases for ActiveModel::Validations message generation that