dry-types 1.0.1 → 1.1.0
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 +33 -3
- data/Gemfile +4 -3
- data/Rakefile +2 -2
- data/benchmarks/hash_schemas.rb +8 -6
- data/benchmarks/lax_schema.rb +0 -1
- data/benchmarks/profile_invalid_input.rb +1 -1
- data/benchmarks/profile_lax_schema_valid.rb +1 -1
- data/benchmarks/profile_valid_input.rb +1 -1
- data/dry-types.gemspec +19 -19
- data/lib/dry/types.rb +2 -2
- data/lib/dry/types/array.rb +6 -0
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +7 -0
- data/lib/dry/types/builder.rb +1 -1
- data/lib/dry/types/builder_methods.rb +15 -15
- data/lib/dry/types/coercions.rb +19 -6
- data/lib/dry/types/coercions/params.rb +4 -4
- data/lib/dry/types/constrained.rb +5 -0
- data/lib/dry/types/constructor.rb +3 -8
- data/lib/dry/types/constructor/function.rb +4 -5
- data/lib/dry/types/core.rb +27 -8
- data/lib/dry/types/enum.rb +1 -0
- data/lib/dry/types/extensions/maybe.rb +9 -1
- data/lib/dry/types/hash.rb +8 -10
- data/lib/dry/types/hash/constructor.rb +5 -5
- data/lib/dry/types/json.rb +4 -0
- data/lib/dry/types/lax.rb +3 -3
- data/lib/dry/types/map.rb +8 -4
- data/lib/dry/types/module.rb +3 -3
- data/lib/dry/types/nominal.rb +3 -4
- data/lib/dry/types/params.rb +4 -0
- data/lib/dry/types/printer.rb +11 -10
- data/lib/dry/types/schema.rb +14 -20
- data/lib/dry/types/schema/key.rb +19 -1
- data/lib/dry/types/spec/types.rb +3 -6
- data/lib/dry/types/version.rb +1 -1
- metadata +40 -33
@@ -71,8 +71,8 @@ module Dry
|
|
71
71
|
else
|
72
72
|
Integer(input)
|
73
73
|
end
|
74
|
-
rescue ArgumentError, TypeError =>
|
75
|
-
CoercionError.handle(
|
74
|
+
rescue ArgumentError, TypeError => e
|
75
|
+
CoercionError.handle(e, &block)
|
76
76
|
end
|
77
77
|
|
78
78
|
# @param [#to_f, Object] input
|
@@ -84,8 +84,8 @@ module Dry
|
|
84
84
|
# @api public
|
85
85
|
def self.to_float(input, &block)
|
86
86
|
Float(input)
|
87
|
-
rescue ArgumentError, TypeError =>
|
88
|
-
CoercionError.handle(
|
87
|
+
rescue ArgumentError, TypeError => e
|
88
|
+
CoercionError.handle(e, &block)
|
89
89
|
end
|
90
90
|
|
91
91
|
# @param [#to_d, Object] input
|
@@ -95,8 +95,8 @@ module Dry
|
|
95
95
|
# @api public
|
96
96
|
def try(input, &block)
|
97
97
|
value = fn.(input)
|
98
|
-
rescue CoercionError =>
|
99
|
-
failure = failure(input,
|
98
|
+
rescue CoercionError => e
|
99
|
+
failure = failure(input, e)
|
100
100
|
block_given? ? yield(failure) : failure
|
101
101
|
else
|
102
102
|
type.try(value, &block)
|
@@ -184,7 +184,7 @@ module Dry
|
|
184
184
|
if type.respond_to?(method)
|
185
185
|
response = type.__send__(method, *args, &block)
|
186
186
|
|
187
|
-
if
|
187
|
+
if response.is_a?(Type) && type.class == response.class
|
188
188
|
response.constructor_type.new(response, options)
|
189
189
|
else
|
190
190
|
response
|
@@ -193,11 +193,6 @@ module Dry
|
|
193
193
|
super
|
194
194
|
end
|
195
195
|
end
|
196
|
-
|
197
|
-
# @api private
|
198
|
-
def composable?(value)
|
199
|
-
value.is_a?(Builder)
|
200
|
-
end
|
201
196
|
end
|
202
197
|
end
|
203
198
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'concurrent/map'
|
@@ -16,8 +15,8 @@ module Dry
|
|
16
15
|
class Safe < Function
|
17
16
|
def call(input, &block)
|
18
17
|
@fn.(input, &block)
|
19
|
-
rescue NoMethodError, TypeError, ArgumentError =>
|
20
|
-
CoercionError.handle(
|
18
|
+
rescue NoMethodError, TypeError, ArgumentError => e
|
19
|
+
CoercionError.handle(e, &block)
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
@@ -91,8 +90,8 @@ module Dry
|
|
91
90
|
class PrivateSafeCall < PrivateCall
|
92
91
|
def call(input, &block)
|
93
92
|
@target.send(@name, input)
|
94
|
-
rescue NoMethodError, TypeError, ArgumentError =>
|
95
|
-
CoercionError.handle(
|
93
|
+
rescue NoMethodError, TypeError, ArgumentError => e
|
94
|
+
CoercionError.handle(e, &block)
|
96
95
|
end
|
97
96
|
end
|
98
97
|
|
data/lib/dry/types/core.rb
CHANGED
@@ -5,7 +5,7 @@ require 'dry/types/any'
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
7
7
|
# Primitives with {Kernel} coercion methods
|
8
|
-
|
8
|
+
KERNEL_COERCIBLE = {
|
9
9
|
string: String,
|
10
10
|
integer: Integer,
|
11
11
|
float: Float,
|
@@ -14,10 +14,19 @@ module Dry
|
|
14
14
|
hash: ::Hash
|
15
15
|
}.freeze
|
16
16
|
|
17
|
-
# Primitives
|
17
|
+
# Primitives with coercions through by convention `to_*` methods
|
18
|
+
METHOD_COERCIBLE = {
|
19
|
+
symbol: Symbol
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
# By convention methods to coerce {METHOD_COERCIBLE} primitives
|
23
|
+
METHOD_COERCIBLE_METHODS = {
|
24
|
+
symbol: :to_sym
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# Primitives that are non-coercible
|
18
28
|
NON_COERCIBLE = {
|
19
29
|
nil: NilClass,
|
20
|
-
symbol: Symbol,
|
21
30
|
class: Class,
|
22
31
|
true: TrueClass,
|
23
32
|
false: FalseClass,
|
@@ -28,7 +37,10 @@ module Dry
|
|
28
37
|
}.freeze
|
29
38
|
|
30
39
|
# All built-in primitives
|
31
|
-
ALL_PRIMITIVES =
|
40
|
+
ALL_PRIMITIVES = [KERNEL_COERCIBLE, METHOD_COERCIBLE, NON_COERCIBLE].reduce(&:merge).freeze
|
41
|
+
|
42
|
+
# All coercible types
|
43
|
+
COERCIBLE = KERNEL_COERCIBLE.merge(METHOD_COERCIBLE).freeze
|
32
44
|
|
33
45
|
# All built-in primitives except {NilClass}
|
34
46
|
NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
|
@@ -46,11 +58,18 @@ module Dry
|
|
46
58
|
register("strict.#{name}", type)
|
47
59
|
end
|
48
60
|
|
49
|
-
# Register {
|
50
|
-
|
61
|
+
# Register {KERNEL_COERCIBLE} types
|
62
|
+
KERNEL_COERCIBLE.each do |name, primitive|
|
51
63
|
register("coercible.#{name}", self["nominal.#{name}"].constructor(Kernel.method(primitive.name)))
|
52
64
|
end
|
53
65
|
|
66
|
+
# Register {METHOD_COERCIBLE} types
|
67
|
+
METHOD_COERCIBLE.each_key do |name|
|
68
|
+
register(
|
69
|
+
"coercible.#{name}", self["nominal.#{name}"].constructor(&METHOD_COERCIBLE_METHODS[name])
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
54
73
|
# Register optional strict {NON_NIL} types
|
55
74
|
NON_NIL.each_key do |name|
|
56
75
|
register("optional.strict.#{name}", self["strict.#{name}"].optional)
|
@@ -64,8 +83,8 @@ module Dry
|
|
64
83
|
# Register `:bool` since it's common and not a built-in Ruby type :(
|
65
84
|
register('nominal.bool', self['nominal.true'] | self['nominal.false'])
|
66
85
|
bool = self['strict.true'] | self['strict.false']
|
67
|
-
register(
|
68
|
-
register(
|
86
|
+
register('strict.bool', bool)
|
87
|
+
register('bool', bool)
|
69
88
|
|
70
89
|
register('any', Any)
|
71
90
|
register('nominal.any', Any)
|
data/lib/dry/types/enum.rb
CHANGED
@@ -79,7 +79,7 @@ module Dry
|
|
79
79
|
# @api public
|
80
80
|
def default(value)
|
81
81
|
if value.nil?
|
82
|
-
raise ArgumentError,
|
82
|
+
raise ArgumentError, 'nil cannot be used as a default of a maybe type'
|
83
83
|
else
|
84
84
|
super
|
85
85
|
end
|
@@ -97,6 +97,14 @@ module Dry
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
+
# @api private
|
101
|
+
class Schema::Key
|
102
|
+
# @api private
|
103
|
+
def maybe
|
104
|
+
__new__(type.maybe)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
100
108
|
# @api private
|
101
109
|
class Printer
|
102
110
|
MAPPING[Maybe] = :visit_maybe
|
data/lib/dry/types/hash.rb
CHANGED
@@ -50,8 +50,8 @@ module Dry
|
|
50
50
|
|
51
51
|
# @api private
|
52
52
|
def weak(*)
|
53
|
-
raise
|
54
|
-
|
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'
|
55
55
|
end
|
56
56
|
alias_method :permissive, :weak
|
57
57
|
alias_method :strict, :weak
|
@@ -69,9 +69,7 @@ module Dry
|
|
69
69
|
def with_type_transform(proc = nil, &block)
|
70
70
|
fn = proc || block
|
71
71
|
|
72
|
-
if fn.nil?
|
73
|
-
raise ArgumentError, "a block or callable argument is required"
|
74
|
-
end
|
72
|
+
raise ArgumentError, 'a block or callable argument is required' if fn.nil?
|
75
73
|
|
76
74
|
handle = Dry::Types::FnContainer.register(fn)
|
77
75
|
with(type_transform_fn: handle)
|
@@ -97,11 +95,11 @@ module Dry
|
|
97
95
|
#
|
98
96
|
# @api public
|
99
97
|
def to_ast(meta: true)
|
100
|
-
if RUBY_VERSION >=
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
98
|
+
opts = if RUBY_VERSION >= '2.5'
|
99
|
+
options.slice(:type_transform_fn)
|
100
|
+
else
|
101
|
+
options.select { |k, _| k == :type_transform_fn }
|
102
|
+
end
|
105
103
|
|
106
104
|
[:hash, [opts, meta ? self.meta : EMPTY_HASH]]
|
107
105
|
end
|
@@ -21,11 +21,11 @@ module Dry
|
|
21
21
|
type.lax.constructor(fn, meta: meta)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
# @api
|
27
|
-
def
|
28
|
-
|
24
|
+
# @see Dry::Types::Array#of
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def schema(*args)
|
28
|
+
type.schema(*args).constructor(fn, meta: meta)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/lib/dry/types/json.rb
CHANGED
@@ -24,6 +24,10 @@ module Dry
|
|
24
24
|
self['nominal.decimal'].constructor(Coercions::JSON.method(:to_decimal))
|
25
25
|
end
|
26
26
|
|
27
|
+
register('json.symbol') do
|
28
|
+
self['nominal.symbol'].constructor(Coercions::JSON.method(:to_symbol))
|
29
|
+
end
|
30
|
+
|
27
31
|
register('json.array') { self['array'] }
|
28
32
|
|
29
33
|
register('json.hash') { self['hash'] }
|
data/lib/dry/types/lax.rb
CHANGED
@@ -40,8 +40,8 @@ module Dry
|
|
40
40
|
# @api public
|
41
41
|
def try(input, &block)
|
42
42
|
type.try(input, &block)
|
43
|
-
rescue CoercionError =>
|
44
|
-
result = failure(input,
|
43
|
+
rescue CoercionError => e
|
44
|
+
result = failure(input, e.message)
|
45
45
|
block ? yield(result) : result
|
46
46
|
end
|
47
47
|
|
@@ -67,7 +67,7 @@ module Dry
|
|
67
67
|
#
|
68
68
|
# @api private
|
69
69
|
def decorate?(response)
|
70
|
-
super || response.is_a?(constructor_type)
|
70
|
+
super || response.is_a?(type.constructor_type)
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
data/lib/dry/types/map.rb
CHANGED
@@ -74,6 +74,7 @@ module Dry
|
|
74
74
|
def try(hash)
|
75
75
|
result = coerce(hash)
|
76
76
|
return result if result.success? || !block_given?
|
77
|
+
|
77
78
|
yield(result)
|
78
79
|
end
|
79
80
|
|
@@ -100,11 +101,14 @@ module Dry
|
|
100
101
|
|
101
102
|
# @api private
|
102
103
|
def coerce(input)
|
103
|
-
|
104
|
-
|
105
|
-
|
104
|
+
unless primitive?(input)
|
105
|
+
return failure(
|
106
|
+
input, CoercionError.new("#{input.inspect} must be an instance of #{primitive}")
|
107
|
+
)
|
108
|
+
end
|
106
109
|
|
107
|
-
output
|
110
|
+
output = {}
|
111
|
+
failures = []
|
108
112
|
|
109
113
|
input.each do |k, v|
|
110
114
|
res_k = key_type.try(k)
|
data/lib/dry/types/module.rb
CHANGED
@@ -31,7 +31,7 @@ module Dry
|
|
31
31
|
base.instance_exec(const_get(:Nominal, false)) do |nominal|
|
32
32
|
extend Dry::Core::Deprecations[:'dry-types']
|
33
33
|
const_set(:Definition, nominal)
|
34
|
-
deprecate_constant(:Definition, message:
|
34
|
+
deprecate_constant(:Definition, message: 'Nominal')
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -93,8 +93,8 @@ module Dry
|
|
93
93
|
|
94
94
|
(referenced.uniq - known).each do |name|
|
95
95
|
raise ArgumentError,
|
96
|
-
"#{
|
97
|
-
"Supported options are #{
|
96
|
+
"#{name.inspect} is not a known type namespace. "\
|
97
|
+
"Supported options are #{known.map(&:inspect).join(', ')}"
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
data/lib/dry/types/nominal.rb
CHANGED
@@ -126,9 +126,8 @@ module Dry
|
|
126
126
|
#
|
127
127
|
# @api public
|
128
128
|
def failure(input, error)
|
129
|
-
unless error.is_a?(CoercionError)
|
130
|
-
|
131
|
-
end
|
129
|
+
raise ArgumentError, 'error must be a CoercionError' unless error.is_a?(CoercionError)
|
130
|
+
|
132
131
|
Result::Failure.new(input, error)
|
133
132
|
end
|
134
133
|
|
@@ -202,7 +201,7 @@ module Dry
|
|
202
201
|
|
203
202
|
extend Dry::Core::Deprecations[:'dry-types']
|
204
203
|
Definition = Nominal
|
205
|
-
deprecate_constant(:Definition, message:
|
204
|
+
deprecate_constant(:Definition, message: 'Nominal')
|
206
205
|
end
|
207
206
|
end
|
208
207
|
|
data/lib/dry/types/params.rb
CHANGED
data/lib/dry/types/printer.rb
CHANGED
@@ -8,6 +8,7 @@ module Dry
|
|
8
8
|
Nominal => :visit_nominal,
|
9
9
|
Constructor => :visit_constructor,
|
10
10
|
Hash::Constructor => :visit_constructor,
|
11
|
+
Array::Constructor => :visit_constructor,
|
11
12
|
Constrained => :visit_constrained,
|
12
13
|
Constrained::Coercible => :visit_constrained,
|
13
14
|
Hash => :visit_hash,
|
@@ -26,7 +27,7 @@ module Dry
|
|
26
27
|
}
|
27
28
|
|
28
29
|
def call(type)
|
29
|
-
output =
|
30
|
+
output = ''.dup
|
30
31
|
visit(type) { |str| output << str }
|
31
32
|
"#<Dry::Types[#{output}]>"
|
32
33
|
end
|
@@ -43,11 +44,11 @@ module Dry
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def visit_any(_)
|
46
|
-
yield
|
47
|
+
yield 'Any'
|
47
48
|
end
|
48
49
|
|
49
50
|
def visit_array(_)
|
50
|
-
|
51
|
+
yield 'Array'
|
51
52
|
end
|
52
53
|
|
53
54
|
def visit_array_member(array)
|
@@ -74,7 +75,7 @@ module Dry
|
|
74
75
|
options = constrained.options.dup
|
75
76
|
rule = options.delete(:rule)
|
76
77
|
|
77
|
-
visit_options(options) do |
|
78
|
+
visit_options(options) do |_opts|
|
78
79
|
yield "Constrained<#{type} rule=[#{rule}]>"
|
79
80
|
end
|
80
81
|
end
|
@@ -110,7 +111,7 @@ module Dry
|
|
110
111
|
header = "Schema<#{schema_parameters}keys={"
|
111
112
|
|
112
113
|
if size.zero?
|
113
|
-
yield "#{
|
114
|
+
yield "#{header}}>"
|
114
115
|
else
|
115
116
|
yield header.dup << keys.map { |key|
|
116
117
|
visit(key) { |type| type }
|
@@ -126,7 +127,7 @@ module Dry
|
|
126
127
|
options.delete(:key_type)
|
127
128
|
options.delete(:value_type)
|
128
129
|
|
129
|
-
visit_options(options) do |
|
130
|
+
visit_options(options) do |_opts|
|
130
131
|
yield "Map<#{key} => #{value}>"
|
131
132
|
end
|
132
133
|
end
|
@@ -193,7 +194,7 @@ module Dry
|
|
193
194
|
yield "Enum<#{type} values={#{values}}#{opts}>"
|
194
195
|
else
|
195
196
|
mapping_str = mapping.map { |key, value|
|
196
|
-
"#{
|
197
|
+
"#{key.inspect}=>#{value.inspect}"
|
197
198
|
}.join(', ')
|
198
199
|
yield "Enum<#{type} mapping={#{mapping_str}}#{opts}>"
|
199
200
|
end
|
@@ -251,14 +252,14 @@ module Dry
|
|
251
252
|
|
252
253
|
case fn
|
253
254
|
when Method
|
254
|
-
yield "#{
|
255
|
+
yield "#{fn.receiver}.#{fn.name}"
|
255
256
|
when Proc
|
256
257
|
path, line = fn.source_location
|
257
258
|
|
258
|
-
if line
|
259
|
+
if line&.zero?
|
259
260
|
yield ".#{path}"
|
260
261
|
elsif path
|
261
|
-
yield "#{path.sub(Dir.pwd + '/', EMPTY_STRING)
|
262
|
+
yield "#{path.sub(Dir.pwd + '/', EMPTY_STRING)}:#{line}"
|
262
263
|
elsif fn.lambda?
|
263
264
|
yield '(lambda)'
|
264
265
|
else
|