dry-types 1.4.0 → 1.5.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 +68 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/dry-types.gemspec +2 -3
- data/lib/dry-types.rb +1 -1
- data/lib/dry/types.rb +55 -31
- data/lib/dry/types/any.rb +2 -2
- data/lib/dry/types/array.rb +2 -2
- data/lib/dry/types/array/constructor.rb +1 -1
- data/lib/dry/types/array/member.rb +1 -1
- data/lib/dry/types/builder.rb +66 -18
- data/lib/dry/types/builder_methods.rb +1 -2
- data/lib/dry/types/coercions/json.rb +5 -5
- data/lib/dry/types/coercions/params.rb +3 -3
- data/lib/dry/types/compiler.rb +10 -10
- data/lib/dry/types/constrained.rb +5 -9
- data/lib/dry/types/constraints.rb +3 -3
- data/lib/dry/types/constructor.rb +39 -6
- data/lib/dry/types/constructor/function.rb +31 -2
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +1 -1
- data/lib/dry/types/core.rb +12 -12
- data/lib/dry/types/decorator.rb +2 -2
- data/lib/dry/types/default.rb +13 -1
- data/lib/dry/types/enum.rb +3 -3
- data/lib/dry/types/errors.rb +1 -1
- data/lib/dry/types/extensions.rb +2 -2
- data/lib/dry/types/extensions/maybe.rb +4 -4
- data/lib/dry/types/extensions/monads.rb +1 -1
- data/lib/dry/types/fn_container.rb +1 -1
- data/lib/dry/types/hash.rb +9 -15
- data/lib/dry/types/hash/constructor.rb +1 -1
- data/lib/dry/types/inflector.rb +1 -1
- data/lib/dry/types/json.rb +15 -15
- data/lib/dry/types/lax.rb +2 -2
- data/lib/dry/types/map.rb +2 -2
- data/lib/dry/types/meta.rb +1 -1
- data/lib/dry/types/module.rb +6 -6
- data/lib/dry/types/nominal.rb +10 -11
- data/lib/dry/types/params.rb +30 -28
- data/lib/dry/types/predicate_inferrer.rb +51 -11
- data/lib/dry/types/predicate_registry.rb +1 -1
- data/lib/dry/types/primitive_inferrer.rb +1 -1
- data/lib/dry/types/printer.rb +25 -25
- data/lib/dry/types/result.rb +1 -1
- data/lib/dry/types/schema.rb +5 -13
- data/lib/dry/types/schema/key.rb +4 -4
- data/lib/dry/types/spec/types.rb +57 -45
- data/lib/dry/types/sum.rb +3 -3
- data/lib/dry/types/type.rb +1 -1
- data/lib/dry/types/version.rb +1 -1
- metadata +9 -22
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "date"
|
4
|
+
require "bigdecimal"
|
5
|
+
require "bigdecimal/util"
|
6
|
+
require "time"
|
7
7
|
|
8
8
|
module Dry
|
9
9
|
module Types
|
@@ -38,7 +38,7 @@ module Dry
|
|
38
38
|
# @raise CoercionError
|
39
39
|
#
|
40
40
|
# @api public
|
41
|
-
def self.to_decimal(input, &
|
41
|
+
def self.to_decimal(input, &_block)
|
42
42
|
if input.is_a?(::Float)
|
43
43
|
input.to_d
|
44
44
|
else
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "bigdecimal"
|
4
|
+
require "bigdecimal/util"
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Types
|
@@ -112,7 +112,7 @@ module Dry
|
|
112
112
|
# @raise CoercionError
|
113
113
|
#
|
114
114
|
# @api public
|
115
|
-
def self.to_decimal(input, &
|
115
|
+
def self.to_decimal(input, &_block)
|
116
116
|
to_float(input) do
|
117
117
|
if block_given?
|
118
118
|
return yield
|
data/lib/dry/types/compiler.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/core/deprecations"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
@@ -63,37 +63,37 @@ module Dry
|
|
63
63
|
def visit_array(node)
|
64
64
|
member, meta = node
|
65
65
|
member = member.is_a?(Class) ? member : visit(member)
|
66
|
-
registry[
|
66
|
+
registry["nominal.array"].of(member).meta(meta)
|
67
67
|
end
|
68
68
|
|
69
69
|
def visit_hash(node)
|
70
70
|
opts, meta = node
|
71
|
-
registry[
|
71
|
+
registry["nominal.hash"].with(**opts, meta: meta)
|
72
72
|
end
|
73
73
|
|
74
74
|
def visit_schema(node)
|
75
75
|
keys, options, meta = node
|
76
|
-
registry[
|
76
|
+
registry["nominal.hash"].schema(keys.map { |key| visit(key) }).with(**options, meta: meta)
|
77
77
|
end
|
78
78
|
|
79
79
|
def visit_json_hash(node)
|
80
80
|
keys, meta = node
|
81
|
-
registry[
|
81
|
+
registry["json.hash"].schema(keys.map { |key| visit(key) }, meta)
|
82
82
|
end
|
83
83
|
|
84
84
|
def visit_json_array(node)
|
85
85
|
member, meta = node
|
86
|
-
registry[
|
86
|
+
registry["json.array"].of(visit(member)).meta(meta)
|
87
87
|
end
|
88
88
|
|
89
89
|
def visit_params_hash(node)
|
90
90
|
keys, meta = node
|
91
|
-
registry[
|
91
|
+
registry["params.hash"].schema(keys.map { |key| visit(key) }, meta)
|
92
92
|
end
|
93
93
|
|
94
94
|
def visit_params_array(node)
|
95
95
|
member, meta = node
|
96
|
-
registry[
|
96
|
+
registry["params.array"].of(visit(member)).meta(meta)
|
97
97
|
end
|
98
98
|
|
99
99
|
def visit_key(node)
|
@@ -108,11 +108,11 @@ module Dry
|
|
108
108
|
|
109
109
|
def visit_map(node)
|
110
110
|
key_type, value_type, meta = node
|
111
|
-
registry[
|
111
|
+
registry["nominal.hash"].map(visit(key_type), visit(value_type)).meta(meta)
|
112
112
|
end
|
113
113
|
|
114
114
|
def visit_any(meta)
|
115
|
-
registry[
|
115
|
+
registry["any"].meta(meta)
|
116
116
|
end
|
117
117
|
|
118
118
|
def compile_fn(fn)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/types/decorator"
|
4
|
+
require "dry/types/constraints"
|
5
|
+
require "dry/types/constrained/coercible"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
module Types
|
@@ -29,11 +29,9 @@ module Dry
|
|
29
29
|
@rule = options.fetch(:rule)
|
30
30
|
end
|
31
31
|
|
32
|
-
# @api private
|
33
|
-
#
|
34
32
|
# @return [Object]
|
35
33
|
#
|
36
|
-
# @api
|
34
|
+
# @api private
|
37
35
|
def call_unsafe(input)
|
38
36
|
result = rule.(input)
|
39
37
|
|
@@ -44,11 +42,9 @@ module Dry
|
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
|
-
# @api private
|
48
|
-
#
|
49
45
|
# @return [Object]
|
50
46
|
#
|
51
|
-
# @api
|
47
|
+
# @api private
|
52
48
|
def call_safe(input, &block)
|
53
49
|
if rule[input]
|
54
50
|
type.call_safe(input, &block)
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/logic/rule_compiler"
|
4
|
+
require "dry/logic/predicates"
|
5
|
+
require "dry/logic/rule/predicate"
|
6
6
|
|
7
7
|
module Dry
|
8
8
|
# Helper methods for constraint types
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/types/fn_container"
|
4
|
+
require "dry/types/constructor/function"
|
5
|
+
require "dry/types/constructor/wrapper"
|
5
6
|
|
6
7
|
module Dry
|
7
8
|
module Types
|
@@ -30,6 +31,32 @@ module Dry
|
|
30
31
|
super(type, **options, fn: Function[options.fetch(:fn, block)])
|
31
32
|
end
|
32
33
|
|
34
|
+
# @param [Builder, Object] input
|
35
|
+
# @param [Hash] options
|
36
|
+
# @param [#call, nil] block
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def self.[](type, fn:, **options)
|
40
|
+
function = Function[fn]
|
41
|
+
|
42
|
+
if function.wrapper?
|
43
|
+
wrapper_type.new(type, fn: function, **options)
|
44
|
+
else
|
45
|
+
new(type, fn: function, **options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
def self.wrapper_type
|
51
|
+
@wrapper_type ||= begin
|
52
|
+
if self < Wrapper
|
53
|
+
self
|
54
|
+
else
|
55
|
+
const_set(:Wrapping, ::Class.new(self).include(Wrapper))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
33
60
|
# Instantiate a new constructor type instance
|
34
61
|
#
|
35
62
|
# @param [Type] type
|
@@ -85,7 +112,13 @@ module Dry
|
|
85
112
|
#
|
86
113
|
# @api public
|
87
114
|
def constructor(new_fn = nil, **options, &block)
|
88
|
-
|
115
|
+
next_fn = Function[new_fn || block]
|
116
|
+
|
117
|
+
if next_fn.wrapper?
|
118
|
+
self.class.wrapper_type.new(with(**options), fn: next_fn)
|
119
|
+
else
|
120
|
+
with(**options, fn: fn >> next_fn)
|
121
|
+
end
|
89
122
|
end
|
90
123
|
alias_method :append, :constructor
|
91
124
|
alias_method :>>, :constructor
|
@@ -123,7 +156,7 @@ module Dry
|
|
123
156
|
# @return [Lax]
|
124
157
|
# @api public
|
125
158
|
def lax
|
126
|
-
Lax.new(
|
159
|
+
Lax.new(constructor_type[type.lax, **options])
|
127
160
|
end
|
128
161
|
|
129
162
|
# Wrap the type with a proc
|
@@ -157,8 +190,8 @@ module Dry
|
|
157
190
|
if type.respond_to?(method)
|
158
191
|
response = type.public_send(method, *args, &block)
|
159
192
|
|
160
|
-
if response.is_a?(Type) && type.class
|
161
|
-
response.constructor_type
|
193
|
+
if response.is_a?(Type) && type.class.equal?(response.class)
|
194
|
+
response.constructor_type[response, **options]
|
162
195
|
else
|
163
196
|
response
|
164
197
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent/map"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
@@ -34,6 +34,10 @@ module Dry
|
|
34
34
|
if public
|
35
35
|
::Class.new(PublicCall) do
|
36
36
|
include PublicCall.call_interface(method, safe)
|
37
|
+
|
38
|
+
define_method(:__to_s__) do
|
39
|
+
"#<PublicCall for :#{method}>"
|
40
|
+
end
|
37
41
|
end
|
38
42
|
elsif safe
|
39
43
|
PrivateCall
|
@@ -116,15 +120,31 @@ module Dry
|
|
116
120
|
end
|
117
121
|
end
|
118
122
|
|
123
|
+
class Wrapper < Function
|
124
|
+
# @return [Object]
|
125
|
+
def call(input, type, &block)
|
126
|
+
@fn.(input, type, &block)
|
127
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
128
|
+
CoercionError.handle(e, &block)
|
129
|
+
end
|
130
|
+
alias_method :[], :call
|
131
|
+
|
132
|
+
def arity
|
133
|
+
2
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
119
137
|
# Choose or build specialized invokation code for a callable
|
120
138
|
#
|
121
139
|
# @param [#call] fn
|
122
140
|
# @return [Function]
|
123
141
|
def self.[](fn)
|
124
|
-
raise ::ArgumentError,
|
142
|
+
raise ::ArgumentError, "Missing constructor block" if fn.nil?
|
125
143
|
|
126
144
|
if fn.is_a?(Function)
|
127
145
|
fn
|
146
|
+
elsif fn.respond_to?(:arity) && fn.arity.equal?(2)
|
147
|
+
Wrapper.new(fn)
|
128
148
|
elsif fn.is_a?(::Method)
|
129
149
|
MethodCall[fn, yields_block?(fn)]
|
130
150
|
elsif yields_block?(fn)
|
@@ -160,6 +180,15 @@ module Dry
|
|
160
180
|
end
|
161
181
|
alias_method :[], :call
|
162
182
|
|
183
|
+
# @return [Integer]
|
184
|
+
def arity
|
185
|
+
1
|
186
|
+
end
|
187
|
+
|
188
|
+
def wrapper?
|
189
|
+
arity.equal?(2)
|
190
|
+
end
|
191
|
+
|
163
192
|
# @return [Array]
|
164
193
|
def to_ast
|
165
194
|
if fn.is_a?(::Proc)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Types
|
5
|
+
# @api public
|
6
|
+
class Constructor < Nominal
|
7
|
+
module Wrapper
|
8
|
+
# @return [Object]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def call_safe(input, &block)
|
12
|
+
fn.(input, type, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Object]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def call_unsafe(input)
|
19
|
+
fn.(input, type)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Object] input
|
23
|
+
# @param [#call,nil] block
|
24
|
+
#
|
25
|
+
# @return [Logic::Result, Types::Result]
|
26
|
+
# @return [Object] if block given and try fails
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def try(input, &block)
|
30
|
+
value = fn.(input, type)
|
31
|
+
rescue CoercionError => e
|
32
|
+
failure = failure(input, e)
|
33
|
+
block_given? ? yield(failure) : failure
|
34
|
+
else
|
35
|
+
type.try(value, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Define a constructor for the type
|
39
|
+
#
|
40
|
+
# @param [#call,nil] constructor
|
41
|
+
# @param [Hash] options
|
42
|
+
# @param [#call,nil] block
|
43
|
+
#
|
44
|
+
# @return [Constructor]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
define_method(:constructor, Builder.instance_method(:constructor))
|
48
|
+
alias_method :append, :constructor
|
49
|
+
alias_method :>>, :constructor
|
50
|
+
|
51
|
+
# Build a new constructor by prepending a block to the coercion function
|
52
|
+
#
|
53
|
+
# @param [#call, nil] new_fn
|
54
|
+
# @param [Hash] options
|
55
|
+
# @param [#call, nil] block
|
56
|
+
#
|
57
|
+
# @return [Constructor]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def prepend(new_fn = nil, **options, &block)
|
61
|
+
prep_fn = Function[new_fn || block]
|
62
|
+
|
63
|
+
decorated =
|
64
|
+
if prep_fn.wrapper?
|
65
|
+
type.constructor(prep_fn, **options)
|
66
|
+
else
|
67
|
+
type.prepend(prep_fn, **options)
|
68
|
+
end
|
69
|
+
|
70
|
+
__new__(decorated)
|
71
|
+
end
|
72
|
+
alias_method :<<, :prepend
|
73
|
+
|
74
|
+
# @return [Constructor]
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def lax
|
78
|
+
# return self back because wrapping function
|
79
|
+
# can handle failed type check
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Replace underlying type
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def __new__(type)
|
89
|
+
self.class.new(type, *@__args__.drop(1), **@options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/dry/types/container.rb
CHANGED
data/lib/dry/types/core.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/types/any"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
@@ -83,17 +83,17 @@ module Dry
|
|
83
83
|
end
|
84
84
|
|
85
85
|
# Register `:bool` since it's common and not a built-in Ruby type :(
|
86
|
-
register(
|
87
|
-
bool = self[
|
88
|
-
register(
|
89
|
-
register(
|
90
|
-
|
91
|
-
register(
|
92
|
-
register(
|
93
|
-
register(
|
86
|
+
register("nominal.bool", self["nominal.true"] | self["nominal.false"])
|
87
|
+
bool = self["strict.true"] | self["strict.false"]
|
88
|
+
register("strict.bool", bool)
|
89
|
+
register("bool", bool)
|
90
|
+
|
91
|
+
register("any", Any)
|
92
|
+
register("nominal.any", Any)
|
93
|
+
register("strict.any", Any)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
-
require
|
98
|
-
require
|
99
|
-
require
|
97
|
+
require "dry/types/coercions"
|
98
|
+
require "dry/types/params"
|
99
|
+
require "dry/types/json"
|
data/lib/dry/types/decorator.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/types/options"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
@@ -100,7 +100,7 @@ module Dry
|
|
100
100
|
#
|
101
101
|
# @api private
|
102
102
|
def __new__(type)
|
103
|
-
self.class.new(type, *@__args__
|
103
|
+
self.class.new(type, *@__args__.drop(1), **@options)
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|