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

API:

  • public

Defined Under Namespace

Classes: Key

Constant Summary collapse

NO_TRANSFORM =

API:

  • public

::Dry::Types::FnContainer.register { |x| x }
SYMBOLIZE_KEY =

API:

  • public

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

#namespace, #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.

Options Hash (**options):

Parameters:

API:

  • private



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)

Returns:

API:

  • public



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:

API:

  • public



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

def name_key_map
  @name_key_map
end

#transform_key#call (readonly)

Returns:

API:

  • public



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}

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

Parameters:

  • (defaults to: EMPTY_HASH)

    a customizable set of options

Returns:

API:

  • public



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:

API:

  • private



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:

API:

  • private



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:

API:

  • public



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

def clear
  with(keys: EMPTY_ARRAY)
end

#constrained?Boolean

Returns:

API:

  • public



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:

API:

  • public



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:

    • Key name

    • (defaults to: Undefined)

      Optional fallback, returned if key is missing

    Returns:

    • key type or fallback if key is not in schema

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

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

    Parameters:

    • Key name

    • Fallback block, runs if key is missing

    Returns:

    • key type or block value if key is not in schema

API:

  • public



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:

  • Key name

Returns:

API:

  • public



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

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

#laxLax

Returns:

API:

  • public



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:

  • schema

Returns:

API:

  • public



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:

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

    Parameters:

    • (defaults to: EMPTY_HASH)

    Returns:

  • #schema(keys) ⇒ Dry::Types::Schema

    Parameters:

    • List of schema keys

    Returns:

API:

  • public



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:

API:

  • public



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

def strict(strict = true)
  with(strict: strict)
end

#strict?Boolean

Whether the schema rejects unknown keys

Returns:

API:

  • public



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:

  • (defaults to: true)

    Whether to dump the meta to the AST

Returns:

  • An AST representation

API:

  • public



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, :namespace),
     meta_ast(meta)]
  ]
end

#transform_keys?Boolean

Whether the schema transforms input keys

Returns:

API:

  • public



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

Yield Parameters:

  • failure (Failure)

Yield Returns:

Parameters:

  • hash

Returns:

  • if coercion fails and a block is given

API:

  • public



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

Raises:

Parameters:

  • (defaults to: nil)

Returns:

API:

  • public



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