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
data/lib/dry/types/any.rb
CHANGED
@@ -1,27 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
|
-
Any
|
5
|
+
# Any is a nominal type that defines Object as the primitive class
|
6
|
+
#
|
7
|
+
# This type is useful in places where you can't be specific about the type
|
8
|
+
# and anything is acceptable.
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class AnyClass < Nominal
|
12
|
+
def self.name
|
13
|
+
"Any"
|
14
|
+
end
|
15
|
+
|
16
|
+
# @api private
|
4
17
|
def initialize(**options)
|
5
|
-
super(::Object, options)
|
18
|
+
super(::Object, **options)
|
6
19
|
end
|
7
20
|
|
8
21
|
# @return [String]
|
22
|
+
#
|
23
|
+
# @api public
|
9
24
|
def name
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
# @param [Object] any input is valid
|
14
|
-
# @return [true]
|
15
|
-
def valid?(_)
|
16
|
-
true
|
25
|
+
"Any"
|
17
26
|
end
|
18
|
-
alias_method :===, :valid?
|
19
27
|
|
20
28
|
# @param [Hash] new_options
|
29
|
+
#
|
21
30
|
# @return [Type]
|
22
|
-
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def with(**new_options)
|
23
34
|
self.class.new(**options, meta: @meta, **new_options)
|
24
35
|
end
|
25
|
-
|
36
|
+
|
37
|
+
# @return [Array]
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def to_ast(meta: true)
|
41
|
+
[:any, meta ? self.meta : EMPTY_HASH]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Any = AnyClass.new
|
26
46
|
end
|
27
47
|
end
|
data/lib/dry/types/array.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/array/member"
|
4
|
+
require "dry/types/array/constructor"
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Types
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
# Array type can be used to define an array with optional member type
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Array < Nominal
|
12
|
+
# Build an array type with a member type
|
13
|
+
#
|
14
|
+
# @param [Type,#call] type
|
15
|
+
#
|
10
16
|
# @return [Array::Member]
|
17
|
+
#
|
18
|
+
# @api public
|
11
19
|
def of(type)
|
12
20
|
member =
|
13
21
|
case type
|
@@ -17,6 +25,11 @@ module Dry
|
|
17
25
|
|
18
26
|
Array::Member.new(primitive, **options, member: member)
|
19
27
|
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
def constructor_type
|
31
|
+
::Dry::Types::Array::Constructor
|
32
|
+
end
|
20
33
|
end
|
21
34
|
end
|
22
35
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/constructor"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
# @api public
|
8
|
+
class Array < Nominal
|
9
|
+
# @api private
|
10
|
+
class Constructor < ::Dry::Types::Constructor
|
11
|
+
# @api private
|
12
|
+
def constructor_type
|
13
|
+
::Dry::Types::Array::Constructor
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Lax]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def lax
|
20
|
+
Lax.new(type.lax.constructor(fn, meta: meta))
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Dry::Types::Array#of
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def of(member)
|
27
|
+
type.of(member).constructor(fn, meta: meta)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,57 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/array/constructor"
|
4
|
+
|
1
5
|
module Dry
|
2
6
|
module Types
|
3
|
-
class Array <
|
7
|
+
class Array < Nominal
|
8
|
+
# Member arrays define their member type that is applied to each element
|
9
|
+
#
|
10
|
+
# @api public
|
4
11
|
class Member < Array
|
5
12
|
# @return [Type]
|
6
13
|
attr_reader :member
|
7
14
|
|
8
15
|
# @param [Class] primitive
|
9
16
|
# @param [Hash] options
|
17
|
+
#
|
10
18
|
# @option options [Type] :member
|
11
|
-
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
def initialize(primitive, **options)
|
12
22
|
@member = options.fetch(:member)
|
13
23
|
super
|
14
24
|
end
|
15
25
|
|
16
26
|
# @param [Object] input
|
17
|
-
#
|
27
|
+
#
|
18
28
|
# @return [Array]
|
19
|
-
|
20
|
-
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
def call_unsafe(input)
|
32
|
+
if primitive?(input)
|
33
|
+
input.each_with_object([]) do |el, output|
|
34
|
+
coerced = member.call_unsafe(el)
|
35
|
+
|
36
|
+
output << coerced unless Undefined.equal?(coerced)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
21
41
|
end
|
22
|
-
alias_method :[], :call
|
23
42
|
|
24
|
-
# @param [
|
25
|
-
# @return [
|
26
|
-
|
27
|
-
|
43
|
+
# @param [Object] input
|
44
|
+
# @return [Array]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def call_safe(input)
|
48
|
+
if primitive?(input)
|
49
|
+
failed = false
|
50
|
+
|
51
|
+
result = input.each_with_object([]) do |el, output|
|
52
|
+
coerced = member.call_safe(el) { |out = el|
|
53
|
+
failed = true
|
54
|
+
out
|
55
|
+
}
|
56
|
+
|
57
|
+
output << coerced unless Undefined.equal?(coerced)
|
58
|
+
end
|
59
|
+
|
60
|
+
failed ? yield(result) : result
|
61
|
+
else
|
62
|
+
yield
|
63
|
+
end
|
28
64
|
end
|
29
65
|
|
30
66
|
# @param [Array, Object] input
|
31
67
|
# @param [#call,nil] block
|
68
|
+
#
|
32
69
|
# @yieldparam [Failure] failure
|
33
70
|
# @yieldreturn [Result]
|
71
|
+
#
|
34
72
|
# @return [Result,Logic::Result]
|
73
|
+
#
|
74
|
+
# @api public
|
35
75
|
def try(input, &block)
|
36
|
-
if
|
37
|
-
|
38
|
-
|
76
|
+
if primitive?(input)
|
77
|
+
output = []
|
78
|
+
|
79
|
+
result = input.map { |el| member.try(el) }
|
80
|
+
result.each do |r|
|
81
|
+
output << r.input unless Undefined.equal?(r.input)
|
82
|
+
end
|
39
83
|
|
40
84
|
if result.all?(&:success?)
|
41
85
|
success(output)
|
42
86
|
else
|
43
|
-
|
87
|
+
error = result.find(&:failure?).error
|
88
|
+
failure = failure(output, error)
|
44
89
|
block ? yield(failure) : failure
|
45
90
|
end
|
46
91
|
else
|
47
|
-
failure = failure(input, "#{input} is not an array")
|
92
|
+
failure = failure(input, CoercionError.new("#{input} is not an array"))
|
48
93
|
block ? yield(failure) : failure
|
49
94
|
end
|
50
95
|
end
|
51
96
|
|
97
|
+
# Build a lax type
|
98
|
+
#
|
99
|
+
# @return [Lax]
|
100
|
+
#
|
52
101
|
# @api public
|
102
|
+
def lax
|
103
|
+
Lax.new(Member.new(primitive, **options, member: member.lax, meta: meta))
|
104
|
+
end
|
105
|
+
|
106
|
+
# @see Nominal#to_ast
|
53
107
|
#
|
54
|
-
# @
|
108
|
+
# @api public
|
55
109
|
def to_ast(meta: true)
|
56
110
|
if member.respond_to?(:to_ast)
|
57
111
|
[:array, [member.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
@@ -59,6 +113,11 @@ module Dry
|
|
59
113
|
[:array, [member, meta ? self.meta : EMPTY_HASH]]
|
60
114
|
end
|
61
115
|
end
|
116
|
+
|
117
|
+
# @api private
|
118
|
+
def constructor_type
|
119
|
+
::Dry::Types::Array::Constructor
|
120
|
+
end
|
62
121
|
end
|
63
122
|
end
|
64
123
|
end
|
data/lib/dry/types/builder.rb
CHANGED
@@ -1,47 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/deprecations"
|
4
|
+
|
1
5
|
module Dry
|
2
6
|
module Types
|
7
|
+
# Common API for building types and composition
|
8
|
+
#
|
9
|
+
# @api public
|
3
10
|
module Builder
|
4
11
|
include Dry::Core::Constants
|
5
12
|
|
6
13
|
# @return [Class]
|
14
|
+
#
|
15
|
+
# @api private
|
7
16
|
def constrained_type
|
8
17
|
Constrained
|
9
18
|
end
|
10
19
|
|
20
|
+
# @return [Class]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def constructor_type
|
24
|
+
Constructor
|
25
|
+
end
|
26
|
+
|
27
|
+
# Compose two types into a Sum type
|
28
|
+
#
|
11
29
|
# @param [Type] other
|
30
|
+
#
|
12
31
|
# @return [Sum, Sum::Constrained]
|
32
|
+
#
|
33
|
+
# @api private
|
13
34
|
def |(other)
|
14
35
|
klass = constrained? && other.constrained? ? Sum::Constrained : Sum
|
15
36
|
klass.new(self, other)
|
16
37
|
end
|
17
38
|
|
39
|
+
# Turn a type into an optional type
|
40
|
+
#
|
18
41
|
# @return [Sum]
|
42
|
+
#
|
43
|
+
# @api public
|
19
44
|
def optional
|
20
|
-
Types[
|
45
|
+
Types["nil"] | self
|
21
46
|
end
|
22
47
|
|
48
|
+
# Turn a type into a constrained type
|
49
|
+
#
|
23
50
|
# @param [Hash] options constraining rule (see {Types.Rule})
|
51
|
+
#
|
24
52
|
# @return [Constrained]
|
53
|
+
#
|
54
|
+
# @api public
|
25
55
|
def constrained(options)
|
26
56
|
constrained_type.new(self, rule: Types.Rule(options))
|
27
57
|
end
|
28
58
|
|
59
|
+
# Turn a type into a type with a default value
|
60
|
+
#
|
29
61
|
# @param [Object] input
|
62
|
+
# @option [Boolean] shared Whether it's safe to share the value across type applications
|
30
63
|
# @param [#call,nil] block
|
64
|
+
#
|
31
65
|
# @raise [ConstraintError]
|
66
|
+
#
|
32
67
|
# @return [Default]
|
33
|
-
|
34
|
-
|
68
|
+
#
|
69
|
+
# @api public
|
70
|
+
def default(input = Undefined, options = EMPTY_HASH, &block)
|
71
|
+
unless input.frozen? || options[:shared]
|
72
|
+
where = Core::Deprecations::STACK.()
|
73
|
+
Core::Deprecations.warn(
|
74
|
+
"#{input.inspect} is mutable."\
|
75
|
+
" Be careful: types will return the same instance of the default"\
|
76
|
+
" value every time. Call `.freeze` when setting the default"\
|
77
|
+
" or pass `shared: true` to discard this warning."\
|
78
|
+
"\n#{where}",
|
79
|
+
tag: :'dry-types'
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
value = Undefined.default(input, block)
|
84
|
+
type = Default[value].new(self, value)
|
35
85
|
|
36
|
-
if
|
37
|
-
|
86
|
+
if !type.callable? && !valid?(value)
|
87
|
+
raise ConstraintError.new(
|
88
|
+
"default value #{value.inspect} violates constraints",
|
89
|
+
value
|
90
|
+
)
|
38
91
|
else
|
39
|
-
|
92
|
+
type
|
40
93
|
end
|
41
94
|
end
|
42
95
|
|
96
|
+
# Define an enum on top of the existing type
|
97
|
+
#
|
43
98
|
# @param [Array] values
|
99
|
+
#
|
44
100
|
# @return [Enum]
|
101
|
+
#
|
102
|
+
# @api public
|
45
103
|
def enum(*values)
|
46
104
|
mapping =
|
47
105
|
if values.length == 1 && values[0].is_a?(::Hash)
|
@@ -53,24 +111,82 @@ module Dry
|
|
53
111
|
Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
|
54
112
|
end
|
55
113
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
114
|
+
# Turn a type into a lax type that will rescue from type-errors and
|
115
|
+
# return the original input
|
116
|
+
#
|
117
|
+
# @return [Lax]
|
118
|
+
#
|
119
|
+
# @api public
|
120
|
+
def lax
|
121
|
+
Lax.new(self)
|
59
122
|
end
|
60
123
|
|
124
|
+
# Define a constructor for the type
|
125
|
+
#
|
61
126
|
# @param [#call,nil] constructor
|
62
127
|
# @param [Hash] options
|
63
128
|
# @param [#call,nil] block
|
129
|
+
#
|
64
130
|
# @return [Constructor]
|
131
|
+
#
|
132
|
+
# @api public
|
65
133
|
def constructor(constructor = nil, **options, &block)
|
66
|
-
|
134
|
+
constructor_type[with(**options), fn: constructor || block]
|
135
|
+
end
|
136
|
+
alias_method :append, :constructor
|
137
|
+
alias_method :prepend, :constructor
|
138
|
+
alias_method :>>, :constructor
|
139
|
+
alias_method :<<, :constructor
|
140
|
+
|
141
|
+
# Use the given value on type mismatch
|
142
|
+
#
|
143
|
+
# @param [Object] value
|
144
|
+
# @option [Boolean] shared Whether it's safe to share the value across type applications
|
145
|
+
# @param [#call,nil] fallback
|
146
|
+
#
|
147
|
+
# @return [Constructor]
|
148
|
+
#
|
149
|
+
# @api public
|
150
|
+
def fallback(value = Undefined, shared: false, &_fallback)
|
151
|
+
if Undefined.equal?(value) && !block_given?
|
152
|
+
raise ::ArgumentError, "fallback value or a block must be given"
|
153
|
+
end
|
154
|
+
|
155
|
+
if !block_given? && !valid?(value)
|
156
|
+
raise ConstraintError.new(
|
157
|
+
"fallback value #{value.inspect} violates constraints",
|
158
|
+
value
|
159
|
+
)
|
160
|
+
end
|
161
|
+
|
162
|
+
unless value.frozen? || shared
|
163
|
+
where = Core::Deprecations::STACK.()
|
164
|
+
Core::Deprecations.warn(
|
165
|
+
"#{value.inspect} is mutable."\
|
166
|
+
" Be careful: types will return the same instance of the fallback"\
|
167
|
+
" value every time. Call `.freeze` when setting the fallback"\
|
168
|
+
" or pass `shared: true` to discard this warning."\
|
169
|
+
"\n#{where}",
|
170
|
+
tag: :'dry-types'
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
constructor do |input, type, &_block|
|
175
|
+
type.(input) do |output = input|
|
176
|
+
if block_given?
|
177
|
+
yield(output)
|
178
|
+
else
|
179
|
+
value
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
67
183
|
end
|
68
184
|
end
|
69
185
|
end
|
70
186
|
end
|
71
187
|
|
72
|
-
require
|
73
|
-
require
|
74
|
-
require
|
75
|
-
require
|
76
|
-
require
|
188
|
+
require "dry/types/default"
|
189
|
+
require "dry/types/constrained"
|
190
|
+
require "dry/types/enum"
|
191
|
+
require "dry/types/lax"
|
192
|
+
require "dry/types/sum"
|