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

Want to receive useful tips, information about new Ruby gems and articles on a daily basis? Make sure you follow me and say hello!

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!

Want to become a better Rails developer?
Download for free the Introduction Rails patterns book and dive into the world of refactoring and easy-testable Ruby code today.
Join over 1,000 developers already subscribed to my newsletter and download the book. You can unsubscribe anytime:

Subscribe and get the book!