A library for defining and validating data structures.
An Errors object documents the failures for a constraint or contract match.
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:
#type: A unique value that defines what kind of error was encountered. Each error’s type should be a namespaced String, e.g. "stannum.constraints.invalid".#data: A Hash of additional data about the error. For example, a failed type validation will include the expected type for the value; a failed range validation might include the minimum and maximum allowable values.#path: The path of the error relative to the top-level object that was validated. The path is always an Array, and each item in the array is either an Integer or a non-empty Symbol. Some examples:
[].3 of an array would have a path of [3].#name property of an object would have a path of [:name].[:manufacturers, 3, :address].#message: A human-readable description of the error. Error messages are not generated by default; either specify a message when defining the constraint, or call #with_messages to generate the error messages based on the error types and data. See Generating Messages, below.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'
# ]
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.
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'
# }
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.
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