All Ruby on Rails Node JS Android iOS React Native Frontend

Dry-validation Basics

Every program receives some kind of input dataIt can by anything - from variables, command line options, HTML web forms, configuration files to binary data. All of this needs to be checked to prevent unexpected errors from happening. Although ActiveModel::Validations is great for web apps and simple models, it isn't very flexible for other types of validations and complex dependencies.

Luckily, we have an alternative called dry-validation. It is one of the tools provided by dry-rb collection. You can find more at http://dry-rb.org/, there are many great libraries. But let's focus on dry-validation for a moment. It was designed from the start to act as general purpose validation framework capable of handling complex logic. It is also very performant in comparison to other libraries. 

In this post I briefly give an overview to the basic rules of DRY-validation.

 
Basics 

Installation is pretty simple, you just need to add the dry-validation gem to your Gemfile and require it. We are going to validate this simple data structure.

{
    name: 'John',
    surname: 'Doe'
}

The first thing we need to do is define validation schema.

require 'dry-validation'
    
validator = Dry::Validation.Schema do
    # validation rules
end

As you can see it takes a block as a parameter, inside of it we will add validation rules. You can think of schemas as classes that contain validation rules. A schema with no rules will always pass for any input. You can check it yourself by using UserValidator like a proc object (it responds to call).

validator.call('some input').success? #=> true
validator.call({}).success? #=> true
validator.call([]).success? #=> true

 

If you are familiar with dry-monads (yet another library from dry-rb) you should notice that we are accessing a Result object. For those of you who don't know The Result object can have two forms success or failure. We can check which one was returned by calling success? method. It'll return a boolean. It can also carry some data but that's another topic.


Let's get back to our schema and add some rules. To ensure that a specific key is present we use required method. Dry-validation makes a clear distinction between key presence and empty values. This concept is skipped in ActiveModel::Validations

where presence validation fails in both cases when the key is not present andwhen value is empty ('', [], {}, nil). Let's say we want to ensure that name and surname are non-empty strings. We can write it as follows.

validator = Dry::Validation.Schema do
    required(:name) { filled? && str? }
    required(:surname) { filled? && str? }
end

required takes a block with validation logic composed by simple predicates.

You can check built-in predicates here https://dry-rb.org/gems/dry-validation/basics/built-in-predicates/

validator.call({
    name: 'John',
    surname: 123
}).success? #=> false

 


validator.call({
    name: 'John',
    surname: 'Doe'
}).success? #=> true

As you can see we can use logic operators. You are probably wondering why we've used filled? and str? and not just str? alone. str? ensures that value is of string type and filled? checks if the value is not empty. For strings, it must be at least 1 character string so '' is considered an empty value. 

Nested rules

Let's move to a little more advanced topic which is validating nested data.

Consider the following structure

{
    name: 'John',
    surname: 'Doe',
    contact: {
        phone: '+48 123456789',
    }
}

 

As you can see, I've added a nested hash for contact information.Let's say that a phone is required and it must be properly formatted. Email field can be omitted but if it's present it should also have a valid format. 

# these are the simplified regex, don't use them in production!

PHONE_REGEX = /\A\+?[\ \d]*\z/
EMAIL_REGEX = /\A[\w \.]+@[\w \.]+\z/

validator = Dry::Validation.Schema do
    required(:name) { filled? && str? }
    required(:surname) { filled? && str? }
    required(:contact).schema do
        required(:phone) { filled? && format?(PHONE_REGEX) }
        optional(:email) { filled? && format?(EMAIL_REGEX) }
    end
end

 

Conclusion

Dry-validation is a really flexible and powerful validation library. It's getting more and more popular in the Ruby community. There are many features I haven't covered like error handling, arrays, reusable schemas and more. To learn more check the official documentation page. 

We're building our future. Let's do this right - join us
New Call-to-action
READ ALSO FROM Ruby/Ruby on Rails
Read also
Need a successful project?
Estimate project or contact us