class Dry::Types::Schema

@api public
{Schema} implements Enumerable using its keys as collection.
@see Dry::Types::Default::Callable#evaluate
@see Dry::Types::Default#evaluate
{Schema} evaluates default values for keys missing in input hash
@see Dry::Types::Schema::Key
as lists of {Key} types.
its values can contain. Such definitions are named {Schema}s and defined
The built-in Hash type can be defined in terms of keys and associated types

def apply(hash, options = EMPTY_HASH) = call_unsafe(hash, options)

Other tags:
    Api: - public

Returns:
  • (Hash{Symbol => Object}) -

Options Hash: (**options)
  • :resolve_defaults (Boolean) -- If false default value
  • :skip_missing (Boolean) -- If true don't raise error if on missing keys

Parameters:
  • hash (Hash) --
def apply(hash, options = EMPTY_HASH) = call_unsafe(hash, options)

def call_safe(hash, options = EMPTY_HASH)

Other tags:
    Api: - private

Returns:
  • (Hash{Symbol => Object}) -

Parameters:
  • hash (Hash) --
def call_safe(hash, options = EMPTY_HASH)
  resolve_safe(coerce(hash) { return yield }, options) { return yield }
end

def call_unsafe(hash, options = EMPTY_HASH)

Other tags:
    Api: - private

Returns:
  • (Hash{Symbol => Object}) -

Parameters:
  • hash (Hash) --
def call_unsafe(hash, options = EMPTY_HASH)
  resolve_unsafe(coerce(hash), options)
end

def clear

Other tags:
    Api: - public

Returns:
  • (Schema) -
def clear
  with(keys: EMPTY_ARRAY)
end

def constrained?

Other tags:
    Api: - public

Returns:
  • (Boolean) -
def constrained?
  true
end

def each(&)

Other tags:
    Api: - public

Returns:
  • (Array, Enumerator) -
def each(&)
  keys.each(&)
end

def initialize(_primitive, **options)

Other tags:
    Api: - private

Options Hash: (**options)
  • :key_transform_fn (String) --
  • :keys (Array[Dry::Types::Schema::Key]) --

Parameters:
  • options (Hash) --
  • _primitive (Class) --
def initialize(_primitive, **options)
  @keys = options.fetch(:keys)
  @name_key_map = keys.each_with_object({}) do |key, idx|
    idx[key.name] = key
  end
  key_fn = options.fetch(:key_transform_fn, NO_TRANSFORM)
  @transform_key = ::Dry::Types::FnContainer[key_fn]
  super
end

def key(name, fallback = Undefined, &)

Other tags:
    Api: - public

Returns:
  • (Dry::Types::Schema::Key, Object) - key type or block value if key is not in schema
  • (Dry::Types::Schema::Key, Object) - key type or fallback if key is not in schema

Parameters:
  • block (Proc) -- Fallback block, runs if key is missing
  • name (Symbol) -- Key name
  • fallback (Object) -- Optional fallback, returned if key is missing
  • name (Symbol) -- Key name

Overloads:
  • key(name, &block)
  • key(name, fallback = Undefined)
def key(name, fallback = Undefined, &)
  if Undefined.equal?(fallback)
    name_key_map.fetch(name, &)
  else
    name_key_map.fetch(name, fallback)
  end
end

def key?(name)

Other tags:
    Api: - public

Returns:
  • (Boolean) -

Parameters:
  • name (Symbol) -- Key name
def key?(name)
  name_key_map.key?(name)
end

def lax

Other tags:
    Api: - public

Returns:
  • (Lax) -
def lax
  Lax.new(schema(keys.map(&:lax)))
end

def merge(other)

Other tags:
    Api: - public

Returns:
  • (Schema) -

Parameters:
  • other (Schema) -- schema
def merge(other)
  schema(other.keys)
end

def merge_keys(*keys)

Other tags:
    Api: - private

Returns:
  • (Dry::Types::Schema) -

Parameters:
  • keys (Array) --
def merge_keys(*keys)
  keys
    .flatten(1)
    .each_with_object({}) { |key, merged| merged[key.name] = key }
    .values
end

def missing_key(key)

Other tags:
    Api: - private

Returns:
  • (MissingKeyError) -
def missing_key(key)
  MissingKeyError.new(key)
end

def resolve_missing_keys(hash, options) # rubocop:disable Metrics/PerceivedComplexity

Other tags:
    Api: - private
def resolve_missing_keys(hash, options) # rubocop:disable Metrics/PerceivedComplexity
  skip_missing = options.fetch(:skip_missing, false)
  resolve_defaults = options.fetch(:resolve_defaults, true)
  keys.each do |key|
    next if hash.key?(key.name)
    if key.default? && resolve_defaults
      hash[key.name] = key.call_unsafe(Undefined)
    elsif key.required? && !skip_missing
      if block_given?
        return yield
      else
        raise missing_key(key.name)
      end
    end
  end
end

def resolve_safe(hash, options = EMPTY_HASH, &block)

Returns:
  • (Hash) -

Other tags:
    Api: - private
def resolve_safe(hash, options = EMPTY_HASH, &block)
  result = {}
  hash.each do |key, value|
    k = @transform_key.(key)
    type = @name_key_map[k]
    if type
      result[k] = type.call_safe(value, &block)
    elsif strict?
      yield
    end
  end
  resolve_missing_keys(result, options, &block) if result.size < keys.size
  result
end

def resolve_unsafe(hash, options = EMPTY_HASH)

Returns:
  • (Hash) -

Other tags:
    Api: - private
def resolve_unsafe(hash, options = EMPTY_HASH)
  result = {}
  hash.each do |key, value|
    k = @transform_key.(key)
    type = @name_key_map[k]
    if type
      begin
        result[k] = type.call_unsafe(value)
      rescue ConstraintError => e
        raise SchemaError.new(type.name, value, e.result)
      rescue CoercionError => e
        raise SchemaError.new(type.name, value, e.message)
      end
    elsif strict?
      raise unexpected_keys(hash.keys)
    end
  end
  resolve_missing_keys(result, options) if result.size < keys.size
  result
end

def schema(keys_or_map)

Other tags:
    Api: - public

Returns:
  • (Dry::Types::Schema) -
  • (Dry::Types::Schema) -

Parameters:
  • meta (Hash) --
  • key (Array) -- List of schema keys
  • meta (Hash) --
  • type_map ({Symbol => Dry::Types::Nominal}) --

Overloads:
  • schema(keys)
  • schema(type_map, meta = EMPTY_HASH)
def schema(keys_or_map)
  if keys_or_map.is_a?(::Array)
    new_keys = keys_or_map
  else
    new_keys = build_keys(keys_or_map)
  end
  keys = merge_keys(self.keys, new_keys)
  Schema.new(primitive, **options, keys: keys, meta: meta)
end

def strict(strict = true) # rubocop:disable Style/OptionalBooleanParameter

Other tags:
    Api: - public

Returns:
  • (Schema) -
def strict(strict = true) # rubocop:disable Style/OptionalBooleanParameter
  with(strict: strict)
end

def strict?

Other tags:
    Api: - public

Returns:
  • (Boolean) -
def strict?
  options.fetch(:strict, false)
end

def to_ast(meta: true)

Other tags:
    Api: - public

Returns:
  • (Array) - An AST representation

Parameters:
  • meta (Boolean) -- Whether to dump the meta to the AST
def to_ast(meta: true)
  [
    :schema,
    [keys.map { |key| key.to_ast(meta: meta) },
     options.slice(:key_transform_fn, :type_transform_fn, :strict),
     meta ? self.meta : EMPTY_HASH]
  ]
end

def transform_keys?

Other tags:
    Api: - public

Returns:
  • (Boolean) -
def transform_keys?
  !options[:key_transform_fn].nil?
end

def try(input)

Other tags:
    Api: - public

Returns:
  • (Object) - if coercion fails and a block is given
  • (Logic::Result) -

Other tags:
    Yieldreturn: -

Other tags:
    Yieldparam: failure -

Parameters:
  • input (Hash) -- hash
def try(input)
  if primitive?(input)
    success = true
    output = {}
    result = {}
    input.each do |key, value|
      k = @transform_key.(key)
      type = @name_key_map[k]
      if type
        key_result = type.try(value)
        result[k] = key_result
        output[k] = key_result.input
        success &&= key_result.success?
      elsif strict?
        success = false
      end
    end
    if output.size < keys.size
      resolve_missing_keys(output, options) do
        success = false
      end
    end
    success &&= primitive?(output)
    if success
      failure = nil
    else
      error = CoercionError.new("#{input} doesn't conform schema", meta: result)
      failure = failure(output, error)
    end
  else
    failure = failure(input, CoercionError.new("#{input} must be a hash"))
  end
  if failure.nil?
    success(output)
  elsif block_given?
    yield(failure)
  else
    failure
  end
end

def unexpected_keys(hash_keys)

Other tags:
    Api: - private

Returns:
  • (UnknownKeysError) -

Parameters:
  • hash_keys (Array) --
def unexpected_keys(hash_keys)
  extra_keys = hash_keys.map(&transform_key) - name_key_map.keys
  UnknownKeysError.new(extra_keys)
end

def with_key_transform(proc = nil, &block)

Other tags:
    Api: - public

Returns:
  • (Schema) -

Parameters:
  • block (#call, nil) --
  • proc (#call, nil) --
def with_key_transform(proc = nil, &block)
  fn = proc || block
  raise ::ArgumentError, "a block or callable argument is required" if fn.nil?
  handle = ::Dry::Types::FnContainer.register(fn)
  with(key_transform_fn: handle)
end