dry-types 0.15.0 → 1.2.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/.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/array.rb
CHANGED
@@ -1,10 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/types/array/member'
|
4
|
+
require 'dry/types/array/constructor'
|
2
5
|
|
3
6
|
module Dry
|
4
7
|
module Types
|
8
|
+
# Array type can be used to define an array with optional member type
|
9
|
+
#
|
10
|
+
# @api public
|
5
11
|
class Array < Nominal
|
6
|
-
#
|
12
|
+
# Build an array type with a member type
|
13
|
+
#
|
14
|
+
# @param [Type,#call] type
|
15
|
+
#
|
7
16
|
# @return [Array::Member]
|
17
|
+
#
|
18
|
+
# @api public
|
8
19
|
def of(type)
|
9
20
|
member =
|
10
21
|
case type
|
@@ -14,6 +25,11 @@ module Dry
|
|
14
25
|
|
15
26
|
Array::Member.new(primitive, **options, member: member)
|
16
27
|
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
def constructor_type
|
31
|
+
::Dry::Types::Array::Constructor
|
32
|
+
end
|
17
33
|
end
|
18
34
|
end
|
19
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,46 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry/types/array/constructor'
|
4
|
+
|
1
5
|
module Dry
|
2
6
|
module Types
|
3
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
|
19
|
+
#
|
20
|
+
# @api private
|
11
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
|
@@ -49,9 +94,18 @@ module Dry
|
|
49
94
|
end
|
50
95
|
end
|
51
96
|
|
52
|
-
#
|
97
|
+
# Build a lax type
|
53
98
|
#
|
99
|
+
# @return [Lax]
|
100
|
+
#
|
101
|
+
# @api public
|
102
|
+
def lax
|
103
|
+
Lax.new(Member.new(primitive, { **options, member: member.lax, meta: meta }))
|
104
|
+
end
|
105
|
+
|
54
106
|
# @see Nominal#to_ast
|
107
|
+
#
|
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,43 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'dry/core/deprecations'
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
7
|
+
# Common API for building types and composition
|
8
|
+
#
|
9
|
+
# @api public
|
5
10
|
module Builder
|
6
11
|
include Dry::Core::Constants
|
7
12
|
|
8
13
|
# @return [Class]
|
14
|
+
#
|
15
|
+
# @api private
|
9
16
|
def constrained_type
|
10
17
|
Constrained
|
11
18
|
end
|
12
19
|
|
13
20
|
# @return [Class]
|
21
|
+
#
|
22
|
+
# @api private
|
14
23
|
def constructor_type
|
15
24
|
Constructor
|
16
25
|
end
|
17
26
|
|
27
|
+
# Compose two types into a Sum type
|
28
|
+
#
|
18
29
|
# @param [Type] other
|
30
|
+
#
|
19
31
|
# @return [Sum, Sum::Constrained]
|
32
|
+
#
|
33
|
+
# @api private
|
20
34
|
def |(other)
|
21
35
|
klass = constrained? && other.constrained? ? Sum::Constrained : Sum
|
22
36
|
klass.new(self, other)
|
23
37
|
end
|
24
38
|
|
39
|
+
# Turn a type into an optional type
|
40
|
+
#
|
25
41
|
# @return [Sum]
|
42
|
+
#
|
43
|
+
# @api public
|
26
44
|
def optional
|
27
45
|
Types['strict.nil'] | self
|
28
46
|
end
|
29
47
|
|
48
|
+
# Turn a type into a constrained type
|
49
|
+
#
|
30
50
|
# @param [Hash] options constraining rule (see {Types.Rule})
|
51
|
+
#
|
31
52
|
# @return [Constrained]
|
53
|
+
#
|
54
|
+
# @api public
|
32
55
|
def constrained(options)
|
33
56
|
constrained_type.new(self, rule: Types.Rule(options))
|
34
57
|
end
|
35
58
|
|
59
|
+
# Turn a type into a type with a default value
|
60
|
+
#
|
36
61
|
# @param [Object] input
|
37
62
|
# @param [Hash] options
|
38
63
|
# @param [#call,nil] block
|
64
|
+
#
|
39
65
|
# @raise [ConstraintError]
|
66
|
+
#
|
40
67
|
# @return [Default]
|
68
|
+
#
|
69
|
+
# @api public
|
41
70
|
def default(input = Undefined, options = EMPTY_HASH, &block)
|
42
71
|
unless input.frozen? || options[:shared]
|
43
72
|
where = Dry::Core::Deprecations::STACK.()
|
@@ -46,7 +75,7 @@ module Dry
|
|
46
75
|
' Be careful: types will return the same instance of the default'\
|
47
76
|
' value every time. Call `.freeze` when setting the default'\
|
48
77
|
' or pass `shared: true` to discard this warning.'\
|
49
|
-
"\n#{
|
78
|
+
"\n#{where}",
|
50
79
|
tag: :'dry-types'
|
51
80
|
)
|
52
81
|
end
|
@@ -60,8 +89,13 @@ module Dry
|
|
60
89
|
end
|
61
90
|
end
|
62
91
|
|
92
|
+
# Define an enum on top of the existing type
|
93
|
+
#
|
63
94
|
# @param [Array] values
|
95
|
+
#
|
64
96
|
# @return [Enum]
|
97
|
+
#
|
98
|
+
# @api public
|
65
99
|
def enum(*values)
|
66
100
|
mapping =
|
67
101
|
if values.length == 1 && values[0].is_a?(::Hash)
|
@@ -73,15 +107,25 @@ module Dry
|
|
73
107
|
Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
|
74
108
|
end
|
75
109
|
|
76
|
-
#
|
77
|
-
|
78
|
-
|
110
|
+
# Turn a type into a lax type that will rescue from type-errors and
|
111
|
+
# return the original input
|
112
|
+
#
|
113
|
+
# @return [Lax]
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
def lax
|
117
|
+
Lax.new(self)
|
79
118
|
end
|
80
119
|
|
120
|
+
# Define a constructor for the type
|
121
|
+
#
|
81
122
|
# @param [#call,nil] constructor
|
82
123
|
# @param [Hash] options
|
83
124
|
# @param [#call,nil] block
|
125
|
+
#
|
84
126
|
# @return [Constructor]
|
127
|
+
#
|
128
|
+
# @api public
|
85
129
|
def constructor(constructor = nil, **options, &block)
|
86
130
|
constructor_type.new(with(options), fn: constructor || block)
|
87
131
|
end
|
@@ -92,5 +136,5 @@ end
|
|
92
136
|
require 'dry/types/default'
|
93
137
|
require 'dry/types/constrained'
|
94
138
|
require 'dry/types/enum'
|
95
|
-
require 'dry/types/
|
139
|
+
require 'dry/types/lax'
|
96
140
|
require 'dry/types/sum'
|
@@ -1,5 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
5
|
+
# Common API for building type objects in a convenient way
|
6
|
+
#
|
7
|
+
# rubocop:disable Naming/MethodName
|
8
|
+
#
|
9
|
+
# @api public
|
3
10
|
module BuilderMethods
|
4
11
|
# @api private
|
5
12
|
def included(base)
|
@@ -8,7 +15,8 @@ module Dry
|
|
8
15
|
end
|
9
16
|
|
10
17
|
# Build an array type.
|
11
|
-
#
|
18
|
+
#
|
19
|
+
# Shortcut for Array#of.
|
12
20
|
#
|
13
21
|
# @example
|
14
22
|
# Types::Strings = Types.Array(Types::String)
|
@@ -17,7 +25,7 @@ module Dry
|
|
17
25
|
#
|
18
26
|
# @return [Dry::Types::Array]
|
19
27
|
def Array(type)
|
20
|
-
|
28
|
+
Strict(::Array).of(type)
|
21
29
|
end
|
22
30
|
|
23
31
|
# Build a hash schema
|
@@ -25,9 +33,8 @@ module Dry
|
|
25
33
|
# @param [Hash{Symbol => Dry::Types::Type}] type_map
|
26
34
|
#
|
27
35
|
# @return [Dry::Types::Array]
|
28
|
-
# @api public
|
29
36
|
def Hash(type_map)
|
30
|
-
|
37
|
+
Strict(::Hash).schema(type_map)
|
31
38
|
end
|
32
39
|
|
33
40
|
# Build a type which values are instances of a given class
|
@@ -41,9 +48,8 @@ module Dry
|
|
41
48
|
# @param [Class,Module] klass Class or module
|
42
49
|
#
|
43
50
|
# @return [Dry::Types::Type]
|
44
|
-
# @api public
|
45
51
|
def Instance(klass)
|
46
|
-
Nominal
|
52
|
+
Nominal(klass).constrained(type: klass)
|
47
53
|
end
|
48
54
|
alias_method :Strict, :Instance
|
49
55
|
|
@@ -53,9 +59,8 @@ module Dry
|
|
53
59
|
# @param [Object] value
|
54
60
|
#
|
55
61
|
# @return [Dry::Types::Type]
|
56
|
-
# @api public
|
57
62
|
def Value(value)
|
58
|
-
Nominal
|
63
|
+
Nominal(value.class).constrained(eql: value)
|
59
64
|
end
|
60
65
|
|
61
66
|
# Build a type with a single value
|
@@ -64,9 +69,8 @@ module Dry
|
|
64
69
|
# @param [Object] object
|
65
70
|
#
|
66
71
|
# @return [Dry::Types::Type]
|
67
|
-
# @api public
|
68
72
|
def Constant(object)
|
69
|
-
Nominal
|
73
|
+
Nominal(object.class).constrained(is: object)
|
70
74
|
end
|
71
75
|
|
72
76
|
# Build a constructor type
|
@@ -77,9 +81,12 @@ module Dry
|
|
77
81
|
# @param [#call,nil] block Value constructor
|
78
82
|
#
|
79
83
|
# @return [Dry::Types::Type]
|
80
|
-
# @api public
|
81
84
|
def Constructor(klass, cons = nil, &block)
|
82
|
-
|
85
|
+
if klass.is_a?(Type)
|
86
|
+
klass.constructor(cons || block || klass.method(:new))
|
87
|
+
else
|
88
|
+
Nominal(klass).constructor(cons || block || klass.method(:new))
|
89
|
+
end
|
83
90
|
end
|
84
91
|
|
85
92
|
# Build a nominal type
|
@@ -87,9 +94,14 @@ module Dry
|
|
87
94
|
# @param [Class] klass
|
88
95
|
#
|
89
96
|
# @return [Dry::Types::Type]
|
90
|
-
# @api public
|
91
97
|
def Nominal(klass)
|
92
|
-
|
98
|
+
if klass <= ::Array
|
99
|
+
Array.new(klass)
|
100
|
+
elsif klass <= ::Hash
|
101
|
+
Hash.new(klass)
|
102
|
+
else
|
103
|
+
Nominal.new(klass)
|
104
|
+
end
|
93
105
|
end
|
94
106
|
|
95
107
|
# Build a map type
|
@@ -102,9 +114,24 @@ module Dry
|
|
102
114
|
# @param [Type] value_type Value type
|
103
115
|
#
|
104
116
|
# @return [Dry::Types::Map]
|
105
|
-
# @api public
|
106
117
|
def Map(key_type, value_type)
|
107
|
-
|
118
|
+
Nominal(::Hash).map(key_type, value_type)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Builds a constrained nominal type accepting any value that
|
122
|
+
# responds to given methods
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# Types::Callable = Types.Interface(:call)
|
126
|
+
# Types::Contact = Types.Interface(:name, :address)
|
127
|
+
#
|
128
|
+
# @param methods [Array<String, Symbol>] Method names
|
129
|
+
#
|
130
|
+
# @return [Dry::Types::Contrained]
|
131
|
+
def Interface(*methods)
|
132
|
+
methods.reduce(Types['nominal.any']) do |type, method|
|
133
|
+
type.constrained(respond_to: method)
|
134
|
+
end
|
108
135
|
end
|
109
136
|
end
|
110
137
|
end
|