Multilingual support for custom validation messages in Rails - ruby-on-rails

Hi let's say I have the following in my accounts model:
validates :name, length: {in: 1..70, message:%Q|Please enter a decent name Sr.|}
How can I add multilingual support to those custom validation messages? I checked this tutorial
But I could not find out how to translate custom validation messages in the model.

I needed once to use translations in model so I went this way:
TITLE = { 0 => :"employee.title.mrs",
1 => :"employee.title.mr",
2 => :"employee.title.miss" }
these are options for select, and in select I used t(value_of_key_here), value was a string that was seen as a key to locale.
So in your case this might work (not really sure):
validates :name, length: {in: 1..70, message: :"enter_decent_name"}
that would return a key in your validation messages and rails will just complain about missing key in translations that you have to add into your yml file:
enter_decent_name: 'Please enter a decent name Sr.'

Related

Rails: truncate text input instead of whining it's too big

I have a Rails 6.0.21 application which, like any normal application, accepts input from users.
Sometimes users type in by mistake or intentionally very long strings where not really supposed to, eg a random 9348913 character string in an input meant for email addresses which saves in a varchar(255) column.
When this happens Rails errors out:
Mysql2::Error: Data too long for column 'email'
I'm looking for a way for the framework to just truncate the data rather than whining it's too big and doesn't fit in.
I can't just truncate before_commit because some models have a lot of fields, I'd end up writing a few thousand lines of code to do this very simple thing.
Anyone know how to do it "automagically"?
You can validate the length of the attribute in your model
class User
validates :email, length: { maximum: 255 }
#validates_lenght_of :email, :maximum => 255 #rails3
end
In your view, limits the amount they can enter with the maximum length attribute:
<input maxlength="255".... >....
And if you have many fields build an array with all the attributes of type string, and if you want to customize the message you can also:
class User
ATTR_TO_VALIDATE = Todo.attribute_names.reject{|attr| Todo.columns_hash[attr].type != :string}
validates ATTR_TO_VALIDATE, length: { maximum: 255,
too_long: "%{count} characters is the maximum allowed" }
end

Rails: get values from length validator for a field in template

I havel a model with validators on some fields. Example:
class Notation < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
validates_length_of :name, minimum: 5, maximum: 128
end
is there a way to get those values from minimum and maximum for the :name field in the template (because I want to show there the value of min and max length to the user, and want to do this dynamically, with the template reflecting the values from the model)?
In the rails console, I can do something like
Notation.validators_on(:name)
which outputs
#<ActiveRecord::Validations::PresenceValidator:0x00000005428420 #attributes=[:name], #options={}>, #<ActiveRecord::Validations::UniquenessValidator:0x0000000541b8d8 #attributes=[:name], #options={:case_sensitive=>true}, #klass=Notation (call 'Notation.connection' to establish a connection)>, #<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
If I get using the array's index
Notation.validators_on(:name)[2]
I have:
#<ActiveModel::Validations::LengthValidator:0x0000000540baf0 #attributes=[:name], #options={:minimum=>5, :maximum=>128}>
but I was wondering if there is another way, maybe passing the type of validation I'm 'queryng', because I can't rely on the order of the array.
To get the validator, you can filter:
options = Notation.validators_on(:name)
.select { |v| v.is_a? LengthValidator }
.first.options
Then you can get options[:maximum] etc.
In case there is no such validator, you might have to rescue from the code, as otherwise the first will fail.

Which takes precedence: Rails type casting or validation?

I am very curious about what this expected behavior is for Rails 4.2 and I have not been able to find an answer to my question.
I'm adding validation to a model on all the time attributes. I want them to ONLY accept integers, not numerical strings. The data type in my schema for this attribute is an integer. I have my validation like so:
RANGE = 0..59
validates :start_minute, inclusion: { in: RANGE }, numericality: true
I've tried these other validations as well. I get the same result.
validates_numericality_of :start_minute, inclusion: { 0..59, only_integer: true }
validates :start_minute, inclusion: { in: 0..59 }, numericality: { only_integer: true }
When I pass my params to my controller from the request spec, start_minute is "12". BUT when I look at the created object, the start_minute is 12.
According to this article by ThoughtBot:
"This is because Active Record automatically type casts all input so that it matches the database schema. Depending on the type, this may be incredibly simple, or extremely complex."
Shouldn't the object not be able to be created? Is the typecasting taking precedence of my validation? Or is there something wrong with my validation? I appreciate any insight to this as I haven't been able to determine what is happening here. I've also created a model spec for this and I'm still able to create a new object with numerical strings.
Thank you for any insight you can give on this. I am still learning the magic of Rails under the hood.
From the rails docs it says,
If you set :only_integer to true, then it will use the
/\A[+-]?\d+\z/
What it(only_integer validator) does is that it validates that the format of value matches the regex above and a string value that contains only numbers like '12' is a match(returns a truthy value which is 0 and passes the validation).
2.3.1 :001 > '12' =~ /\A[+-]?\d+\z/
=> 0

How do I put a custom validation message for a "greater_than" property from my model?

I'm using Rails 5. In my model, I have this validation rule
validates :price, :numericality => { :greater_than => 0 }
for one of my fields. I want to create a custom validation error message but this isn't displaying for my ./config/locales/en.yml file
en:
activerecord:
errors:
models:
my_record:
attributes:
...
price:
greater_than: "Please etner a valid number for price."
When I try and load my app, I get the error below
can not load translations from /Users/davea/Documents/workspace/cindex/config/locales/en.yml: #<Psych::SyntaxError: (/Users/davea/Documents/workspace/cindex/config/locales/en.yml): found character that cannot start any token while scanning for the next token at line 30 column 1>
What's the right way to set up the custom error message in my locales file?
Definitely a YAML parsing issue caused by bad formatting or syntax. Check that you don't have any single quotes anywhere and your indentation is correct at line 29, 30 and 31

Rails 4. Country validation in model

I'm creating rails API and want to want to add validation for countries field which contains an ISO 3166-1 code on model level.
For example if use gem carmen-rails, it provides only helper country_select. Is that way to use validation for accordance country for ISO 3166-1 code in the model?
Here is the newest syntax for validation with the countries gem:
validates :country, inclusion: { in: ISO3166::Country.all.map(&:alpha2) }
You are just trying to validate that the country code entered is appropriate? this should work with carmen
validates :country, inclusion: { in: Carmen::Country.all.map(&:code) }
But if this is all you need seems like the countries gem might work well too. With countries you could do
validates :country, inclusion: { in: Country.all.map(&:pop) }
Or
validate :country_is_iso_compliant
def country_is_iso_compliant
errors.add(:country, "must be 2 characters (ISO 3166-1).") unless Country[country]
end
Update
For Region and State you can validate all 3 at the same time like this.
validates :country, :region, :state, presence: true
validate :location
def location
current_country = Country[country]
if current_country
#valid regions would be something Like "Europe" or "Americas" or "Africa" etc.
errors.add(:region, "incorrect region for country #{current_country.name}.") unless current_country.region == region
#this will work for short codes like "CA" or "01" etc.
#for named states use current_country.states.map{ |k,v| v["name"}.include?(state)
#which would work for "California" Or "Lusaka"(it's in Zambia learn something new every day)
errors.add(:state, "incorrect state for country #{current_country.name}.") unless current_country.states.keys.include?(state)
else
errors.add(:country, "must be a 2 character country representation (ISO 3166-1).")
end
end
Although Region seems unnecessary as you could imply this from the country like
before_validation {|record| record.region = Country[country].region if Country[country]}
Create a Fixture with the data provided by Wikipedia on ISO-3166-1 and validate the country based on that data.
Also you can create an auto-complete feature easing the input. You can look at the auto-complete provided here for guidance.

Resources