Creating Bulletproof Validations in Ruby on Rails with Custom Validators [Part III]

Creating Bulletproof Validations in Ruby on Rails with Custom Validators [Part III]

Implementation

Revisited on May 26th, 2023


Greeting

Hey guys, how've you been? It's AC, Alexandre Calaça here. I'm so excited that you landed here. Hope you enjoy this new article.

By the way, I would be glad to receive your feedback about it.

Since you're already here, consider follow me on github and here on Hashnode.

You might be interested in checking my complete list of articles.


Objective

This is the Part III of the series Creating Bulletproof Validations in Ruby on Rails with Custom Validators.

Part I provided a basic introduction to validations.

Part II showed up to provide strong foundations for the next part, which is this Part III.

In this article, we will explore how to create custom validations in separate classes with Ruby on Rails. We will cover the basic concepts of custom validators, how to set them up in your Rails application, and how to use them in your models.

By the end of this article, you will have a solid understanding of how to create custom validators in Rails and how they can benefit your application development process.

Planning of this series:

Part I - Basic introduction
Part II - Strong Foundations
Part III - Implementation (This article you're reading)


Introduction

In a Ruby on Rails application, validation of input data is essential to ensure that the data is correct and meets the business requirements. Rails provides several built-in validation methods that can be used to validate various types of input data, such as numericality, presence, and format of the data. However, as the complexity of your models or the validation rules increases, using only the built-in validation methods can become challenging to manage.

To address this problem, you can create custom validation methods as separate classes that can be reused across multiple models. These custom validation classes allow you to define more complex validation rules and keep your models lean and easy to maintain. Additionally, custom validation classes promote code reuse by allowing you to define a set of validation rules in one place and then use it across multiple models..


Validations

Validations are a set of rules or conditions that are applied to input data to ensure that it meets certain criteria.

The purpose of validations is to ensure that the data entered by users or generated by the system is accurate, consistent, and safe to use.

Check Part I - Basic introduction and Part II - Strong Foundations in order to review this topic.


Validations in Ruby on Rails

In Ruby on Rails, validations are used to ensure that the data entered by users is valid and meets certain criteria before it is saved to the database. This helps to maintain data integrity and prevent errors in your application.

There are basically 3 approaches to validations:

  • built-in validation methods

  • Custom methods

  • Custom validators

This article is going to focus on Custom validators.


Built-in validation methods

Check Part I - Basic introduction and Part II - Strong Foundations in order to review this topic.


Custom methods

Check Part I - Basic introduction and Part II - Strong Foundations in order to review this topic.


Custom validators

Custom validators in Ruby on Rails are custom-built validation methods that allow you to define your own validation rules for your models. These validators are separate classes that can be created in your Rails application and used in your models.

Custom validators are important in Ruby on Rails development because they enable you to define more complex validation rules beyond what is provided by the built-in Rails validation methods. For example, you may need to validate a model attribute based on some external condition or perform validation that involves multiple attributes. Custom validators allow you to write this custom validation logic and encapsulate it in a separate class, making it easy to reuse across multiple models.

Using custom validators can also help keep your models lean and focused on their core responsibilities. By separating validation logic into a separate class, you can ensure that your models are not cluttered with validation code and that their codebase remains easy to maintain.

Overall, custom validators are an essential tool in Ruby on Rails development that allow you to define your own validation rules and keep your models lean and focused. By using custom validators, you can ensure that your Rails application's input data is validated according to your specific requirements and that your code remains modular and maintainable.


Steps

To create a custom validator in Rails, you need to define a class that inherits from ActiveModel::Validator or ActiveModel::EachValidator and implement a validate method.

The validate method can take a single or multiple arguments. One of them must be the model instance being validated, and you can add error messages to the model's errors collection if the validation fails.

Let me guide you through the process with these eight simple steps, which I call "baby steps."

8 super simples steps, I called them "baby steps".


1. Identification

Identify what class and attribute(s) you're going to test against.

Our example is going to consider a Product model with the following attributes:

irb(main):006:0> Product
=> Product(id: integer, title: string, description: text, image_url: string, price: decimal, created_at: datetime, updated_at: datetime)

Let's pick up the title attribute to start with. Let's just start with a custom presence validation.


2. Directory's Creation

This step is super simple. Before creating a separate class for the Product validation, it's necessary to add this class to a directory.

Any folders in app/ will be autoloaded, so you could create app/validators/my_validator.rb and include MyValidator in your model. Rails will autoload that and find it correctly. This is my preferred way of doing things usually.

Anyway, let's continue and go step by step.

Let's create a directory called validators inside app:

mkdir app/validators

It depends on your Rails version, but your app directory would look something like this

ls app
assets  controllers  helpers  mailers  models  validators  views

3. Validator's Creation

The third step is to create the responsible class for the validation.

Inside app/validators, create a file called product_validator.rb:

touch app/validators/product_validator.rb
ls app/validators/
product_validator.rb

4. Implement the class

The fourth step is to create a structure for the ProductValidator class. Pay attention, the file is product_validator.rb and the class must be called ProductValidator.

#app/validators/product_validator.rb
class ProductValidator < ActiveModel::Validator

  # Code goes here

end

5. Implement the validate method

class ProductValidator < ActiveModel::Validator
  def validate(record)
    if record.title.blank?
      record.errors[:title] << (options[:message] || "can't be blank")
    end

    # Add other validations for other attributes here
  end
end

6. Include the new validator class

In our example, let's use the Product model.

It's necessary to add include ActiveModel::Validations module into the Product class. This allows the class to use the validation methods and helpers provided by the ActiveModel::Validations module.

In order to declare our new validation class for the Product model, we're going to use the following command inside your model

  include ActiveModel::Validations

This should be the result so far

#app/models/product.rb
class Product < ActiveRecord::Base
  include ActiveModel::Validations
  attr_accessible :description, :image_url, :price, :title  

end

7. Use the new validate method

#app/models/product.rb

validates_with ProductValidator

The result should look something like the following:

#app/models/product.rb
class Product < ActiveRecord::Base
  include ActiveModel::Validations
  attr_accessible :description, :image_url, :price, :title

  validates_with ProductValidator
end

8. Test in the console

product = Product.new
product.save!

You might see something like this

irb(main):011:0> product = Product.new
=> #<Product id: nil, title: nil, description: nil, image_url: nil, price: nil, created_at: nil, updated_at: nil>
irb(main):012:0> product.save!
   (0.1ms)  begin transaction
   (0.1ms)  rollback transaction
ActiveRecord::RecordInvalid: Validation failed: Title can't be blank

Conclusion

Summing up, In Part I, we had a basic introduction to validations.

Part II provided more details and more examples.

In conclusion, using custom validations in separate classes with Ruby on Rails can be a powerful tool for simplifying complex validation rules and promoting code reuse.

By following the steps outlined in this article, you can create your own custom validators and apply them to your Rails models with ease. Whether you are a seasoned Rails developer or just starting out, custom validators can help you build more robust and maintainable applications.


Celebrate

Way to go, guys. You did a great job. You can definitely celebrate!


Final thoughts

That's all for today. Thanks for reading this article.

I hope this article helped you. Let me know if you have any questions.

Your thoughts, suggestions and corrections are more than welcome.

By the way, feel free to drop your suggestions on new blog articles.

Hope to see you next time.