Better Programming

Advice for programmers.

Follow publication

How to Implement Custom ActiveRecord Validations

Don’t let bad data get into your database

Moses Ogwo
Better Programming
Published in
3 min readFeb 4, 2020

--

Photo by Scott Webb on Unsplash

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?
=> true
Table.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?
=> true
Table.new(legs: 4, top:1, material: “clay”).valid?
=> false
Table.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.

Sign up to discover human stories that deepen your understanding of the world.

--

--

Moses Ogwo
Moses Ogwo

Written by Moses Ogwo

Software Engineer at Peoplebox

Responses (1)

Write a response