Validation
Have you ever wondered how come your favourite application knew your password is weak or worse, your username is already taken?!
If you have seen those annoying notifications saying you need such and such for your password to qualify, then you have been a victim of validation.
Definition: Validation
To establish the soundness, accuracy, or legitimacy of something.
Record validation in ruby is a mechanism for checking the accuracy of data in your ruby API or application.
Validation is a necessary and extremely significant step to integrate into any meaningful application.
The repercussions of invalid data are far-reaching with devastating blow-ups in business processes, survival, continuity, etc...
Building any thorough validation system is cumbersome and requires one to think out of the box if not at least playing "user" of their application and emulate how extremely a user might tinker with it.
The good news is, in the world of Ruby and Ruby on Rails, validations can be achieved without breaking much of a sweat.
In this article we are going to look at how you can use valid?
and invalid?
to perform validations in your RoR application.
In Rails, whenever create, create!, save, save!, update, or update!
is invoked, validations are triggered. These methods will only save data to the database if the data provided is valid.
It is important to note that validations by default run before the database commands associated with the methods mentioned are sent to the database.
For instance, create
which corresponds to SQL's INSERT
command will invoke validations on an entity before performing the actual INSERT
operation.
In the following steps, I am assuming you have set up your ruby environment and installed the necessary gems for a rails application.
In your terminal run:
rails new validation --api --minimal
The command above will create a new rails project. On successful completion:
Move to the project directory:
cd validations
Open the directory in vs-code:
code .
If everything worked out well, you should have a folder structure similar to this:
Let us build a Person resource and use it to demonstrate how validations work in rails.
Still on the terminal in the same project folder run:
rails g resource Person name email phone
This command will generate:
- Person model
- Person controller
- routes
- Run migrations
- Other files are not necessary for this tutorial
Go to ```
app/models/person.rb
```ruby
# app/models/person.rb
class Person < ApplicationRecord
end
As you can see, our model does not have any code yet.
On the terminal, let us run: rails c
to start an interactive rails console
Let us create a Person p
by running the following command
p = Person.create
On the terminal, you should see this:
This command ran successfully and we have created one Person!
Let us have a look at the person by running p
But wait... why is it that the person does not have not any data i.e everything except the id
is nil
?
Well, running the Person.create
triggered an INSERT
command which initiated the insert process on our database.
Since we did not have any checks in place to validate our data, the "useless" record was deemed valid and thus accepted and saved without raising any errors or exceptions.
I will leave it to your imagination what will happen if you pull this data and try to display it on your front-end app.
Now let us add some code to our models to constrain the data we receive and permit:
# app/models/person.rb
class Person < ApplicationRecord
validates :username, presence: true # no blank
validates :email, presence: true, uniqueness: true # no blank, no duplicates
validates :phone, length: {is: 10} # must be EXACTLY 10 characters long
end
In the above snippet, we are adding validation to ensure:
- The entity data must contain a username, not blank
- It must have an email and the email must be unique
- The phone number must be exactly 10 digits no more no less
Reload the terminal by running reload!
Try running the same command i.e Person.create
again:
As you can see, this process did not run to completion successfully. This can easily be confirmed by that read error that says rollback transaction
which means the entity was not persisted to the database.
Why?
Well, our guards and the validations ensured that we do not allow useless data in our application.
This is what went down:
- Run the
Peron.create
method - Validations are invoked
- Check if name is valid =>
false
- Check if email is valid =>
false
- Check if phone is valid =>
false
- Verdict: Invalid object. Do not save!
This means the INSERT
command was never invoked.
How did rails know this?
Well, under the hood, every entity has an errors
object associated with it.
If the object has any errors, the implication is that that entity is invalid. Otherwise, it is valid.
Let us check out this errors
object.
Still, on the terminal, let us run p.errors
We get the shape of the object but it is not quite readable
Run:
p.errors.full_messages
In the screenshot above, we can see the errors that depict validations the entity has violated.
We can argue that since the errors
object not empty, the entity is invalid and thus cannot be permited to persist to the database.
valid? and invalid?
As we have learned, validation will be triggered automatically whenever any of the following methods are invoked:
- create
- create!
- save
- save!
- update
- update!
However, we sometimes wish to manually run the validations.
To achieve this, valid?
and invalid?
are our go-to methods.
valid?
- This method returns
true
if an entity is valid and false otherwise.
invalid?
- This method returns
false
if an entity is valid and false otherwise.
Both of these methods perform a check on the errors
object associated with the entity it was invoked on. If the object has any errors, the valid?
return true
.
The valid?
method returns false
to indicate that the entity is invalid and thus cannot be saved to the database. The opposite is true.
How to use valid?
and invalid?
Going back to our Person controller, we can add corresponding logic as follows:
class PeopleController < ApplicationController
def create
# create person
person = Person.create(username: params[:username], email: params[:email], phone: params[:phone])
if person.valid? # if no errors in errors object
# return user created to the client
render json: person, status: :created
else
# otherwise let them know what went wrong
render json: {errors: person.errors.full_messages}, status: :unprocessable_entity
end
end
end
Let us give it a test drive using Postman
Start by running rails s
or rails server
on the terminal
Copy the link http://127.0.0.1:3000
and paste it on Postman address and a /persons
and ensure that your Postman interface looks as shown below:
Here is the sample data I am using:
{
"username": "hermitex",
"email": "hermitex@gmail.com",
"phone": "0765568854"
}
Click on Send
Here is the response:
Let us try to POST an invalid person
{
"username": "",
"email": "hermitex@gmail.com",
"phone": "0765568854"
}
Notice that we have intentionally sent ""
value for username
and
repeated the email
By definition, this entity is invalid since our validation logic does not allow duplicate emails and the username must not be blank. Remember?
Let us see what we get.
There we go!
We have not only successfully prevented an invalid INSERT
operation but also responded with appropriate messages and statuses to let the user know what might have gone wrong with their request.
Summary
- Validation is a way of ensuring data accuracy and integrity
- In rails validation can be performed using helper methods like
validates
- The following active records methods trigger validations:
- create
- create!
- save
- save!
- update
- update!
- To trigger validations manually, we can use
- valid? or
- invalid?
- Rails knows whether an entity is valid or not by checking the errors object associated with each entity
- Presence of an error in the errors object signifies that the object is invalid and valid otherwise
Well, validations can be customized to become complex depending on the use case[s]
Learn more about how this can be done here
Top comments (1)
Awesome article...well structured.