Building custom validator

Ruby on Rails / Validations

Sometimes built in validations are not enough so we have to write our own validation rules. Because of this, our models can become fat really quick so the best idea to avoid it is to extract validation logic to separated classes. Such approach makes the code more clean, isolated and reusable.

Model validations

To demonstrate the process of isolating validation code let’s build a sample model class and put there simple validation rule:

class User < ActiveRecord::Base
  PHONE_REGEX = /some_regex/

  validates :phone, format: { with: PHONE_REGEX, message: 'invalid phone number' }
end

It’s a nice and clean validation rule, however, we can’t reuse it elsewhere. This is the moment when we should consider building validator class. Let’s take a look at the official documentation and find out what they said about it:

The easiest way to add custom validators for validating individual attributes is with the convenient ActiveModel::EachValidator

Custom validator rules

Rules for building custom validator are following:

  1. If you want your validator to be automatically loaded you have to use NameValidator name pattern where Name is your validator name and the suffix Validator helps Rails to detect if given class is a validator.
  2. Your class should inherit from ActiveModel::EachValidator class
  3. You have to implement validate_each method

Custom validator

Let’s stick to the mentioned rules and build our first custom validator:

class PhoneFormatValidator < ActiveModel::EachValidator
  PHONE_REGEX = /some_regex/

  def validate_each(record, attribute, value)
    if value.match(PHONE_REGEX).blank?
      record.errors.add(attribute, 'invalid phone number')
    end
  end
end

Using custom validator in a Rails model

It’s time to replace our current logic with the custom validator that we build in a previous step. All you have to do is to use the name of your validator:

class User < ActiveRecord::Base
  validates :phone, phone_format: true
end

Don’t forget to write tests for you validator class!