/
hash.rb
143 lines (124 loc) · 3.7 KB
/
hash.rb
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'