dry-types 0.15.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +18 -2
- data/.travis.yml +10 -5
- data/.yardopts +6 -2
- data/CHANGELOG.md +186 -3
- data/Gemfile +11 -5
- data/README.md +4 -3
- data/Rakefile +4 -2
- data/benchmarks/hash_schemas.rb +10 -6
- data/benchmarks/lax_schema.rb +15 -0
- data/benchmarks/profile_invalid_input.rb +15 -0
- data/benchmarks/profile_lax_schema_valid.rb +16 -0
- data/benchmarks/profile_valid_input.rb +15 -0
- data/benchmarks/schema_valid_vs_invalid.rb +21 -0
- data/benchmarks/setup.rb +17 -0
- data/docsite/source/array-with-member.html.md +13 -0
- data/docsite/source/built-in-types.html.md +116 -0
- data/docsite/source/constraints.html.md +31 -0
- data/docsite/source/custom-types.html.md +93 -0
- data/docsite/source/default-values.html.md +91 -0
- data/docsite/source/enum.html.md +69 -0
- data/docsite/source/getting-started.html.md +57 -0
- data/docsite/source/hash-schemas.html.md +169 -0
- data/docsite/source/index.html.md +155 -0
- data/docsite/source/map.html.md +17 -0
- data/docsite/source/optional-values.html.md +96 -0
- data/docsite/source/sum.html.md +21 -0
- data/dry-types.gemspec +21 -19
- data/lib/dry-types.rb +2 -0
- data/lib/dry/types.rb +60 -17
- data/lib/dry/types/any.rb +21 -10
- data/lib/dry/types/array.rb +17 -1
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +72 -13
- data/lib/dry/types/builder.rb +49 -5
- data/lib/dry/types/builder_methods.rb +43 -16
- data/lib/dry/types/coercions.rb +84 -19
- data/lib/dry/types/coercions/json.rb +22 -3
- data/lib/dry/types/coercions/params.rb +98 -30
- data/lib/dry/types/compiler.rb +35 -12
- data/lib/dry/types/constrained.rb +78 -27
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constraints.rb +15 -1
- data/lib/dry/types/constructor.rb +77 -62
- data/lib/dry/types/constructor/function.rb +200 -0
- data/lib/dry/types/container.rb +5 -0
- data/lib/dry/types/core.rb +35 -14
- data/lib/dry/types/decorator.rb +37 -10
- data/lib/dry/types/default.rb +48 -16
- data/lib/dry/types/enum.rb +31 -16
- data/lib/dry/types/errors.rb +73 -7
- data/lib/dry/types/extensions.rb +6 -0
- data/lib/dry/types/extensions/maybe.rb +52 -5
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/fn_container.rb +5 -0
- data/lib/dry/types/hash.rb +32 -14
- data/lib/dry/types/hash/constructor.rb +16 -3
- data/lib/dry/types/inflector.rb +2 -0
- data/lib/dry/types/json.rb +7 -5
- data/lib/dry/types/{safe.rb → lax.rb} +33 -16
- data/lib/dry/types/map.rb +70 -32
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +10 -5
- data/lib/dry/types/nominal.rb +105 -14
- data/lib/dry/types/options.rb +12 -25
- data/lib/dry/types/params.rb +14 -3
- data/lib/dry/types/predicate_inferrer.rb +197 -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 +5 -1
- data/lib/dry/types/printer.rb +70 -64
- data/lib/dry/types/result.rb +26 -0
- data/lib/dry/types/schema.rb +177 -80
- data/lib/dry/types/schema/key.rb +48 -35
- data/lib/dry/types/spec/types.rb +43 -6
- data/lib/dry/types/sum.rb +70 -21
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- metadata +91 -62
data/lib/dry/types/extensions.rb
CHANGED
@@ -1,31 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/monads/maybe'
|
2
4
|
require 'dry/types/decorator'
|
3
5
|
|
4
6
|
module Dry
|
5
7
|
module Types
|
8
|
+
# Maybe extension provides Maybe types where values are wrapped using `Either` monad
|
9
|
+
#
|
10
|
+
# @api public
|
6
11
|
class Maybe
|
7
12
|
include Type
|
8
13
|
include Dry::Equalizer(:type, :options, inspect: false)
|
9
14
|
include Decorator
|
10
15
|
include Builder
|
16
|
+
include Printable
|
11
17
|
include Dry::Monads::Maybe::Mixin
|
12
18
|
|
13
19
|
# @param [Dry::Monads::Maybe, Object] input
|
20
|
+
#
|
21
|
+
# @return [Dry::Monads::Maybe]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
def call_unsafe(input = Undefined)
|
25
|
+
case input
|
26
|
+
when Dry::Monads::Maybe
|
27
|
+
input
|
28
|
+
when Undefined
|
29
|
+
None()
|
30
|
+
else
|
31
|
+
Maybe(type.call_unsafe(input))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param [Dry::Monads::Maybe, Object] input
|
36
|
+
#
|
14
37
|
# @return [Dry::Monads::Maybe]
|
15
|
-
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def call_safe(input = Undefined, &block)
|
16
41
|
case input
|
17
42
|
when Dry::Monads::Maybe
|
18
43
|
input
|
19
44
|
when Undefined
|
20
45
|
None()
|
21
46
|
else
|
22
|
-
Maybe(type
|
47
|
+
Maybe(type.call_safe(input, &block))
|
23
48
|
end
|
24
49
|
end
|
25
|
-
alias_method :[], :call
|
26
50
|
|
27
51
|
# @param [Object] input
|
52
|
+
#
|
28
53
|
# @return [Result::Success]
|
54
|
+
#
|
55
|
+
# @api public
|
29
56
|
def try(input = Undefined)
|
30
57
|
res = if input.equal?(Undefined)
|
31
58
|
None()
|
@@ -37,16 +64,22 @@ module Dry
|
|
37
64
|
end
|
38
65
|
|
39
66
|
# @return [true]
|
67
|
+
#
|
68
|
+
# @api public
|
40
69
|
def default?
|
41
70
|
true
|
42
71
|
end
|
43
72
|
|
44
73
|
# @param [Object] value
|
74
|
+
#
|
45
75
|
# @see Dry::Types::Builder#default
|
76
|
+
#
|
46
77
|
# @raise [ArgumentError] if nil provided as default value
|
78
|
+
#
|
79
|
+
# @api public
|
47
80
|
def default(value)
|
48
81
|
if value.nil?
|
49
|
-
raise ArgumentError,
|
82
|
+
raise ArgumentError, 'nil cannot be used as a default of a maybe type'
|
50
83
|
else
|
51
84
|
super
|
52
85
|
end
|
@@ -54,18 +87,32 @@ module Dry
|
|
54
87
|
end
|
55
88
|
|
56
89
|
module Builder
|
90
|
+
# Turn a type into a maybe type
|
91
|
+
#
|
57
92
|
# @return [Maybe]
|
93
|
+
#
|
94
|
+
# @api public
|
58
95
|
def maybe
|
59
96
|
Maybe.new(Types['strict.nil'] | self)
|
60
97
|
end
|
61
98
|
end
|
62
99
|
|
100
|
+
# @api private
|
101
|
+
class Schema::Key
|
102
|
+
# @api private
|
103
|
+
def maybe
|
104
|
+
__new__(type.maybe)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @api private
|
63
109
|
class Printer
|
64
110
|
MAPPING[Maybe] = :visit_maybe
|
65
111
|
|
112
|
+
# @api private
|
66
113
|
def visit_maybe(maybe)
|
67
114
|
visit(maybe.type) do |type|
|
68
|
-
yield "Maybe<#{
|
115
|
+
yield "Maybe<#{type}>"
|
69
116
|
end
|
70
117
|
end
|
71
118
|
end
|
@@ -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
|
data/lib/dry/types/hash.rb
CHANGED
@@ -1,18 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/types/hash/constructor'
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
7
|
+
# Hash types can be used to define maps and schemas
|
8
|
+
#
|
9
|
+
# @api public
|
5
10
|
class Hash < Nominal
|
6
11
|
NOT_REQUIRED = { required: false }.freeze
|
7
12
|
|
8
|
-
# @overload
|
13
|
+
# @overload schema(type_map, meta = EMPTY_HASH)
|
9
14
|
# @param [{Symbol => Dry::Types::Nominal}] type_map
|
10
15
|
# @param [Hash] meta
|
11
16
|
# @return [Dry::Types::Schema]
|
17
|
+
#
|
12
18
|
# @overload schema(keys)
|
13
19
|
# @param [Array<Dry::Types::Schema::Key>] key List of schema keys
|
14
20
|
# @param [Hash] meta
|
15
21
|
# @return [Dry::Types::Schema]
|
22
|
+
#
|
23
|
+
# @api public
|
16
24
|
def schema(keys_or_map, meta = EMPTY_HASH)
|
17
25
|
if keys_or_map.is_a?(::Array)
|
18
26
|
keys = keys_or_map
|
@@ -27,7 +35,10 @@ module Dry
|
|
27
35
|
#
|
28
36
|
# @param [Type] key_type
|
29
37
|
# @param [Type] value_type
|
38
|
+
#
|
30
39
|
# @return [Map]
|
40
|
+
#
|
41
|
+
# @api public
|
31
42
|
def map(key_type, value_type)
|
32
43
|
Map.new(
|
33
44
|
primitive,
|
@@ -37,11 +48,10 @@ module Dry
|
|
37
48
|
)
|
38
49
|
end
|
39
50
|
|
40
|
-
# @
|
41
|
-
# @return [Schema]
|
51
|
+
# @api private
|
42
52
|
def weak(*)
|
43
|
-
raise
|
44
|
-
|
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'
|
45
55
|
end
|
46
56
|
alias_method :permissive, :weak
|
47
57
|
alias_method :strict, :weak
|
@@ -49,15 +59,17 @@ module Dry
|
|
49
59
|
alias_method :symbolized, :weak
|
50
60
|
|
51
61
|
# Injects a type transformation function for building schemas
|
62
|
+
#
|
52
63
|
# @param [#call,nil] proc
|
53
64
|
# @param [#call,nil] block
|
65
|
+
#
|
54
66
|
# @return [Hash]
|
67
|
+
#
|
68
|
+
# @api public
|
55
69
|
def with_type_transform(proc = nil, &block)
|
56
70
|
fn = proc || block
|
57
71
|
|
58
|
-
if fn.nil?
|
59
|
-
raise ArgumentError, "a block or callable argument is required"
|
60
|
-
end
|
72
|
+
raise ArgumentError, 'a block or callable argument is required' if fn.nil?
|
61
73
|
|
62
74
|
handle = Dry::Types::FnContainer.register(fn)
|
63
75
|
with(type_transform_fn: handle)
|
@@ -69,20 +81,25 @@ module Dry
|
|
69
81
|
end
|
70
82
|
|
71
83
|
# Whether the type transforms types of schemas created by {Dry::Types::Hash#schema}
|
84
|
+
#
|
72
85
|
# @return [Boolean]
|
86
|
+
#
|
73
87
|
# @api public
|
74
88
|
def transform_types?
|
75
89
|
!options[:type_transform_fn].nil?
|
76
90
|
end
|
77
91
|
|
78
92
|
# @param meta [Boolean] Whether to dump the meta to the AST
|
93
|
+
#
|
79
94
|
# @return [Array] An AST representation
|
95
|
+
#
|
96
|
+
# @api public
|
80
97
|
def to_ast(meta: true)
|
81
|
-
if RUBY_VERSION >=
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
86
103
|
|
87
104
|
[:hash, [opts, meta ? self.meta : EMPTY_HASH]]
|
88
105
|
end
|
@@ -104,7 +121,8 @@ module Dry
|
|
104
121
|
# @api private
|
105
122
|
def resolve_type(type)
|
106
123
|
case type
|
107
|
-
when
|
124
|
+
when Type then type
|
125
|
+
when ::Class, ::String then Types[type]
|
108
126
|
else type
|
109
127
|
end
|
110
128
|
end
|
@@ -1,7 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/types/constructor'
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
7
|
+
# Hash type exposes additional APIs for working with schema hashes
|
8
|
+
#
|
9
|
+
# @api public
|
5
10
|
class Hash < Nominal
|
6
11
|
class Constructor < ::Dry::Types::Constructor
|
7
12
|
# @api private
|
@@ -9,10 +14,18 @@ module Dry
|
|
9
14
|
::Dry::Types::Hash::Constructor
|
10
15
|
end
|
11
16
|
|
12
|
-
|
17
|
+
# @return [Lax]
|
18
|
+
#
|
19
|
+
# @api public
|
20
|
+
def lax
|
21
|
+
type.lax.constructor(fn, meta: meta)
|
22
|
+
end
|
13
23
|
|
14
|
-
|
15
|
-
|
24
|
+
# @see Dry::Types::Array#of
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
def schema(*args)
|
28
|
+
type.schema(*args).constructor(fn, meta: meta)
|
16
29
|
end
|
17
30
|
end
|
18
31
|
end
|
data/lib/dry/types/inflector.rb
CHANGED
data/lib/dry/types/json.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/types/coercions/json'
|
2
4
|
|
3
5
|
module Dry
|
@@ -22,12 +24,12 @@ module Dry
|
|
22
24
|
self['nominal.decimal'].constructor(Coercions::JSON.method(:to_decimal))
|
23
25
|
end
|
24
26
|
|
25
|
-
register('json.
|
26
|
-
self['nominal.
|
27
|
+
register('json.symbol') do
|
28
|
+
self['nominal.symbol'].constructor(Coercions::JSON.method(:to_symbol))
|
27
29
|
end
|
28
30
|
|
29
|
-
register('json.
|
30
|
-
|
31
|
-
|
31
|
+
register('json.array') { self['array'] }
|
32
|
+
|
33
|
+
register('json.hash') { self['hash'] }
|
32
34
|
end
|
33
35
|
end
|
@@ -1,61 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/core/deprecations'
|
1
4
|
require 'dry/types/decorator'
|
2
5
|
|
3
6
|
module Dry
|
4
7
|
module Types
|
5
|
-
|
8
|
+
# Lax types rescue from type-related errors when constructors fail
|
9
|
+
#
|
10
|
+
# @api public
|
11
|
+
class Lax
|
6
12
|
include Type
|
7
13
|
include Decorator
|
8
14
|
include Builder
|
9
15
|
include Printable
|
10
16
|
include Dry::Equalizer(:type, inspect: false)
|
11
17
|
|
12
|
-
|
18
|
+
undef :options, :constructor
|
13
19
|
|
14
20
|
# @param [Object] input
|
21
|
+
#
|
15
22
|
# @return [Object]
|
23
|
+
#
|
24
|
+
# @api public
|
16
25
|
def call(input)
|
17
|
-
|
18
|
-
|
19
|
-
if result.respond_to?(:input)
|
20
|
-
result.input
|
21
|
-
else
|
22
|
-
input
|
23
|
-
end
|
26
|
+
type.call_safe(input) { |output = input| output }
|
24
27
|
end
|
25
28
|
alias_method :[], :call
|
29
|
+
alias_method :call_safe, :call
|
30
|
+
alias_method :call_unsafe, :call
|
26
31
|
|
27
32
|
# @param [Object] input
|
28
33
|
# @param [#call,nil] block
|
34
|
+
#
|
29
35
|
# @yieldparam [Failure] failure
|
30
36
|
# @yieldreturn [Result]
|
37
|
+
#
|
31
38
|
# @return [Result,Logic::Result]
|
39
|
+
#
|
40
|
+
# @api public
|
32
41
|
def try(input, &block)
|
33
42
|
type.try(input, &block)
|
34
|
-
rescue
|
43
|
+
rescue CoercionError => e
|
35
44
|
result = failure(input, e.message)
|
36
45
|
block ? yield(result) : result
|
37
46
|
end
|
38
47
|
|
39
|
-
# @api public
|
40
|
-
#
|
41
48
|
# @see Nominal#to_ast
|
49
|
+
#
|
50
|
+
# @api public
|
42
51
|
def to_ast(meta: true)
|
43
|
-
[:
|
52
|
+
[:lax, type.to_ast(meta: meta)]
|
44
53
|
end
|
45
54
|
|
55
|
+
# @return [Lax]
|
56
|
+
#
|
46
57
|
# @api public
|
47
|
-
|
48
|
-
def safe
|
58
|
+
def lax
|
49
59
|
self
|
50
60
|
end
|
51
61
|
|
52
62
|
private
|
53
63
|
|
54
64
|
# @param [Object, Dry::Types::Constructor] response
|
65
|
+
#
|
55
66
|
# @return [Boolean]
|
67
|
+
#
|
68
|
+
# @api private
|
56
69
|
def decorate?(response)
|
57
|
-
super || response.
|
70
|
+
super || response.is_a?(type.constructor_type)
|
58
71
|
end
|
59
72
|
end
|
73
|
+
|
74
|
+
extend ::Dry::Core::Deprecations[:'dry-types']
|
75
|
+
Safe = Lax
|
76
|
+
deprecate_constant(:Safe)
|
60
77
|
end
|
61
78
|
end
|