How to use ActiveModel::EachValidator in rails 5 - ruby-on-rails

How to validate specific attribute using ActiveModel::EachValidator.
I have written the below snippet of code. This validation will not call on saving or validating object.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record,attribute,value)
# Logic to check email is valid or not
end
end
This will work with rails 3.

You can put your EmailValidator class inside the models/concerns directory. Then inside your model you can validate the email attribute using the example below.
class User < ApplicationRecord
validates :email, presence: true, email: true
end
Rails will look for the EmailValidatorclass within the scope when it encounters email: true then validate the attribute using the validate_each method.

A simple base class that can be used along with ActiveModel::Validations::ClassMethods#validates_with
class User
include ActiveModel::Validations
validates_with EmailValidator
end
class EmailValidator < ActiveModel::Validator
def validate(record)
# Logic to check email is valid or not
record.errors.add :email, "This is some complex validation"
end
end
Any class that inherits from ActiveModel::Validator must implement a method called validate which accepts a record.
To cause a validation error, you must add to the record's errors directly from within the validators message.
For more details you can check here.
If you are looking for only email validation then you can try this.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
unless value =~ /^([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
record.errors.add(attr_name, :email, options.merge(:value => value))
end
end
end

Related

Mix create and update validations in single custom validator using ActiveModel::Validator

I found two alternatives to make use of the ActiveModel::Validator helper, but I am failing to find an easier or clean(er) approach than either checking for the presence of record.id or creating multiple custom validators.
I have a User class as follows:
class User < ApplicationRecord
include ActiveModel::Validations
validates_with UserValidator
...
end
and a custom validator
class UserValidator < ActiveModel::Validator
def validate(record)
# validate some stuff
end
end
The logic inside the validate method is executed no matter the action from the controller. So on create or on update (or save).
My goal is to split the logic and I have found multiple approaches that work but I don't really like either of them.
Approach 1: Check if a record.id exists
class UserValidator < ActiveModel::Validator
def validate(record)
if record.id.nil?
# `on: :create` validations
else
# `on: :update` validations
end
end
end
Approach 2: Create multiple validator classes
class User < ApplicationRecord
include ActiveModel::Validations
validates_with UserCreateValidator, on: :create
validates_with UserUpdateValidator, on: :update
...
end
Preferably I would only have a single class UserValidator and within that custom validator check for some attribute or pass an option that would allow me to distinguish between create or update. Is that somehow achievable?
class UserValidator < ActiveModel::Validator
def validate(record)
if record.new_record?
# `on: :create` validations
else
# `on: :update` validations
end
end
end
See ActiveRecord::Persistence.

Custom validation in Rails on different model

How can I use custom validation between two different models in Ruby on Rails?
I have two models how can I validate columns between these two models.
The cleanest way is to create separate validator class instead of just a custom validation method. A validator is really just a subclass of ActiveModel::Validator or ActiveModel::EachValidator that implements #validate or #validate_each:
class TitleCaseValidator < ActiveModel::Validator
def validate(record)
unless record.name[0] == record.name[0].upcase
record.errors[:title] << 'must be title cased'
end
end
end
class Album
validates_with TitleCaseValidator
end
class Book
validates_with TitleCaseValidator
end
class TitleCaseValidator < ActiveModel::EachValidator
def validate(record, attribute, value)
unless value[0] == value[0].upcase
record.errors[attribute] << 'must be title cased'
end
end
end
class Album
validates :title, title_case: true
end
class Artist
validates :name, title_case: true
end
See Performing Custom Validations.

Rails - Externalise Validators

In my ruby classes I noticed that there are several validators that are repeated such as the validator that checks for the presence of Id...
I would like to gather these validators in a external class/module/file and then do something like
class User
include Mongoid::Document
...
validates_with :MyCustomExternalValidator
Is this possible?
Details:
-Latest Ruby
-RSPEC for testing
Yes, it is. From guides.rubyonrails.org:
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.starts_with? 'X'
record.errors[:name] << 'Need a name starting with X please!'
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end

Refactoring custom validation for multiple fields in model

I have a model with a custom validation function which verifies that the date is not in the past. Currently, the validation is hard-coded to check a single field, selected_date, in the model. How do I go about refactoring the validation so that I can either pass a parameter to the custom validation so I can test 2 fields?
class Appointment < ActiveRecord::Base
attr_accessible :selected_date, :alternate_date
validates_presence_of :selected_date
validate :validate_date
def validate_date
if selected_date < Date.today
errors.add(:selected_date, 'Date has passed')
end
end
end
create file lib/future_validator.rb:
class FutureValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
if value < Date.today
object.errors[attribute] << "has passed"
end
end
end
In your models:
validates :selected_date, presence: true, future: true
validates :other_date, presence: true, future: true
See this RailsCast: Validations in Rails 3
NOTE: Make sure lib files are auto loaded in config/application.rb and restart the server after adding that file.

Adding custom validations to ActiveRecord module via extend?

I'm trying to move my validations to a module. I want to extend an existing object an aribtrary set of validators, but I'm struggling to figure out how to get them to execute. Any ideas?
Active Record Object
class Test < ActiveRecord::Base
has_many :resources
end
Validator
module TestValidator
extend ActiveSupport::Concern
included do
validates_associated :resources
end
end
Console
t = Test.new
t.extend TestValidator
t.valid?
# true ... should be false
I hope this can help
6.1 Custom Validators
Custom validators are classes that extend ActiveModel::Validator. These classes must implement a validate method which takes a record as an argument and performs the validation on it. The custom validator is called using the validates_with method.
class MyValidator < ActiveModel::Validator
def validate(record)
unless record.name.starts_with? 'X'
record.errors[:name] << 'Need a name starting with X please!'
end
end
end
class Person
include ActiveModel::Validations
validates_with MyValidator
end
The easiest way to add custom validators for validating individual attributes is with the convenient ActiveModel::EachValidator. In this case, the custom validator class must implement a validate_each method which takes three arguments: record, attribute and value which correspond to the instance, the attribute to be validated and the value of the attribute in the passed instance.
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
record.errors[attribute] << (options[:message] || "is not an email")
end
end
end
class Person < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
As shown in the example, you can also combine standard validations with your own custom validators.
https://guides.rubyonrails.org/active_record_validations.html#custom-validators

Resources