struct Interro::Validations::Result(T)

Overview

The entrypoint into validating your objects is to instantiate a Result(T), which you then call validation methods on. When you're finished calling validations, you call #valid with a block. That block will execute if your values pass all validations, returning the value in the block (which must be the T type of the Result), or an Interro::Validations::Failure object, which contains the validation errors for the given inputs.

struct UserQuery < Interro::QueryBuilder(User)
  def create(*, name : String, email : String, team : Team, role : User::Role)
    Result(User).new
      .validate_presence(name: name, email: email)
      .validate_uniqueness("email") { where(email: email).any? }
      .validate_format(/\w@\w/, email: email, failure_message: "must be a valid email address")
      .valid do
        insert(
          name: name,
          email: email,
          team_id: team.id,
          role: role.value,
        )
      end
  end
end

Defined in:

validations.cr

Constructors

Instance Method Summary

Constructor Detail

def self.new #

[View source]

Instance Method Detail

def |(other : Result) #

Combine the errors of two different Result instances. The T types do not have to match.


[View source]
def initialize #

[View source]
def valid(&) : T | Failure #

Execute the block given if all validations have passed, otherwise return a Failure containing all of the validation errors.


[View source]
def validate(attribute : String, message : String, &) #

Validate a block returns truthy, failing validation with the given message if it returns falsy.

struct Post < Interro::QueryBuilder(T)
  table "posts"

  def create(*, title : String, body : String, by author : User, published : Bool, tags : Array(String)? = nil) : Post | Failure
    Result(Post).new
      .validate("tags", "must be populated for published posts") do
        !published || tags.try(&.any?)
      end
      .valid do
        published_at = Time.utc if published
        insert(
          title: title,
          body: body,
          author_id: author.id,
          published_at: published_at,
          tags: tags,
        )
      end
  end
end

[View source]
def validate(message : String, &) #

Validate a block returns truthy, failing validation with the given message if it returns falsy.

struct Post < Interro::QueryBuilder(T)
  table "posts"

  def create(*, title : String, body : String, by author : User, published : Bool, tags : Array(String)? = nil) : Post | Failure
    Result(Post).new
      .validate("published posts must have tags") do
        !published || tags.try(&.any?)
      end
      .valid do
        published_at = Time.utc if published
        insert(
          title: title,
          body: body,
          author_id: author.id,
          published_at: published_at,
          tags: tags,
        )
      end
  end
end

[View source]
def validate_format(name, value : String, format : Regex, *, failure_message : String = "is in the wrong format") : self #

Validate that value matches the expected format in the Regex, using the attribute name specified in name and a default failure_message.

Result(User).new
  .validate_format("email", email, /\w@\w/, failure_message: "must be a valid email address")

[View source]
def validate_format(value : String, format : Regex, *, failure_message : String) : self #

Validate that value matches the expected format in the Regex. This method does not infer a name, so a custom failure_message must be provided.

Result(User).new
  .validate_format(email, /\w@\w/, failure_message: "Email must be a valid email address ([email protected])")

[View source]
def validate_format(format : Regex, **attributes) : self #

Validate that all of the attributes match the expected format in the Regex.

Result(User).new
  .validate_format(/\w@\w/, email: email)

[View source]
def validate_presence(**values) : self #

Validate whether all of the values are present by calling .presence on them.

Result(Post).new
  .validate_presence(title: title, body: body)

[View source]
def validate_size(name : String, value, size : Range, unit : String, *, failure_message = default_validate_size_failure_message(size, unit)) : self #

Validate that value is of the expected size range. Value can be any object that responds to #size.

Result(User).new
  .validate_size("username", username, 2..64, "characters")

[View source]
def validate_uniqueness(attribute, &) : self #

Validate that the given attribute is unique by executing a block that returns truthy if any other objects exist with that attribute.

Result(User).new
  .validate_uniqueness("email") { where(email: email).any? }

[View source]
def validate_uniqueness(*, message : String, &) : self #

Validate that the given attribute is unique by executing a block that returns truthy if any other objects exist with that attribute.

Result(User).new
  .validate_uniqueness(message: "That email has already been taken") { where(email: email).any? }

[View source]