Active Record Validations

Haleigh Dalke
The Startup
Published in
5 min readJul 6, 2020
Photo by Kai Pilger on Unsplash

When I was first learning Ruby, I used a variety of if/else statements to “validate” user input and prevent breakage in my CLI applications. I’m sure you can understand this was not the most efficient way to go about safeguarding your code — though it was prior to learning the ultra-powerful, time and energy-saving, omniscient Active Record. In this post, I’m going to outline the importance of using validations and some of my most used Active Record Validations helpers.

Why Validations?

Validations are used to ensure only valid data is persisted to your database. For example, you’re helping Joe Biden’s 2020 presidential campaign and want to collect the names and phone numbers of all Trump supporters you know. You want to send an automated voice recording that outlines the Trump administration’s obscene failures in handling the COVID-19 pandemic (they deserve to know). Little did you know, Putin found out about your mission and started typing alphabetic characters in your phone number input field. CRAP! Your program breaks because 408-RUSSIA-IS-AWESOME is not a valid phone number.

Photo by Jørgen Håland on Unsplash

It’s worth noting: there are many ways to validate data in a Ruby program, but according to Ruby documentation, model-level validations are the most appropriate in most circumstances.

As you may know, saving an object that correlates to a row in your database is a 2 step process in Active Record. First, the object is instantiated.

trump_supporter = User.new("Kanye", 323-290-5589)

Second, the object is saved and persisted in the database.

trump_supporter.save

The moment you save the object to the database, Active Record validations are triggered. This is why it is typically good practice to separate instantiation and saving for better error handling — as opposed to lumping them together using Active Record’s .create. The following methods will trigger a validation:

.create
.create!
.save
.save!
.update
.update!

The bang versions (with !) will raise an exception if the record is invalid, whereas the non-bang versions will do the following: .save and .update return false, .create returns that object. NOTE: it is possible to save an object to a database without triggering our validations. Be sure to use these methods if you wish to trigger validations.

If you need extra error handling help, consider the following methods:

.valid? # triggers validations and returns true if no errors were found in the object, false otherwise.invalid? # inverse of .valid?.errors.messages # returns a collection of errors (if any)

Validation Helpers

Active Record provides us with 13 major validation helpers. You may find it useful to combine these helpers where appropriate, refer to the official documentation for customizing these validations, or create your own validation when you need additional specificity. For the scope of this post, I will only address the simplest implementation of Active Record’s 13 major validation helpers.

acceptance

class User < ApplicationRecord
validates :terms_of_use, acceptance: true
end

This method validates that an HTML form’s checkbox was checked when the form was submitted. Usually used for validating that a user has read the terms of service, agreed to something, etc.

validates_associated

class Author < ApplicationRecord
has_many :posts
validates_associated :posts
end

Use this method when an object has a relationship with another object that also needs to be validated. The method .valid? will be called on each associated object before continuing. Note: this method works with all association types, but remember to only call within one object to prevent an infinite loop of validations.

confirmation

class User < ApplicationRecord
validates :email, confirmation: true
validates :
email_confirmation, confirmation: true
end

This method creates a virtual attribute whose name is the name of the field that has to be confirmed with “_confirmation” appended. It is used when we want to compare and ensure that two text fields are exactly the same (ie a User must type in the same email twice to confirm the correct email). The form within our view might look like this:

<%= text_field :user, :email %>
<%= text_field :user, :email_confirmation %>

exclusion

class User < ApplicationRecord
validates :email, exclusion: {in: %w(yahoo aol hotmail msn)}
end

This validation is used when we want to exclude entries with certain characteristics or text. We use the helper :in to receive a set of values that will not be accepted for valid attributes. In this example, we don’t want to save any emails from the users who have Yahoo, AOL, Hotmail, and MSN accounts. Welcome to the age of Gmail, my friends.

format

class User < ApplicationRecord
validates :name, format: {with: /\A[a-zA-Z]+\z/, message: "only
allows letters"}
end

This method ensures that the text is formatted as desired. We use the helper :with to compare our attribute using a regular expression (see Rubular for help with regular expressions). The :message helper is used here to aid in error messages (see Active Record Validations for additional information on this universal validation helper).

inclusion

class Tshirt < ApplicationRecord
validates :size, inclusion: {in: %w(small medium large)}
end

This validation ensures that the given attribute exists among a given set. In this example, a t-shirt must be size small, medium, or large. Unfortunately, no XS or XL included in these shirts. Again, we use the helper :in to provide a set of values.

length

class User < ApplicationRecord
validates :bio, length: {maximum: 500}
end

As straightforward as it sounds, the length validation constrains an attribute’s length. Use the helpers :minimum, :maximum, :in (for range), and :is (for exact value) to assist in the specification.

numericality

class User < ApplicationRecord
validates :age, numericality: true
end

This method ensures the attribute value is numerical. It compares the given value against the following regular expression: /\A[+-]?\d+\z/. By default, nil values are not accepted. Use the following helpers after numericality: to customize the validation:

:only_integer
:greater_than
:greater_than_or_equal_to
:equal_to
:less_than
:less_than_or_equal_to
:other_than
:odd
:even
:allow_nil

presence

class User < ApplicationRecord
validates :name, :age, :email, presence: true
end

This validation is used to confirm the existence of attributes. It uses .blank? to check if the attribute is present, and raises an error if it returns false.

absence

class User < ApplicationRecord
validates :name, :age, :email, absence: true
end

Absence is the inverse of presence and uses .present? to evaluate if the value is nil or an empty string.

uniqueness

class User < ApplicationRecord
validates :email, uniqueness: true
end

This validation is extremely useful. It cross-checks the database for uniqueness before saving the object. Note: there may still be repeated values if the object is connected to two different databases. You can use :scope to specify specific attributes that must be unique, see the documentation.

validates_with & validates_each

These validations are more complex and highly customizable. In short, validates_with uses relationships with other objects to validate attributes. It passes the record to a separate class for validation. Whereasvalidates_each validates attributes against a block. It does not have a predefined validation function, thus requiring you to customize and specify. Refer to the documentation for additional information and examples.

Hopefully, you’ve gained a better understanding of how and why to use Active Record Validations to prevent breakage and unwanted data in your databases. Thanks for reading! References: Ruby on Rails Active Record Validation.

--

--

Haleigh Dalke
The Startup

Full-stack Software Engineer | Flatiron School alumni