When we are creating a new task with a blank title, the application will create
a new task with no title. We don't want the title to be blank. We want the title
field to be filled before a task is created.
Making title field a required field
We can edit the Task
model to add a validation.
Navigate to app/models/task.rb
and add the following code for validating the
title:
Verifying validation
Before saving a record to the database, Rails runs validations on these Active
Record objects. If these validations produce any errors, Rails will not save the
object.
Let's fire up Rails console and let's create an instance of Task
model using
the new
method. We are going to use reload!
in console for the folks who
already had the console open:
Now we have two instances t1 and t2. t1 has a title. t2 has no title.
We can run validations using the built-in method valid?
. Invoking valid?
on
an Active Record object runs validations and returns true
if there are no
errors and returns false
if the record has any error:
valid?
returns true
for t1
and false
for t2
.
We can check for the errors associated with object t2
using the
errors.messages
method:
As we can see the object t2
is not valid because we did not provide any title
and when we check the error messages we can see the reason for it.
Caveats with validations at the Rails level
Let's execute a raw sql query and see what happens when we don't pass any value
for title
field. Here we are directly trying to add a record in the database
and we are bypassing Active Record model.
We are able to create a record without title. What it means is that just the
validation in the model is not enough. We also need to make title not nullable
at the database level.
Let's add title to all the existing tasks. If there are records with null values
for title then we can't make title not nullable at the database level:
Adding migration to make title field NOT NULL
Let's add a constraint at the database level to not to allow null
values for
title field. Let's generate a migration to make this change:
Now let's open this migration file and add the code to make title
not
nullable:
Run the command rails db:migrate
to apply the migration:
Open the file db/schema.rb
under the db
folder. We can see that now "title"
field has null: false
statement.
Verifying validation using console
Now fire up the console again using rails c
. Let's try creating a new task the
same way we created before by inserting into the database directly:
As expected, it throws a violation NotNullViolation
which means our title
can't be nil.
Adding length validation to title field
Similarly, we can also add length validation to the title
field. To add length
validation we can use length validator method:
This validation ensures that the length of the title would not exceed 125
characters.
We should always use constants to store values such as maximum lengths and use
the constant in our code instead of hardcoding the value each time. This is
because, if we need to update the maximum length for an attribute, we will only
need to update the value stored in the constant instead of updating the value
throughout the codebase.
Let's add a constant MAX_TITLE_LENGTH
for the maximum length of the task title
in our Task model. We could define the constant in a global file like
constants.rb
but as a convention, we want to keep all entities, like say
variables, methods, constants etc limited to the smallest possible scope. Since
we will only be using MAX_TITLE_LENGTH
in the context of our Task model we can
define it under the scope of our Task class.
This also let's us define the constant as MAX_TITLE_LENGTH
and not as
MAX_TASK_TITLE_LENGTH
since the constant will be available under the Task
namespace avoiding confusion. The general idea of naming it without again adding
the keyword TASK
is to avoid redundancy and keep naming clean.
Update the following line of code in TaskModel
:
Adding format validation to title field
To ensure that a task's title has at least one alphanumeric character, you can use the format
validator method in Ruby on Rails. Here's how you can add this validation to the title field in the Task model:
In the code above, the format
validator is used to enforce the presence of at least one alphanumeric character in the title field. .*
matches any number of characters including alphanumeric, whitespace, and punctuation. [a-zA-Z0-9]
matches any alphanumeric character. Together, .*[a-zA-Z0-9].*
matches strings with at least one alphanumeric character and the with
option help us to specify this regular expression.
Let's commit these changes: