Loading...

Hanami Validations in Rails

Hanami Validations

Usual validations

After Extracting validations from ActiveRecord model there is one way to improve your validations. We got decoupled code that wasn’t supposed to be decoupled. Active model validations doesn’t look too good and needs a virtus-like gem to be working outside of the activemodel.

Let’s take our UserValidator from post about extracting validations

class UserValidator
  include Virtus.model
  include ActiveModel::Validations
 
  attribute :first_name, String
  attribute :last_name, String
  attribute :nickname, String
  attribute :age, Integer
  attribute :published, Boolean
 
  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :age, presence: true, numericality: { only_integer: true }
  validates :nickname, presence: true, length: { in: 3..20 }
end

My opinion

It’s pretty nice already but personally, I really dislike this type of writing validation logic. It doesn’t show the purpose of code on first sight and you need to take some time to understand more complex validations. These are pretty simple, but validation logic can go really complex in many fields.

Fixing the issue

What I’m using recently is `Hanami::Validations`. It’s part of Hanami framework and built on dry-validation from dry-rb. Let’s start with translating our old UserValidator to hanami.

class UserValidator
  include Hanami::Validations
 
  required(:first_name) { filled & str? }
  required(:last_name) { filled & str? }
  required(:nickname) { filled & str? & size?(3..20) }
  required(:age) { filled & int? }
  required(:published) { filled & bool? }
end

Doesn’t it look nice? It’s nice, tidy and easily readable. Public interface is a little bit different than one from AR so we need to do a little adjustments in controller code

  def create
    validation_result = UserValidator.new(validation_result.output).validate
 
    if validation_result.valid?
      user = User.create!(validation_result.output)
      render :show, status: :created, location: user
    else
      render json: validation_result.errors, status: :unprocessable_entity
    end
  end
 
  def update
    validation_result = UserValidator.new(validation_result.output).validate
    user = User.find(params[:id])
 
    if validation_result.valid?
      user.update(validation_result.output)
      render :show, status: :created, location: user
    else
      render json: validation_result.errors, status: :unprocessable_entity
    end
  end

Is it really different?

There are few differences about validations done this way. The first and most obvious one is the syntax. It’s more readable and based on boolean logic which seems natural for us, programmers.
It uses dry-types so you get real type validation in ruby. The public interface is a little bit different than the one from active record. `validate` method doesn’t mutate internal state of an object but instead gives you result object with `success?` the method which is quite obvious. Also, you get `errors` which self-describes pretty well and `output` which is the validated form when validation was successful.

Interested?

If you’d like to give hanami/dry validations a try, just check http://dry-rb.org/gems/dry-validation/ and https://github.com/hanami/validations They both have awesome documentation and it’s really easy to start with them. Just check them!

If you like my posts, you can follow me on twitter/facebook or subscribe to push notifications(red mark in the left down corner of the page).

Setting up Rails API with Docker

docker rails api

Lately, Docker has gotten a lot of popularity and it turns out, that there are some serious reasons behind that. It simplified containers so much, that even developers can handle managing them. When building a new application, it’s worth to consider building it in docker container.It won’t depend on your local machine and can be easily moved to your teammates’ machines.
(more…)

Social media & sharing icons powered by UltimatelySocial