class Flows::Contract::HashOf

Contract for Ruby `Hash` with specified structure.

Hash can have extra keys. Extra keys will be removed after transform. Underlying contracts' transforms will be applied to correspond values.

@example

point_type = Flows::Contract::HashOf.new(x: Numeric, y: Numeric)

point_type === { x: 1, y: 2.0 }
# => true

point_type === { x: 1, y: 2.0, name: 'Petr' }
# => true

point_type.cast(x: 1, y: 2.0, name: 'Petr').unwrap
# => { x: 1, y: 2.0 }

point_type.check({ x: 1, name: 'Petr' })
# => Flows::Result::Error.new('missing key `:y`')

point_type.check({ x: 1, y: 'Vasya' })
# => Flows::Result::Error.new('key `:y` has an invalid value: must match `Numeric`')

Constants

HASH_CONTRACT

Public Class Methods

new(shape = {}) click to toggle source
# File lib/flows/contract/hash_of.rb, line 28
def initialize(shape = {})
  @shape = shape.transform_values(&method(:to_contract))
end

Public Instance Methods

check!(other) click to toggle source
# File lib/flows/contract/hash_of.rb, line 32
def check!(other)
  HASH_CONTRACT.check!(other)

  errors = check_shape(other)

  raise Error.new(other, errors.join("\n")) if errors.any?

  true
end
transform!(other) click to toggle source
# File lib/flows/contract/hash_of.rb, line 42
def transform!(other)
  check!(other)

  other
    .slice(*@shape.keys)
    .map { |key, value| [key, @shape[key].transform!(value)] }
    .to_h
end

Private Instance Methods

check_shape(other) click to toggle source

:reek: DuplicateMethodCall

# File lib/flows/contract/hash_of.rb, line 54
def check_shape(other)
  @shape.each_with_object([]) do |(key, type), errors|
    unless other.key?(key)
      errors << "missing hash key `#{key.inspect}`"
      next
    end

    result = type.check(other[key])

    if result.err?
      errors << merge_nested_errors("hash key `#{key.inspect}` has an invalid assigned value:", result.error)
    end
  end
end