V1.3: Custom Predicates


We have seen that built-in predicates as an expressive tool to get our job done with common use cases.

But what if our case is not common? We can define our own custom predicates.

Inline Custom Predicates

If we are facing a really unique validation that does not need to be reused across our code, we can opt for an inline custom predicate:

require 'hanami/validations'

class Signup
  include Hanami::Validations

  predicate :url?, message: 'must be an URL' do |current|
    # ...
  end

  validations do
    required(:website) { url? }
  end
end

Global Custom Predicates

If our goal is to share commonly used custom predicates, we can include them in a module to be used in all our validators:

require 'hanami/validations'

module MyPredicates
  include Hanami::Validations::Predicates

  self.messages_path = 'config/errors.yml'

  predicate(:email?) do |current|
    current.match(/.../)
  end
end

We have defined a module MyPredicates with the purpose to share its custom predicates with all the validators that need them.

require 'hanami/validations'
require_relative 'my_predicates'

class Signup
  include Hanami::Validations
  predicates MyPredicates

  validations do
    required(:email) { email? }
  end
end

Internationalization (I18n)

require 'hanami/validations'

module MyPredicates
  include Hanami::Validations::Predicates

  self.messages_path = 'config/errors.yml'

  predicate(:uuid?) do |input|
    !/[0-9a-f]{8}-
    [0-9a-f]{4}-
    [0-9a-f]{4}-
    [0-9a-f]{4}-
    [0-9a-f]{12}/x.match(input).nil?
  end
end
require 'hanami/validations'
require_relative 'my_predicates'

module Web
  module Controllers
    module Signup
      class Params < Hanami::Action::Params
        predicates MyPredicates

        validations do
          required(:id).filled(:uuid?)
        end
      end
    end
  end
end
module Web
  module Controllers
    module Signup
      class Create
        include Web::Action
        params Params

        def call(params)
          # ...
        end
      end
    end
  end
end