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
# File 'lib/dry/types/schema.rb', line 80

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

#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:



296
297
298
# File 'lib/dry/types/schema.rb', line 296

def clear
  with(keys: EMPTY_ARRAY)
end

#constrained?Boolean

Returns:

  • (Boolean)


268
269
270
# File 'lib/dry/types/schema.rb', line 268

def constrained?
  true
end

#eachArray<Dry::Types::Schema::Key>, Enumerator

Iterate over each key type

Returns:



227
228
229
# File 'lib/dry/types/schema.rb', line 227

def each(&)
  keys.each(&)
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:



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

def key(name, fallback = Undefined, &)
  if Undefined.equal?(fallback)
    name_key_map.fetch(name, &)
  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)


238
239
240
# File 'lib/dry/types/schema.rb', line 238

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

#laxLax

Returns:



275
276
277
# File 'lib/dry/types/schema.rb', line 275

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:



287
288
289
# File 'lib/dry/types/schema.rb', line 287

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

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

Overloads:



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

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:



170
171
172
# File 'lib/dry/types/schema.rb', line 170

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

#strict?Boolean

Whether the schema rejects unknown keys

Returns:

  • (Boolean)


161
162
163
# File 'lib/dry/types/schema.rb', line 161

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



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

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)


196
197
198
# File 'lib/dry/types/schema.rb', line 196

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



93
94
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
# File 'lib/dry/types/schema.rb', line 93

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)


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

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