Let's say I have a before_validation that checks to make sure the first letter of a name is "y":
before_validation :check_name_first_letter_is_y
But I want to make sure that the first name is also present:
validates :name, presence: true
But this will run the before_validation BEFORE validating if there's a first name present, right? How would I check if a name is present before running my before_validate?
I could try:
after_validation :check_name_first_letter_is_y
But will that stop the save method if I return false? Or is it too late because it's already been validated?
It might be easier to do this in one call as follows (if I didn't misread your question!):
validates :name, presence: ->(rec) { rec.name.initial == 'Y' }
Update:
Introduce a new ActiveModel::EachValidator that checks the first character in the name to be y and use the presence validator as you normally would, but ensure presence validator comes before the name validator so that presence check is done before checking first letter.
# app/validators/NameValidator.rb
class NameValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value.initial != 'y'
record.errors[attribute] << (options[:message] || "First letter in name must be `y`")
end
end
end
Then in your model, use the following two validations:
validates :name, presence: true
valdiates :name, name: true
Please refer: http://api.rubyonrails.org/classes/ActiveModel/Validator.html.
Also would suggest you to come up with a "Railsy" name for NameValidator class!
You can simply add a second format validation:
validates :name, presence: true, format: { with: /^y/, allow_blank: true }
Related
I've some Active Record validations on my model:
class Product < ApplicationRecord
validates :name, presence: true, length: { is: 10 }
end
That seems fine. It validates that the field name is not nil, "" and that it must have exactly 10 characters. Now, if I want to add a custom validation, I'd add the validate call:
class Product < ApplicationRecord
validates :name, presence: true, length: { is: 10 }
validate :name_must_start_with_abc
private
def name_must_start_with_abc
unless name.start_with?('abc')
self.errors['name'] << 'must start with "abc"'
end
end
end
The problem is: when the name field is nil, the presence_of validation will catch it, but won't stop it from validating using the custom method, name_must_start_with_abc, raising a NoMethodError, as name is nil.
To overcome that, I'd have to add a nil-check on the name_must_start_with_abc method.
def name_must_start_with_abc
return if name.nil?
unless name.start_with?('abc')
self.errors['name'] << 'must start with "abc"'
end
end
That's what I don't wan't to do, because if I add more "dependant" validations, I'd have to re-validate it on each custom validation method.
How to handle dependant validations on Rails? Is there a way to prevent a custom validation to be called if the other validations haven't passed?
I think there is no perfect solution unless you write all your validations as custom methods. Approach I use often:
class Product < ApplicationRecord
validates :name, presence: true, length: { is: 10 }
validate :name_custom_validator
private
def name_custom_validator
return if errors.include?(:name)
# validation code
end
end
This way you can add as many validations to :name and if any of them fails your custom validator won't execute. But the problem with this code is that your custom validation method must be last.
class Product < ApplicationRecord
validates :name, presence: true, length: { is: 10 }
validate :name_must_start_with_abc, unless: Proc.new { name.nil? }
private
def name_must_start_with_abc
unless name.start_with?('abc')
self.errors['name'] << 'must start with "abc"'
end
end
end
Please check allow_blank, :allow_nil and conditional validation as well for more options.
In my model, I would like to add a validation to check if my attribute presents both of its values (e.g., one of the record has its value ValueA and another record has its value ValueB-those being the only possibilities, and it needs to have at least one or both of them).
What is the best way to achieve this?
Try out this
class YourModel < ActiveRecord::Base
VALID_VALUES = ['Value1', 'Value2']
with_options presence: true do
validates :your_field, inclusion: { in: VALID_VALUES, allow_blank: true }
end
end
I've found plenty of posts around how to validate a field is present, if another condition is true, such as these:
Rails: How to validate format only if value is present?
Rails - Validation :if one condition is true
However, how do I do it the opposite way around?
My User has an attribute called terms_of_service.
How do I best write a validation that checks that the terms_of_service == true, if present?
You're looking for the acceptance validation.
You can either use it like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: true
end
or with further options, like this:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { message: 'must be abided' }
end
[edit]
You can set the options you expect the field to be as well, as a single item or an array. So if you store the field inside a hidden attribute, you can check that it is still "accepted" however you describe accepted:
class Person < ApplicationRecord
validates :terms_of_service, acceptance: { accept: ['yes', 'TRUE'] }
end
I can't think of any default validation methods that could serve your purpose, but you can do with a custom validation. Also, a boolean can be either truthy or falsy, so you just need to check if its true or not. something like this should work.
validate :terms_of_service_value
def terms_of_service_value
if terms_of_service != true
errors.add(:terms_of_service, "Should be selected/True")
end
end
I am using the Rails Admin gem. When I add a new activity type and create it again with the same name, it validates that name is already taken. But whenever I try to edit one it will give you an error: "name can't be blank"
For example, I created Swimming, and I tried to add a new activity type which is swimming/SWIMMING etc. To avoid this I used the before_validation callback, to make the first letter a capital, then check the uniqueness of name.
Yes, it's working but whenever I try to edit the name field it will become blank after I submit it.
NOTE: I also tried to use validates :name, presence: true, :uniqueness => {:case_sensitive => true} only without the before_validation but it didn't work.
Activity Type
class ActivityType < ApplicationRecord
before_destroy :ensure_has_no_activity_type
before_validation :capitalize_first_letter_name
has_many :activities
validates :name, presence: true,:uniqueness => {:case_sensitive => true}, length: { maximum: 20 },format: Utilities::RegexValidations.alphanumeric_underscore
validates :description, presence: false
private
def ensure_has_no_activity_type
if activities.present?
errors.add(:base, 'Cannot delete activity type that has activity')
throw(:abort)
end
end
def capitalize_first_letter_name
# Capitalize the first letter and the rest will be small letter
self.name = self.name.capitalize!
end
end
Question: Why whenever I tried to edit and try to submit it, does the name field become blank? What is the reason for this?
The problem arises from capitalize_first_letter_name. "".capitalize! will return nil. If you change it to "".capitalize that will return blank string as expected.
Moreover, capitalize! will return nil if no changes were made. See https://ruby-doc.org/core-2.2.0/String.html#method-i-capitalize-21.
I want to validate that a user name has no white/blank spaces for my Users. Is there a built in validation that does this? Or what is the best way to do this. Seems like it would be a pretty common requirement.
I would try format validator:
validates :username, format: { with: /\A[a-zA-Z0-9]+\Z/ }
as most of the time when you don't want whitespaces in username you also don't want other characters.
Or when you really only need to check for whitespace, use without instead:
validates :username, format: { without: /\s/ }
Full documentation: http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_format_of (validates ... format: {} is the same as validates_format_of ...)
I believe you will have to create a custom validator:
validate :check_empty_space
def check_empty_space
if self.attribute.match(/\s+/)
errors.add(:attribute, "No empty spaces please :(")
end
end
In your User model add validation. validates :username, format: { without: /\s/ } will remove white/blank spaces for your Users. You can even add a message alerting the user that their username contains whitespace.
class User < ActiveRecord::Base
validates :username, format: { without: /\s/, message: "must contain no spaces" }
end
You can use before_validation callback to strip whitespace
class User
before_validation :strip_blanks
protected
def strip_blanks
self.username = self.username.strip
end
end
MurifoX's answer is better, but regarding it being a common requirement, i think this is more often used:
validates :presence
class User < ActiveRecord::Base
validates :name, presence: true
end