How to Implement Custom ActiveRecord Validations
Don’t let bad data get into your database

Validations are important for the security and stability of our apps. We definitely don’t want to receive any and every kind of data into our database.
Thankfully, Rails provides us with a handful of active record validation helpers. However, sometimes we need more than what Rails has provided — we need to set up custom validators.
In this short piece, we are going to explore three options for implementing custom active record validations. Our example model is a simple Table
model with legs (integer)
, top (integer)
, and material (string)
attributes.
For a table
record to be valid, it must have four legs, one top, and the material must be either “wood” or “iron”. We will validate each attribute using one of the three validation options. Let’s go.
1. Custom Methods
Here, we will define a private method for the Table
class to check the value of legs
if it is present. If the value is not equal to 4, we add an error message to the record’s errors
collection.
Next, to enforce the validation, we call the validate
method and pass in a symbol of the validation method defined above.
And now, from the Rails console, we get the following results:
Table.new(legs: 4, top: 1, material: “wood”).valid? => trueTable.new(legs: 3, top: 1, material: “wood”).valid? => falseTable.new(legs: 3, top: 1, material: “wood”).errors.full_messages => [“Legs must be 4”]
Note: You may have to restart the console and hold the record in a variable to get the correct results.
2. Custom Validators (for a Record)
We can define a validator to check a record for validity based on our custom condition(s). This validator will be a class that inherits from ActiveModel::Validator.
Now, we will define a custom validator to check a table
record’s top
attribute. Remember our table must have only 1 top to be valid.
First we create the file app/models/concerns/top_validator.rb
and we define our custom validator class inside.
In our TopValidator
class, we define a validate method that takes in our record as an argument and checks the record for our validation condition. If the condition is not satisfied, we add an error message to the record’s error collection.
To enforce this validation, we call the validates_with
method in our model and pass it our validator class name (in this case, not a symbol).
And now, we get the following results from the Rails console:
Table.new(legs: 4, top:1, material: “wood”).valid?
=> trueTable.new(legs: 4, top:2, material: “wood”).valid?
=> false Table.new(legs: 4, top:2, material: “wood”).errors.full_messages
=> [“Top must be equal to 1”]
3. Custom Validators (for an Attribute)
Here, we define a validator class that inherits from ActiveModel::EachValidator
to verify if a particular attribute satisfies our custom condition(s). If not, we add an error message to the record’s error collection.
Let us now define a custom type
validator that we can use to check the material
attribute of the table in app/models/concerns/type_validator.rb
. Remember, it must be either “wood” or “string”.
Within the class, we define a validate_each
method which takes record, attribute, and value as arguments. We check the value of the attribute for our condition, if it doesn’t satisfy it, we add an error message to the record’s errors collection.
To enforce this validation, we call validates
and pass in the attribute name as a symbol and a hash of an element with a key set to the validator name and the value set to true.
And now, we have the following results:
Table.new(legs: 4, top:1, material: “wood”).valid?
=> trueTable.new(legs: 4, top:1, material: “clay”).valid?
=> falseTable.new(legs: 4, top:1, material: “clay”).errors.full_messages
=> [“Material must be either wood or iron”]
I hope you found this helpful. Thanks for your time.