Enhancing Data Validation in Ruby on Rails with Custom Types and dry-validation

In the realm of Ruby on Rails development, data validation plays a critical role in ensuring the integrity and reliability of applications. While Rails provides built-in validation mechanisms, there are scenarios where custom validation requirements arise, necessitating a more flexible approach. In this article, we'll explore how you can leverage custom types defined with dry-types alongside dry-validation to enhance data validation in your Rails applications.

Enhancing Data Validation in Ruby on Rails with Custom Types and dry-validation
GUILHERME ANDRADE
April 26, 2024
/
Tutorials

The Need for Custom Types

In many applications, certain attributes have specific validation requirements beyond simple presence or format checks. For example, consider a scenario where you need to validate the gender attribute of a user record, ensuring that it contains one of the values 'male', 'female', or 'other'. While Rails provides mechanisms for basic validation, handling such custom requirements can be challenging.

Introducing dry-types

dry-types is a powerful gem that provides a flexible toolkit for defining and working with types in Ruby applications. One of its key features is the ability to define custom types tailored to your application's domain. Let's start by defining a custom type for the gender attribute:

require 'dry-types'

module Types
  include Dry::Types.module
  
  # Define custom type for gender attribute
  Gender = Strict::String.enum('male', 'female', 'other')
end

In this example, we define a custom type Gender using dry-types, restricting the values to 'male', 'female', or 'other'. With our custom type defined, let's integrate it into the validation process using dry-validation.

Validating Data with dry-validation

dry-validation is a validation library that integrates seamlessly with dry-types, allowing you to define validation contracts with support for custom types. Let's create a validation contract for a user entity that includes our custom type:

require 'dry-validation'

class UserValidator < Dry::Validation::Contract
  params do
    required(:name).filled(:string)
    required(:age).filled(:integer)
    required(:gender).filled(Types::Gender)
  end
end

In this validation contract, we specify that the :gender attribute must be filled and must adhere to the Types::Gender custom type we defined earlier. Now, let's see how we can use this validation contract in practice.

Putting it into Practice

# Usage example
input = { name: 'John', age: 25, gender: 'male' }

result = UserValidator.new.call(input)

if result.success?
  puts 'Validation passed!'
else
  puts 'Validation failed:'
  puts result.errors.to_h
end


In this example, we create a UserValidator instance and call it with input data containing attributes for name, age, and gender. The validation process checks that all required attributes are present and that the gender attribute conforms to our custom type. If validation fails, we print out the validation errors for further inspection.

Conclusion

By leveraging custom types defined with dry-types alongside dry-validation, you can enhance data validation in your Ruby on Rails applications with ease. Whether you need to enforce specific constraints on attributes or validate complex data structures, this approach provides a flexible and powerful solution. Incorporate custom types into your validation process to ensure data integrity and reliability, empowering you to build robust and maintainable Rails applications.

Happy coding!