Stannum

A library for defining and validating data structures.

Errors

An Errors object documents the failures for a constraint or contract match.

Contents

Constraints And Errors

When a constraint or contract fails to match an object, Stannum can return the reasons for that failure in the form of an errors object. Specifically, a Stannum::Errors object is returned by calling #errors_for or #negated_errors_for with a failing object, or as part of the result of calling #match or #negated_match.

contract.matches?(nil)
#=> false
contract.errors_for(nil)
#=> an instance of Stannum::Errors
status, errors = contract.match(nil)
status
#=> false
errors
#=> an instance of Stannum::Errors

A Stannum::Errors object is an Enumerable collection, while each error is a Hash with the following properties:

The simplest way to access the errors in a Stannum::Errors object is via the #each method, which will yield each error in the collection to the given block. Because each Stannum::Errors is enumerable, you can use the standard Enumerable methods such as #map, #reduce, #select, and so on. You can also use #count to return the number of errors, or #empty? to check if there are any errors in the collection.

errors.count
#=> 3
errors.empty?
#=> false
errors.first
#=> {
#     data: {},
#     message: nil,
#     path: [:name],
#     type: 'stannum.constraints.invalid'
#   }
errors.map(&:type)
#=> [
#     'stannum.constraints.invalid',
#     'stannum.constraints.absent',
#     'stannum.constraints.is_not_type'
#   ]

Adding Errors

Usually, an errors object is generated automatically by a constraint or contract with its errors already defined. If you want to add custom errors to an errors object, use the #add method, which takes the error type as one required argument. You can also specify the message keyword, which sets the message of the error. Finally, any additional keywords are added to the error data.

errors = Stannum::Errors.new
errors.count
#=> 0
errors.empty?
#=> true

errors.add('example.constraints.out_of_range', message: 'out of range', min: 0, max: 10)
#=> the errors object
errors.count
#=> 1
errors.empty?
#=> false
errors.first
#=> {
#     data: { min: 0, max: 10 },
#     message: 'out of range',
#     path: [],
#     type: 'example.constraints.out_of_range'
#   }

Conveniently, #add returns the errors object itself, so you can chain together multiple #add calls.

Nested Errors

To represent the properties of an object or the values in a data structure, Stannum::Errors can be nested together. Nested error objects are accessed using the #[] operator.

errors = Stannum::Errors.new
errors[:manufacturers][0][:address].add('stannum.constraints.invalid')
errors[:manufacturers][0][:address]
#=> an instance of Stannum::Errors
errors[:manufacturers][0][:address].count
#=> 1
errors[:manufacturers][0][:address].first
#=> {
#     data: {},
#     message: nil,
#     path: [],
#     type: 'stannum.constraints.invalid'
#   }

errors.count
#=> 1
errors.first
#=> {
#     data: {},
#     message: nil,
#     path: [:manufacturers, 0, :address],
#     type: 'stannum.constraints.invalid'
#   }

You can also use the #dig method to access nested errors:

errors.dig(:manufacturers, 0, :address).first
#=> {
#     data: {},
#     message: nil,
#     path: [],
#     type: 'stannum.constraints.invalid'
#   }

Generating Messages

By default, errors objects do not generate messages. Stannum::Errors defines the #with_messages method to generate messages for a given errors object. If the :force keyword is set to true, then #with_messages will overwrite any messages that are already set on an error, whether from a constraint or generated by a different strategy.

errors.first.message
#=> nil

errors = errors.with_messages.first.message
errors.first.message
#=> 'is invalid'

Stannum uses the strategy pattern to determine how error messages are generated. You can pass the strategy: keyword to #with_messages to force Stannum to use the specified strategy, or set the Stannum::Messages.strategy property to define the default for your application. The default strategy for Stannum uses an I18n-like configuration file to define messages based on the type and optionally the data for each error.

Error Summaries

Each errors object defines a #summary method that collects the path and message for each error into a single comma-separated string.

errors = Stannum::Errors.new
errors['rocket'].add('already_launched', message: 'has already launched')
errors['rocket'].add('wrong_direction', message: 'not pointed toward space')
errors['rocket']['fuel'].add('empty', message: 'is empty')

errors.summary
#=> "rocket: has already launched, rocket: not pointed toward space, rocket.fuel: is empty"

Use the error summary to display a human-readable representation of the errors, such as in a displayed failure message.


Back to Documentation