Class: Dry::Types::Schema

Inherits:
Hash show all
Includes:
Enumerable
Defined in:
lib/dry/types/schema.rb,
lib/dry/types/schema/key.rb

Overview

Schema is a hash with explicit member types defined

Defined Under Namespace

Classes: Key

Constant Summary collapse

NO_TRANSFORM =
Dry::Types::FnContainer.register { |x| x }
SYMBOLIZE_KEY =
Dry::Types::FnContainer.register(:to_sym.to_proc)

Constants inherited from Hash

Hash::NOT_REQUIRED

Constants inherited from Nominal

Nominal::ALWAYS

Instance Attribute Summary collapse

Attributes inherited from Nominal

#primitive

Attributes included from Options

#options

Instance Method Summary collapse

Methods inherited from Hash

#constructor_type, #map, #transform_types?, #weak, #with_type_transform

Methods inherited from Nominal

[], #coerce, #default?, #failure, #name, #optional?, #primitive?, #success, #to_proc, #try_coerce

Methods included from Printable

#to_s

Methods included from Builder

#&, #>, #constrained, #constrained_type, #constructor, #constructor_type, #default, #enum, #fallback, #maybe, #optional, #|

Methods included from Meta

#meta, #pristine, #with

Methods included from Options

#with

Methods included from Type

#call, #valid?

Constructor Details

#initialize(_primitive, **options) ⇒ Schema

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Schema.

Parameters:

  • _primitive (Class)
  • options (Hash)

Options Hash (**options):



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/dry/types/schema.rb', line 41

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

Instance Attribute Details

#keysArray[Dry::Types::Schema::Key] (readonly)



26
27
28
# File 'lib/dry/types/schema.rb', line 26

def keys
  @keys
end

#name_key_mapHash[Symbol, Dry::Types::Schema::Key] (readonly)

Returns:



29
30
31
# File 'lib/dry/types/schema.rb', line 29

def name_key_map
  @name_key_map
end

#transform_key#call (readonly)

Returns:



32
33
34
# File 'lib/dry/types/schema.rb', line 32

def transform_key
  @transform_key
end

Instance Method Details

#apply(hash, options = EMPTY_HASH) ⇒ Hash{Symbol => Object}

Parameters:

  • hash (Hash)
  • options (Hash) (defaults to: EMPTY_HASH)

    a customizable set of options

Options Hash (options):

  • :skip_missing (Boolean)

    If true don’t raise error if on missing keys

  • :resolve_defaults (Boolean)

    If false default value won’t be evaluated for missing key

Returns:

  • (Hash{Symbol => Object})


80
81
82
# File 'lib/dry/types/schema.rb', line 80

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

#call_safe(hash, options = EMPTY_HASH) ⇒ Hash{Symbol => Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

Returns:

  • (Hash{Symbol => Object})


68
69
70
# File 'lib/dry/types/schema.rb', line 68

def call_safe(hash, options = EMPTY_HASH)
  resolve_safe(coerce(hash) { return yield }, options) { return yield }
end

#call_unsafe(hash, options = EMPTY_HASH) ⇒ Hash{Symbol => Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

Returns:

  • (Hash{Symbol => Object})


59
60
61
# File 'lib/dry/types/schema.rb', line 59

def call_unsafe(hash, options = EMPTY_HASH)
  resolve_unsafe(coerce(hash), options)
end

#clearSchema

Empty schema with the same options

Returns:



298
299
300
# File 'lib/dry/types/schema.rb', line 298

def clear
  with(keys: EMPTY_ARRAY)
end

#constrained?Boolean

Returns:

  • (Boolean)


270
271
272
# File 'lib/dry/types/schema.rb', line 270

def constrained?
  true
end

#each(&block) ⇒ Array<Dry::Types::Schema::Key>, Enumerator

Iterate over each key type

Returns:



229
230
231
# File 'lib/dry/types/schema.rb', line 229

def each(&block)
  keys.each(&block)
end

#key(name, fallback = Undefined) ⇒ Dry::Types::Schema::Key, Object #key(name, &block) ⇒ Dry::Types::Schema::Key, Object

Fetch key type by a key name

Behaves as ::Hash#fetch

Overloads:

  • #key(name, fallback = Undefined) ⇒ Dry::Types::Schema::Key, Object

    Returns key type or fallback if key is not in schema.

    Parameters:

    • name (Symbol)

      Key name

    • fallback (Object) (defaults to: Undefined)

      Optional fallback, returned if key is missing

    Returns:

  • #key(name, &block) ⇒ Dry::Types::Schema::Key, Object

    Returns key type or block value if key is not in schema.

    Parameters:

    • name (Symbol)

      Key name

    • block (Proc)

      Fallback block, runs if key is missing

    Returns:



259
260
261
262
263
264
265
# File 'lib/dry/types/schema.rb', line 259

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

#key?(name) ⇒ Boolean

Whether the schema has the given key

Parameters:

  • name (Symbol)

    Key name

Returns:

  • (Boolean)


240
241
242
# File 'lib/dry/types/schema.rb', line 240

def key?(name)
  name_key_map.key?(name)
end

#laxLax

Returns:



277
278
279
# File 'lib/dry/types/schema.rb', line 277

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

#merge(other) ⇒ Schema

Merge given schema keys into current schema

A new instance is returned.

Parameters:

Returns:



289
290
291
# File 'lib/dry/types/schema.rb', line 289

def merge(other)
  schema(other.keys)
end

#schema(type_map, meta = EMPTY_HASH) ⇒ Dry::Types::Schema #schema(keys) ⇒ Dry::Types::Schema

Overloads:



213
214
215
216
217
218
219
220
221
222
# File 'lib/dry/types/schema.rb', line 213

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

#strict(strict = true) ⇒ Schema

Make the schema intolerant to unknown keys

Returns:



172
173
174
# File 'lib/dry/types/schema.rb', line 172

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

#strict?Boolean

Whether the schema rejects unknown keys

Returns:

  • (Boolean)


163
164
165
# File 'lib/dry/types/schema.rb', line 163

def strict?
  options.fetch(:strict, false)
end

#to_ast(meta: true) ⇒ Array

Returns An AST representation.

Parameters:

  • meta (Boolean) (defaults to: true)

    Whether to dump the meta to the AST

Returns:

  • (Array)

    An AST representation



149
150
151
152
153
154
155
156
# File 'lib/dry/types/schema.rb', line 149

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

#transform_keys?Boolean

Whether the schema transforms input keys

Returns:

  • (Boolean)


198
199
200
# File 'lib/dry/types/schema.rb', line 198

def transform_keys?
  !options[:key_transform_fn].nil?
end

#try(input) {|failure| ... } ⇒ Logic::Result, Object

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/PerceivedComplexity

Parameters:

  • input (Hash)

    hash

Yield Parameters:

  • failure (Failure)

Yield Returns:

Returns:

  • (Logic::Result)
  • (Object)

    if coercion fails and a block is given



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/dry/types/schema.rb', line 95

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

#with_key_transform(proc = nil, &block) ⇒ Schema

Inject a key transformation function

Parameters:

  • proc (#call, nil) (defaults to: nil)
  • block (#call, nil)

Returns:

Raises:

  • (ArgumentError)


184
185
186
187
188
189
190
191
# File 'lib/dry/types/schema.rb', line 184

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