Class: Stannum::Errors
- Inherits:
-
Object
- Object
- Stannum::Errors
- Includes:
- Enumerable
- Defined in:
- lib/stannum/errors.rb
Overview
An errors object represents a collection of errors.
Most of the time, an end user will not be creating an Errors object directly. Instead, an errors object may be returned by a process that validates or coerces data to an expected form. For one such example, see the Stannum::Constraint and its subclasses.
Internally, an errors object is an Array of errors. Each error is represented by a Hash containing the keys :data, :message, :path and :type.
-
The :type of the error is a short, unique symbol or string that identifies the type of the error, such as ‘invalid’ or ‘not_found’. The type is frequently namespaced, e.g. ‘stannum.constraints.present’.
-
The :message of the error is a short string that provides a human-readable description of the error, such as ‘is invalid’ or ‘is not found’. The message may include format directives for error data (see below). If the :message key is missing or the value is nil, use a default error message or generate the message from the :type.
-
The :data of the error stores additional information about the error and the expected behavior. For example, an out of range error might have type: ‘out_of_range’ and data { min: 0, max: 10 }, indicating that the expected values were between 0 and 10. If the data key is missing or the value is empty, there is no additional information about the error.
-
The :path of the error reflects the steps to resolve the relevant property from the given data object. The path is an Array with keys of either Symbols/Strings (for object properties or Hash keys) or Integers (for Array indices). For example, given the hash { companies: [{ teams: [] }] } and an expecation that a company’s team must not be empty, the resulting error would have path: [:companies, 0, :teams]. if the path key is missing or the value is empty, the error refers to the root object.
Instance Method Summary collapse
-
#==(other) ⇒ true, false
(also: #eql?)
Checks if the other errors object contains the same errors.
-
#[](key) ⇒ Stannum::Errors
Accesses a nested errors object.
-
#[]=(key, value) ⇒ Object
Replaces the child errors with the specified errors object or Array.
-
#add(type, message: nil, **data) ⇒ Stannum::Errors
Adds an error of the specified type.
-
#dig(first, *rest) ⇒ Stannum::Errors
Accesses a (possibly deeply) nested errors object.
-
#dup ⇒ Stannum::Errors
Creates a deep copy of the errors object.
- #each ⇒ Object
-
#empty? ⇒ true, false
(also: #blank?)
Checks if the errors object contains any errors.
-
#group_by_path ⇒ Hash<Array, Array>
Groups the errors by the error path.
-
#initialize ⇒ Errors
constructor
A new instance of Errors.
-
#inspect ⇒ String
A human-readable representation of the object.
-
#merge(value) ⇒ Stannum::Errors
Adds the given errors to a copy of the errors object.
-
#size ⇒ Integer
(also: #count)
The number of errors in the errors object.
-
#summary ⇒ String
Generates a text summary of the errors.
-
#to_a ⇒ Array<Hash>
Generates an array of error objects.
-
#update(value) ⇒ self
Adds the given errors to the errors object.
-
#with_messages(force: false, strategy: nil) ⇒ Stannum::Errors
Creates a copy of the errors and generates error messages for each error.
Constructor Details
#initialize ⇒ Errors
Returns a new instance of Errors.
143 144 145 146 147 |
# File 'lib/stannum/errors.rb', line 143 def initialize @children = Hash.new { |hsh, key| hsh[key] = self.class.new } @cache = Set.new @errors = [] end |
Instance Method Details
#==(other) ⇒ true, false Also known as: eql?
Checks if the other errors object contains the same errors.
153 154 155 156 157 158 159 |
# File 'lib/stannum/errors.rb', line 153 def ==(other) return false unless other.is_a?(Array) || other.is_a?(self.class) return false unless empty? == other.empty? compare_hashed_errors(other) end |
#[](key) ⇒ Stannum::Errors
Accesses a nested errors object.
Each errors object can have one or more children, each of which is itself an errors object. These nested errors represent errors on some subset of the main object - for example, a failed validation of a named property, of the value in a key-value pair, or of an indexed value in an ordered collection.
The children are created as needed and are stored with either an integer or a symbol key. Calling errors multiple times will always return the same errors object. Likewise, calling errors multiple times will return the same object, and calling errors will return that same errors object as well.
231 232 233 234 235 |
# File 'lib/stannum/errors.rb', line 231 def [](key) validate_key(key) @children[key] end |
#[]=(key, value) ⇒ Object
Replaces the child errors with the specified errors object or Array.
If the given value is nil or an empty array, the #[]= operator will remove the child errors object at the given key, removing all errors within that namespace and all namespaces nested inside it.
If the given value is an errors object or an Array of errors object, the #[]= operation will replace the child errors object at the given key, removing all existing errors and adding the new errors. Each added error will use its nested path (if any) as a relative path from the given key.
294 295 296 297 298 299 300 |
# File 'lib/stannum/errors.rb', line 294 def []=(key, value) validate_key(key) value = normalize_value(value, allow_nil: true) @children[key] = value end |
#add(type, message: nil, **data) ⇒ Stannum::Errors
Adds an error of the specified type.
328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/stannum/errors.rb', line 328 def add(type, message: nil, **data) error = build_error(data: data, message: , type: type) hashed = error.hash return self if @cache.include?(hashed) @errors << error @cache << hashed self end |
#dig(keys) ⇒ Stannum::Errors #dig(*keys) ⇒ Stannum::Errors
Accesses a (possibly deeply) nested errors object.
Similiar to the #[] method, but can access a deeply nested errors object as well. The #dig method can take either a list of one or more keys (Integers, Strings, and Symbols) as arguments, or an Array of keys. Calling errors.dig is equivalent to calling errors[] with each key in sequence.
406 407 408 409 410 |
# File 'lib/stannum/errors.rb', line 406 def dig(first, *rest) path = first.is_a?(Array) ? first : [first, *rest] path.reduce(self) { |errors, segment| errors[segment] } end |
#dup ⇒ Stannum::Errors
Creates a deep copy of the errors object.
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/stannum/errors.rb', line 415 def dup # rubocop:disable Metrics/MethodLength child = self.class.new each do |error| child # rubocop:disable Style/SingleArgumentDig .dig(error.fetch(:path, [])) .add( error.fetch(:type), message: error[:message], **error.fetch(:data, {}) ) end child end |
#each ⇒ Enumerator #each {|error| ... } ⇒ Object
441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/stannum/errors.rb', line 441 def each return to_enum(:each) { size } unless block_given? @errors.each { |item| yield item.merge(path: []) } @children.each do |path, child| child.each do |item| yield item.merge(path: item.fetch(:path, []).dup.unshift(path)) end end end |
#empty? ⇒ true, false Also known as: blank?
Checks if the errors object contains any errors.
457 458 459 |
# File 'lib/stannum/errors.rb', line 457 def empty? @errors.empty? && @children.all?(&:empty?) end |
#group_by_path ⇒ Hash<Array, Array> #group_by_path {|error| ... } ⇒ Hash<Array, Array>
Groups the errors by the error path.
Generates a Hash whose keys are the unique error :path values. For each path, the corresponding value is the Array of all errors with that path.
This will flatten paths: an error with path [:parts] will be grouped in a separate array from a part with path [:parts, :assemblies].
Errors with an empty path will be grouped with a key of an empty Array.
480 481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/stannum/errors.rb', line 480 def group_by_path grouped = Hash.new { |hsh, key| hsh[key] = [] } each do |error| path = error[:path] value = block_given? ? yield(error) : error grouped[path] << value end grouped end |
#inspect ⇒ String
Returns a human-readable representation of the object.
494 495 496 497 498 |
# File 'lib/stannum/errors.rb', line 494 def inspect oid = super[2...].split.first.split(':').last "#<#{self.class.name}:#{oid} @summary=%{#{summary}}>" end |
#merge(value) ⇒ Stannum::Errors
Adds the given errors to a copy of the errors object.
Creates a copy of the errors object, and then adds each error in the passed in errors object or array to the copy. The copy will thus contain all of the errors from the original object and all of the errors from the passed in object. The original object is not changed.
516 517 518 519 520 |
# File 'lib/stannum/errors.rb', line 516 def merge(value) value = normalize_value(value, allow_nil: false) dup.update_errors(value) end |
#size ⇒ Integer Also known as: count
The number of errors in the errors object.
525 526 527 528 529 |
# File 'lib/stannum/errors.rb', line 525 def size @errors.size + @children.each_value.reduce(0) do |total, child| total + child.size end end |
#summary ⇒ String
Generates a text summary of the errors.
535 536 537 538 539 |
# File 'lib/stannum/errors.rb', line 535 def summary .map { |error| generate_summary_item(error) } .join(', ') end |
#to_a ⇒ Array<Hash>
Generates an array of error objects.
Each error is a hash containing the keys :data, :message, :path and :type.
546 547 548 |
# File 'lib/stannum/errors.rb', line 546 def to_a each.to_a end |
#update(value) ⇒ self
Adds the given errors to the errors object.
Adds each error in the passed in errors object or array to the current errors object. It will then contain all of the original errors and all of the errors from the passed in object. This changes the current object.
565 566 567 568 569 |
# File 'lib/stannum/errors.rb', line 565 def update(value) value = normalize_value(value, allow_nil: false) update_errors(value) end |
#with_messages(force: false, strategy: nil) ⇒ Stannum::Errors
Creates a copy of the errors and generates error messages for each error.
579 580 581 582 583 584 585 586 587 588 589 590 591 |
# File 'lib/stannum/errors.rb', line 579 def (force: false, strategy: nil) strategy ||= Stannum::Messages.strategy dup.tap do |errors| errors.each_error do |error| next unless force || error[:message].nil? || error[:message].empty? = strategy.call(error[:type], **(error[:data] || {})) error[:message] = end end end |