1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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 139 140 141 142 143
|
# frozen_string_literal: true
require 'dry/types/hash/constructor'
module Dry
module Types
# Hash types can be used to define maps and schemas
#
# @api public
class Hash < Nominal
NOT_REQUIRED = { required: false }.freeze
# @overload schema(type_map, meta = EMPTY_HASH)
# @param [{Symbol => Dry::Types::Nominal}] type_map
# @param [Hash] meta
# @return [Dry::Types::Schema]
#
# @overload schema(keys)
# @param [Array<Dry::Types::Schema::Key>] key List of schema keys
# @param [Hash] meta
# @return [Dry::Types::Schema]
#
# @api public
def schema(keys_or_map, meta = EMPTY_HASH)
if keys_or_map.is_a?(::Array)
keys = keys_or_map
else
keys = build_keys(keys_or_map)
end
Schema.new(primitive, keys: keys, **options, meta: self.meta.merge(meta))
end
# Build a map type
#
# @param [Type] key_type
# @param [Type] value_type
#
# @return [Map]
#
# @api public
def map(key_type, value_type)
Map.new(
primitive,
key_type: resolve_type(key_type),
value_type: resolve_type(value_type),
meta: meta
)
end
# @api private
def weak(*)
raise 'Support for old hash schemas was removed, please refer to the CHANGELOG '\
'on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
end
alias_method :permissive, :weak
alias_method :strict, :weak
alias_method :strict_with_defaults, :weak
alias_method :symbolized, :weak
# Injects a type transformation function for building schemas
#
# @param [#call,nil] proc
# @param [#call,nil] block
#
# @return [Hash]
#
# @api public
def with_type_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(type_transform_fn: handle)
end
# @api private
def constructor_type
::Dry::Types::Hash::Constructor
end
# Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
#
# @return [Boolean]
#
# @api public
def transform_types?
!options[:type_transform_fn].nil?
end
# @param meta [Boolean] Whether to dump the meta to the AST
#
# @return [Array] An AST representation
#
# @api public
def to_ast(meta: true)
opts = if RUBY_VERSION >= '2.5'
options.slice(:type_transform_fn)
else
options.select { |k, _| k == :type_transform_fn }
end
[:hash, [opts, meta ? self.meta : EMPTY_HASH]]
end
private
# @api private
def build_keys(type_map)
type_fn = options.fetch(:type_transform_fn, Schema::NO_TRANSFORM)
type_transform = Dry::Types::FnContainer[type_fn]
type_map.map do |map_key, type|
name, options = key_name(map_key)
key = Schema::Key.new(resolve_type(type), name, **options)
type_transform.(key)
end
end
# @api private
def resolve_type(type)
case type
when Type then type
when ::Class, ::String then Types[type]
else type
end
end
# @api private
def key_name(key)
if key.to_s.end_with?('?')
[key.to_s.chop.to_sym, NOT_REQUIRED]
else
[key, EMPTY_HASH]
end
end
end
end
end
require 'dry/types/schema/key'
require 'dry/types/schema'
|