Build your own Rails custom validatorsAxel Vergult, August 27th, 2012 | Rails, Ruby
Validating your data through Rails models is essential. It enables you to ensure consistency throughout your data structures. You may also want to validate data on the client and/or database side. But even still, keeping your validation in your Rails models is a good practice because you can create real meaningful states for your Ruby objects that are independent of other levels.
Rails provides built in validators like Presence, Confirmation, Numericality, Acceptance, etc.. which you should already be familiar with.
But you can also build custom validators if the existing ones don’t satisfy your requirements. I’ll guide you through three different ways to create custom validators for your models, but before we get started, let me introduce you to the module defining these validators: ActiveModel.
What is ActiveModel?
ActiveModel gathers all related model behaviors except its database layer (kept in ActiveRecord). By including ActiveModel, you give a class all of Rails model super powers such as translations, errors, callbacks, dirty states, observer support, serialization… and also validations!
Validate object states directly in your model with validates_each
The simplest way to create a custom validator is to use
This method validates each attribute against a block.
Suppose you want to create a representation of an existing music album. Since such an album has already been released, you don’t want your album data structure to accept a future context date as a released date.
You could validate the released date by defining a Rails 2.x style class method
validates_against_future which will use
validates_each to apply validation to all passed attributes, like in this example:
Validate object single states with an isolated custom validator
When you only want to validate single states of an object and you know you will reuse this validation elsewhere, extracting it into its own class is the most modular approach. You will also be able to use it as an ordinary
validates option in your models.
In order to achieve this, you must inherit from
ActiveModel::EachValidator and implement the
validate_each method (be careful to use “validate_each” and not “validates_each” this time).
Based on the previous example, let’s re-write the
validates_against_future validation for our existing album representation. We will call this validator
prevent_future_date: true option passed to
validates will cause ActiveModel to search for a class named
'prevent_future_date'.camelcase + Validator (i.e. PreventFutureDateValidator) and call its
validate_each method on the attribute.
Validate entire object with an isolated custom validator
A custom validator also allows you to validate an entire record instead of just a record’s attributes. To do this, you will have to create a separate class that inherits from
ActiveModel::Validator and implements the
validate method. To apply the validation, simply call
validates_with in your model and pass the custom validator as the argument.
Here at Official.fm, we use custom validators for the benefits listed above. We usually prefer the second approach for creating them as we want to keep validations as record independent as possible.
But we took the modularity a step further by making some of our custom validations “project free” by packaging and releasing them as a gem.
This gem is called valid8ors and you can check it out on GitHub or just install it via a simple
gem install valid8ors command.
Creating custom validators with Rails is pretty straightforward once you know how they work. By providing different ways to build them depending on your needs, custom validators make your code DRYer and more modular, and thus more maintainable and readable.
So take some time and look back at your code and ask yourself if you can extract some of your custom validations from your models.