dry-types 0.13.2 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +763 -233
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +28 -28
- data/lib/dry-types.rb +3 -1
- data/lib/dry/types.rb +156 -76
- data/lib/dry/types/any.rb +32 -12
- data/lib/dry/types/array.rb +19 -6
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +75 -16
- data/lib/dry/types/builder.rb +131 -15
- data/lib/dry/types/builder_methods.rb +49 -20
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +118 -31
- data/lib/dry/types/compat.rb +0 -2
- data/lib/dry/types/compiler.rb +56 -41
- data/lib/dry/types/constrained.rb +81 -32
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor.rb +127 -54
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +7 -0
- data/lib/dry/types/core.rb +54 -21
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +43 -20
- data/lib/dry/types/errors.rb +75 -9
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/extensions/maybe.rb +74 -16
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash.rb +86 -67
- data/lib/dry/types/hash/constructor.rb +33 -0
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +76 -33
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +120 -0
- data/lib/dry/types/nominal.rb +210 -0
- data/lib/dry/types/options.rb +13 -26
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/printable.rb +16 -0
- data/lib/dry/types/printer.rb +315 -0
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema.rb +408 -0
- data/lib/dry/types/schema/key.rb +156 -0
- data/lib/dry/types/spec/types.rb +103 -33
- data/lib/dry/types/sum.rb +84 -35
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- metadata +68 -79
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -24
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/compat/form_types.rb +0 -27
- data/lib/dry/types/compat/int.rb +0 -14
- data/lib/dry/types/definition.rb +0 -113
- data/lib/dry/types/hash/schema.rb +0 -199
- data/lib/dry/types/hash/schema_builder.rb +0 -75
- data/lib/dry/types/safe.rb +0 -59
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/monads/result"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
# Monad extension for Result
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Result
|
11
|
+
include Dry::Monads::Result::Mixin
|
12
|
+
|
13
|
+
# Turn result into a monad
|
14
|
+
#
|
15
|
+
# This makes result objects work with dry-monads (or anything with a compatible interface)
|
16
|
+
#
|
17
|
+
# @return [Dry::Monads::Success,Dry::Monads::Failure]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def to_monad
|
21
|
+
if success?
|
22
|
+
Success(input)
|
23
|
+
else
|
24
|
+
Failure([error, input])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/container"
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
7
|
+
# Internal container for constructor functions used by the built-in types
|
8
|
+
#
|
9
|
+
# @api private
|
5
10
|
class FnContainer
|
6
11
|
# @api private
|
7
12
|
def self.container
|
data/lib/dry/types/hash.rb
CHANGED
@@ -1,34 +1,44 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/hash/constructor"
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
# Hash types can be used to define maps and schemas
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Hash < Nominal
|
11
|
+
NOT_REQUIRED = {required: false}.freeze
|
12
|
+
|
13
|
+
# @overload schema(type_map, meta = EMPTY_HASH)
|
14
|
+
# @param [{Symbol => Dry::Types::Nominal}] type_map
|
15
|
+
# @param [Hash] meta
|
16
|
+
# @return [Dry::Types::Schema]
|
17
|
+
#
|
18
|
+
# @overload schema(keys)
|
19
|
+
# @param [Array<Dry::Types::Schema::Key>] key List of schema keys
|
20
|
+
# @param [Hash] meta
|
21
|
+
# @return [Dry::Types::Schema]
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def schema(keys_or_map, meta = EMPTY_HASH)
|
25
|
+
if keys_or_map.is_a?(::Array)
|
26
|
+
keys = keys_or_map
|
16
27
|
else
|
17
|
-
|
18
|
-
primitive,
|
19
|
-
**options,
|
20
|
-
member_types: member_types,
|
21
|
-
meta: meta,
|
22
|
-
hash_type: constructor
|
23
|
-
)
|
28
|
+
keys = build_keys(keys_or_map)
|
24
29
|
end
|
30
|
+
|
31
|
+
Schema.new(primitive, keys: keys, **options, meta: self.meta.merge(meta))
|
25
32
|
end
|
26
33
|
|
27
34
|
# Build a map type
|
28
35
|
#
|
29
36
|
# @param [Type] key_type
|
30
37
|
# @param [Type] value_type
|
38
|
+
#
|
31
39
|
# @return [Map]
|
40
|
+
#
|
41
|
+
# @api public
|
32
42
|
def map(key_type, value_type)
|
33
43
|
Map.new(
|
34
44
|
primitive,
|
@@ -38,81 +48,90 @@ module Dry
|
|
38
48
|
)
|
39
49
|
end
|
40
50
|
|
41
|
-
# @param [{Symbol => Definition}] type_map
|
42
|
-
# @return [Schema]
|
43
|
-
def weak(type_map)
|
44
|
-
schema(type_map, :weak)
|
45
|
-
end
|
46
|
-
|
47
|
-
# @param [{Symbol => Definition}] type_map
|
48
|
-
# @return [Schema]
|
49
|
-
def permissive(type_map)
|
50
|
-
schema(type_map, :permissive)
|
51
|
-
end
|
52
|
-
|
53
|
-
# @param [{Symbol => Definition}] type_map
|
54
|
-
# @return [Schema]
|
55
|
-
def strict(type_map)
|
56
|
-
schema(type_map, :strict)
|
57
|
-
end
|
58
|
-
|
59
|
-
# @param [{Symbol => Definition}] type_map
|
60
|
-
# @return [Schema]
|
61
|
-
def strict_with_defaults(type_map)
|
62
|
-
schema(type_map, :strict_with_defaults)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param [{Symbol => Definition}] type_map
|
66
|
-
# @return [Schema]
|
67
|
-
def symbolized(type_map)
|
68
|
-
schema(type_map, :symbolized)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Build a schema from an AST
|
72
51
|
# @api private
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
SCHEMA_BUILDER.instantiate(primitive, **options, member_types: member_types)
|
52
|
+
def weak(*)
|
53
|
+
raise "Support for old hash schemas was removed, please refer to the CHANGELOG "\
|
54
|
+
"on how to proceed with the new API https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
|
77
55
|
end
|
56
|
+
alias_method :permissive, :weak
|
57
|
+
alias_method :strict, :weak
|
58
|
+
alias_method :strict_with_defaults, :weak
|
59
|
+
alias_method :symbolized, :weak
|
78
60
|
|
79
61
|
# Injects a type transformation function for building schemas
|
62
|
+
#
|
80
63
|
# @param [#call,nil] proc
|
81
64
|
# @param [#call,nil] block
|
65
|
+
#
|
82
66
|
# @return [Hash]
|
67
|
+
#
|
68
|
+
# @api public
|
83
69
|
def with_type_transform(proc = nil, &block)
|
84
70
|
fn = proc || block
|
85
71
|
|
86
|
-
if fn.nil?
|
87
|
-
raise ArgumentError, "a block or callable argument is required"
|
88
|
-
end
|
72
|
+
raise ArgumentError, "a block or callable argument is required" if fn.nil?
|
89
73
|
|
90
74
|
handle = Dry::Types::FnContainer.register(fn)
|
91
|
-
|
75
|
+
with(type_transform_fn: handle)
|
76
|
+
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
def constructor_type
|
80
|
+
::Dry::Types::Hash::Constructor
|
81
|
+
end
|
82
|
+
|
83
|
+
# Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
|
84
|
+
#
|
85
|
+
# @return [Boolean]
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
def transform_types?
|
89
|
+
!options[:type_transform_fn].nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param meta [Boolean] Whether to dump the meta to the AST
|
93
|
+
#
|
94
|
+
# @return [Array] An AST representation
|
95
|
+
#
|
96
|
+
# @api public
|
97
|
+
def to_ast(meta: true)
|
98
|
+
[:hash, [options.slice(:type_transform_fn), meta ? self.meta : EMPTY_HASH]]
|
92
99
|
end
|
93
100
|
|
94
101
|
private
|
95
102
|
|
96
103
|
# @api private
|
97
|
-
def
|
98
|
-
type_fn =
|
104
|
+
def build_keys(type_map)
|
105
|
+
type_fn = options.fetch(:type_transform_fn, Schema::NO_TRANSFORM)
|
99
106
|
type_transform = Dry::Types::FnContainer[type_fn]
|
100
107
|
|
101
|
-
type_map.
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}
|
108
|
+
type_map.map do |map_key, type|
|
109
|
+
name, options = key_name(map_key)
|
110
|
+
key = Schema::Key.new(resolve_type(type), name, **options)
|
111
|
+
type_transform.(key)
|
112
|
+
end
|
107
113
|
end
|
108
114
|
|
109
115
|
# @api private
|
110
116
|
def resolve_type(type)
|
111
117
|
case type
|
112
|
-
when
|
118
|
+
when Type then type
|
119
|
+
when ::Class, ::String then Types[type]
|
113
120
|
else type
|
114
121
|
end
|
115
122
|
end
|
123
|
+
|
124
|
+
# @api private
|
125
|
+
def key_name(key)
|
126
|
+
if key.to_s.end_with?("?")
|
127
|
+
[key.to_s.chop.to_sym, NOT_REQUIRED]
|
128
|
+
else
|
129
|
+
[key, EMPTY_HASH]
|
130
|
+
end
|
131
|
+
end
|
116
132
|
end
|
117
133
|
end
|
118
134
|
end
|
135
|
+
|
136
|
+
require "dry/types/schema/key"
|
137
|
+
require "dry/types/schema"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/constructor"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
# Hash type exposes additional APIs for working with schema hashes
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class Hash < Nominal
|
11
|
+
class Constructor < ::Dry::Types::Constructor
|
12
|
+
# @api private
|
13
|
+
def constructor_type
|
14
|
+
::Dry::Types::Hash::Constructor
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Lax]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def lax
|
21
|
+
type.lax.constructor(fn, meta: meta)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @see Dry::Types::Array#of
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def schema(*args)
|
28
|
+
type.schema(*args).constructor(fn, meta: meta)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/dry/types/inflector.rb
CHANGED
data/lib/dry/types/json.rb
CHANGED
@@ -1,33 +1,35 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/coercions/json"
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
5
|
-
register(
|
6
|
-
self[
|
7
|
+
register("json.nil") do
|
8
|
+
self["nominal.nil"].constructor(Coercions::JSON.method(:to_nil))
|
7
9
|
end
|
8
10
|
|
9
|
-
register(
|
10
|
-
self[
|
11
|
+
register("json.date") do
|
12
|
+
self["nominal.date"].constructor(Coercions::JSON.method(:to_date))
|
11
13
|
end
|
12
14
|
|
13
|
-
register(
|
14
|
-
self[
|
15
|
+
register("json.date_time") do
|
16
|
+
self["nominal.date_time"].constructor(Coercions::JSON.method(:to_date_time))
|
15
17
|
end
|
16
18
|
|
17
|
-
register(
|
18
|
-
self[
|
19
|
+
register("json.time") do
|
20
|
+
self["nominal.time"].constructor(Coercions::JSON.method(:to_time))
|
19
21
|
end
|
20
22
|
|
21
|
-
register(
|
22
|
-
self[
|
23
|
+
register("json.decimal") do
|
24
|
+
self["nominal.decimal"].constructor(Coercions::JSON.method(:to_decimal))
|
23
25
|
end
|
24
26
|
|
25
|
-
register(
|
26
|
-
self[
|
27
|
+
register("json.symbol") do
|
28
|
+
self["nominal.symbol"].constructor(Coercions::JSON.method(:to_symbol))
|
27
29
|
end
|
28
30
|
|
29
|
-
register(
|
30
|
-
|
31
|
-
|
31
|
+
register("json.array") { self["array"] }
|
32
|
+
|
33
|
+
register("json.hash") { self["hash"] }
|
32
34
|
end
|
33
35
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
require "dry/types/decorator"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Types
|
8
|
+
# Lax types rescue from type-related errors when constructors fail
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Lax
|
12
|
+
include Type
|
13
|
+
include Decorator
|
14
|
+
include Builder
|
15
|
+
include Printable
|
16
|
+
include Dry::Equalizer(:type, inspect: false, immutable: true)
|
17
|
+
|
18
|
+
undef :options, :constructor, :<<, :>>, :prepend, :append
|
19
|
+
|
20
|
+
# @param [Object] input
|
21
|
+
#
|
22
|
+
# @return [Object]
|
23
|
+
#
|
24
|
+
# @api public
|
25
|
+
def call(input)
|
26
|
+
type.call_safe(input) { |output = input| output }
|
27
|
+
end
|
28
|
+
alias_method :[], :call
|
29
|
+
alias_method :call_safe, :call
|
30
|
+
alias_method :call_unsafe, :call
|
31
|
+
|
32
|
+
# @param [Object] input
|
33
|
+
# @param [#call,nil] block
|
34
|
+
#
|
35
|
+
# @yieldparam [Failure] failure
|
36
|
+
# @yieldreturn [Result]
|
37
|
+
#
|
38
|
+
# @return [Result,Logic::Result]
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
def try(input, &block)
|
42
|
+
type.try(input, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @see Nominal#to_ast
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def to_ast(meta: true)
|
49
|
+
[:lax, type.to_ast(meta: meta)]
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Lax]
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def lax
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# @param [Object, Dry::Types::Constructor] response
|
62
|
+
#
|
63
|
+
# @return [Boolean]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def decorate?(response)
|
67
|
+
super || response.is_a?(type.constructor_type)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
extend ::Dry::Core::Deprecations[:'dry-types']
|
72
|
+
Safe = Lax
|
73
|
+
deprecate_constant(:Safe)
|
74
|
+
end
|
75
|
+
end
|
data/lib/dry/types/map.rb
CHANGED
@@ -1,91 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
|
-
|
4
|
-
|
5
|
+
# Homogeneous mapping. It describes a hash with unknown keys that match a certain type.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# type = Dry::Types['hash'].map(
|
9
|
+
# Dry::Types['integer'].constrained(gteq: 1, lteq: 10),
|
10
|
+
# Dry::Types['string']
|
11
|
+
# )
|
12
|
+
#
|
13
|
+
# type.(1 => 'right')
|
14
|
+
# # => {1 => 'right'}
|
15
|
+
#
|
16
|
+
# type.('1' => 'wrong')
|
17
|
+
# # Dry::Types::MapError: "1" violates constraints (type?(Integer, "1") AND gteq?(1, "1") AND lteq?(10, "1") failed)
|
18
|
+
#
|
19
|
+
# type.(11 => 'wrong')
|
20
|
+
# # Dry::Types::MapError: 11 violates constraints (lteq?(10, 11) failed)
|
21
|
+
#
|
22
|
+
# @api public
|
23
|
+
class Map < Nominal
|
24
|
+
def initialize(_primitive, key_type: Types["any"], value_type: Types["any"], meta: EMPTY_HASH)
|
5
25
|
super(_primitive, key_type: key_type, value_type: value_type, meta: meta)
|
6
|
-
validate_options!
|
7
26
|
end
|
8
27
|
|
9
28
|
# @return [Type]
|
29
|
+
#
|
30
|
+
# @api public
|
10
31
|
def key_type
|
11
32
|
options[:key_type]
|
12
33
|
end
|
13
34
|
|
14
35
|
# @return [Type]
|
36
|
+
#
|
37
|
+
# @api public
|
15
38
|
def value_type
|
16
39
|
options[:value_type]
|
17
40
|
end
|
18
41
|
|
19
42
|
# @return [String]
|
43
|
+
#
|
44
|
+
# @api public
|
20
45
|
def name
|
21
46
|
"Map"
|
22
47
|
end
|
23
48
|
|
24
49
|
# @param [Hash] hash
|
50
|
+
#
|
25
51
|
# @return [Hash]
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def call_unsafe(hash)
|
55
|
+
try(hash) { |failure|
|
56
|
+
raise MapError, failure.error.message
|
57
|
+
}.input
|
30
58
|
end
|
31
|
-
alias_method :[], :call
|
32
59
|
|
33
60
|
# @param [Hash] hash
|
34
|
-
#
|
35
|
-
|
36
|
-
|
61
|
+
#
|
62
|
+
# @return [Hash]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def call_safe(hash)
|
66
|
+
try(hash) { return yield }.input
|
37
67
|
end
|
38
|
-
alias_method :===, :valid?
|
39
68
|
|
40
69
|
# @param [Hash] hash
|
70
|
+
#
|
41
71
|
# @return [Result]
|
72
|
+
#
|
73
|
+
# @api public
|
42
74
|
def try(hash)
|
43
75
|
result = coerce(hash)
|
44
76
|
return result if result.success? || !block_given?
|
77
|
+
|
45
78
|
yield(result)
|
46
79
|
end
|
47
80
|
|
48
81
|
# @param meta [Boolean] Whether to dump the meta to the AST
|
82
|
+
#
|
49
83
|
# @return [Array] An AST representation
|
84
|
+
#
|
85
|
+
# @api public
|
50
86
|
def to_ast(meta: true)
|
51
87
|
[:map,
|
52
|
-
[key_type.to_ast(meta: true),
|
88
|
+
[key_type.to_ast(meta: true),
|
89
|
+
value_type.to_ast(meta: true),
|
53
90
|
meta ? self.meta : EMPTY_HASH]]
|
54
91
|
end
|
55
92
|
|
93
|
+
# @return [Boolean]
|
94
|
+
#
|
95
|
+
# @api public
|
96
|
+
def constrained?
|
97
|
+
value_type.constrained?
|
98
|
+
end
|
99
|
+
|
56
100
|
private
|
57
101
|
|
102
|
+
# @api private
|
58
103
|
def coerce(input)
|
59
|
-
|
60
|
-
|
61
|
-
|
104
|
+
unless primitive?(input)
|
105
|
+
return failure(
|
106
|
+
input, CoercionError.new("#{input.inspect} must be an instance of #{primitive}")
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
output = {}
|
111
|
+
failures = []
|
62
112
|
|
63
|
-
|
113
|
+
input.each do |k, v|
|
114
|
+
res_k = key_type.try(k)
|
115
|
+
res_v = value_type.try(v)
|
64
116
|
|
65
|
-
input.each do |k,v|
|
66
|
-
res_k = options[:key_type].try(k)
|
67
|
-
res_v = options[:value_type].try(v)
|
68
117
|
if res_k.failure?
|
69
|
-
failures <<
|
118
|
+
failures << res_k.error
|
70
119
|
elsif output.key?(res_k.input)
|
71
|
-
failures << "duplicate coerced hash key #{res_k.input.inspect}"
|
120
|
+
failures << CoercionError.new("duplicate coerced hash key #{res_k.input.inspect}")
|
72
121
|
elsif res_v.failure?
|
73
|
-
failures <<
|
122
|
+
failures << res_v.error
|
74
123
|
else
|
75
124
|
output[res_k.input] = res_v.input
|
76
125
|
end
|
77
126
|
end
|
78
127
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def validate_options!
|
85
|
-
%i(key_type value_type).each do |opt|
|
86
|
-
type = send(opt)
|
87
|
-
next if type.is_a?(Type)
|
88
|
-
raise MapError, ":#{opt} must be a #{Type}, got: #{type.inspect}"
|
128
|
+
if failures.empty?
|
129
|
+
success(output)
|
130
|
+
else
|
131
|
+
failure(input, MultipleError.new(failures))
|
89
132
|
end
|
90
133
|
end
|
91
134
|
end
|