Using Dry Validation for validating Ruby on Rails Controller params
I have being using Joi Validation for almost all my node project, getting to Rails, I am not always happy with how I validate writing different functions to validate request and sometimnes, it makes my controller bloated. I am not always happy about this, little did I know there is a gem for that.
In all my Node APIs, I usually create schema, add the schema to the middleware, to validate incoming requests and throws error if some rules are not followed. The good thing about Joi is that it has lot of functions to validate if something is a number, string etc.
I have been looking for a rails version of JOI but I have not been able to get, until today I was reading an article on Microservice in rails and a gem was mentioned which I check out and viola!! it has all what I need.
dry-validation is a data validation library that provides a powerful DSL for defining schemas and validation rules. It is very easy to use, define your contracts and call it in your controller. Sound Easy right.
An example of what I have been doing before I start using the gem, what if I have lot of parameter to validate, is the parameter a date, is start_date greater than end_date and the likes, the file will become bloated. Some can say I can create a module for that, but it still look dirty to me.
I moved my validation to a library that makes everything easy.
How to Setup⌗
## Gemfile gem 'dry-validation'
And then run bundle:
$ bundle
After this, you are good to go.
The next thing is to create a contracts.
# app/contracts/profile_contract.rb
class ProfileContract < Dry::Validation::Contract
params do
required(:email).filled(:string)
required(:username).filled(:string)
required(:accent).filled(:string)
end
rule(:email) do
unless /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value)
key.failure('has invalid format')
end
end
values = ['blue', 'yellow']
rule(:accent) do
key.failure('must be one of Blue or Yellow') if values.include value
end
end
In our controller, we can easily call the contract and do our validation.
class profileController < ApplicationController
def create
contract = ProfileContract.new
contract.call(profile_params)
render :json { errors: contract.errors } if contract.errors
## Save
end
end
Thats all, it makes the controller lean and has a railsey way of doing things. I love Rails. If you have a question, you can reach me on github. danielshow