dry-types 1.4.0 → 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 +78 -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 +6 -9
- data/lib/dry/types/constraints.rb +3 -3
- data/lib/dry/types/constructor.rb +40 -6
- data/lib/dry/types/constructor/function.rb +32 -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 +14 -1
- data/lib/dry/types/enum.rb +4 -3
- data/lib/dry/types/errors.rb +1 -1
- data/lib/dry/types/extensions.rb +2 -2
- data/lib/dry/types/extensions/maybe.rb +5 -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 +11 -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 +4 -3
- data/lib/dry/types/type.rb +1 -1
- data/lib/dry/types/version.rb +1 -1
- metadata +9 -22
@@ -4,7 +4,6 @@ module Dry
|
|
4
4
|
module Types
|
5
5
|
# Common API for building type objects in a convenient way
|
6
6
|
#
|
7
|
-
# rubocop:disable Naming/MethodName
|
8
7
|
#
|
9
8
|
# @api public
|
10
9
|
module BuilderMethods
|
@@ -133,7 +132,7 @@ module Dry
|
|
133
132
|
#
|
134
133
|
# @return [Dry::Types::Contrained]
|
135
134
|
def Interface(*methods)
|
136
|
-
methods.reduce(Types[
|
135
|
+
methods.reduce(Types["nominal.any"]) do |type, method|
|
137
136
|
type.constrained(respond_to: method)
|
138
137
|
end
|
139
138
|
end
|
@@ -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,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/types/decorator"
|
5
|
+
require "dry/types/constraints"
|
6
|
+
require "dry/types/constrained/coercible"
|
6
7
|
|
7
8
|
module Dry
|
8
9
|
module Types
|
@@ -29,11 +30,9 @@ module Dry
|
|
29
30
|
@rule = options.fetch(:rule)
|
30
31
|
end
|
31
32
|
|
32
|
-
# @api private
|
33
|
-
#
|
34
33
|
# @return [Object]
|
35
34
|
#
|
36
|
-
# @api
|
35
|
+
# @api private
|
37
36
|
def call_unsafe(input)
|
38
37
|
result = rule.(input)
|
39
38
|
|
@@ -44,11 +43,9 @@ module Dry
|
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
|
-
# @api private
|
48
|
-
#
|
49
46
|
# @return [Object]
|
50
47
|
#
|
51
|
-
# @api
|
48
|
+
# @api private
|
52
49
|
def call_safe(input, &block)
|
53
50
|
if rule[input]
|
54
51
|
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,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/types/fn_container"
|
5
|
+
require "dry/types/constructor/function"
|
6
|
+
require "dry/types/constructor/wrapper"
|
5
7
|
|
6
8
|
module Dry
|
7
9
|
module Types
|
@@ -30,6 +32,32 @@ module Dry
|
|
30
32
|
super(type, **options, fn: Function[options.fetch(:fn, block)])
|
31
33
|
end
|
32
34
|
|
35
|
+
# @param [Builder, Object] input
|
36
|
+
# @param [Hash] options
|
37
|
+
# @param [#call, nil] block
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def self.[](type, fn:, **options)
|
41
|
+
function = Function[fn]
|
42
|
+
|
43
|
+
if function.wrapper?
|
44
|
+
wrapper_type.new(type, fn: function, **options)
|
45
|
+
else
|
46
|
+
new(type, fn: function, **options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def self.wrapper_type
|
52
|
+
@wrapper_type ||= begin
|
53
|
+
if self < Wrapper
|
54
|
+
self
|
55
|
+
else
|
56
|
+
const_set(:Wrapping, ::Class.new(self).include(Wrapper))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
33
61
|
# Instantiate a new constructor type instance
|
34
62
|
#
|
35
63
|
# @param [Type] type
|
@@ -85,7 +113,13 @@ module Dry
|
|
85
113
|
#
|
86
114
|
# @api public
|
87
115
|
def constructor(new_fn = nil, **options, &block)
|
88
|
-
|
116
|
+
next_fn = Function[new_fn || block]
|
117
|
+
|
118
|
+
if next_fn.wrapper?
|
119
|
+
self.class.wrapper_type.new(with(**options), fn: next_fn)
|
120
|
+
else
|
121
|
+
with(**options, fn: fn >> next_fn)
|
122
|
+
end
|
89
123
|
end
|
90
124
|
alias_method :append, :constructor
|
91
125
|
alias_method :>>, :constructor
|
@@ -123,7 +157,7 @@ module Dry
|
|
123
157
|
# @return [Lax]
|
124
158
|
# @api public
|
125
159
|
def lax
|
126
|
-
Lax.new(
|
160
|
+
Lax.new(constructor_type[type.lax, **options])
|
127
161
|
end
|
128
162
|
|
129
163
|
# Wrap the type with a proc
|
@@ -157,8 +191,8 @@ module Dry
|
|
157
191
|
if type.respond_to?(method)
|
158
192
|
response = type.public_send(method, *args, &block)
|
159
193
|
|
160
|
-
if response.is_a?(Type) && type.class
|
161
|
-
response.constructor_type
|
194
|
+
if response.is_a?(Type) && type.class.equal?(response.class)
|
195
|
+
response.constructor_type[response, **options]
|
162
196
|
else
|
163
197
|
response
|
164
198
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "concurrent/map"
|
4
5
|
|
5
6
|
module Dry
|
6
7
|
module Types
|
@@ -34,6 +35,10 @@ module Dry
|
|
34
35
|
if public
|
35
36
|
::Class.new(PublicCall) do
|
36
37
|
include PublicCall.call_interface(method, safe)
|
38
|
+
|
39
|
+
define_method(:__to_s__) do
|
40
|
+
"#<PublicCall for :#{method}>"
|
41
|
+
end
|
37
42
|
end
|
38
43
|
elsif safe
|
39
44
|
PrivateCall
|
@@ -116,15 +121,31 @@ module Dry
|
|
116
121
|
end
|
117
122
|
end
|
118
123
|
|
124
|
+
class Wrapper < Function
|
125
|
+
# @return [Object]
|
126
|
+
def call(input, type, &block)
|
127
|
+
@fn.(input, type, &block)
|
128
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
129
|
+
CoercionError.handle(e, &block)
|
130
|
+
end
|
131
|
+
alias_method :[], :call
|
132
|
+
|
133
|
+
def arity
|
134
|
+
2
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
119
138
|
# Choose or build specialized invokation code for a callable
|
120
139
|
#
|
121
140
|
# @param [#call] fn
|
122
141
|
# @return [Function]
|
123
142
|
def self.[](fn)
|
124
|
-
raise ::ArgumentError,
|
143
|
+
raise ::ArgumentError, "Missing constructor block" if fn.nil?
|
125
144
|
|
126
145
|
if fn.is_a?(Function)
|
127
146
|
fn
|
147
|
+
elsif fn.respond_to?(:arity) && fn.arity.equal?(2)
|
148
|
+
Wrapper.new(fn)
|
128
149
|
elsif fn.is_a?(::Method)
|
129
150
|
MethodCall[fn, yields_block?(fn)]
|
130
151
|
elsif yields_block?(fn)
|
@@ -160,6 +181,15 @@ module Dry
|
|
160
181
|
end
|
161
182
|
alias_method :[], :call
|
162
183
|
|
184
|
+
# @return [Integer]
|
185
|
+
def arity
|
186
|
+
1
|
187
|
+
end
|
188
|
+
|
189
|
+
def wrapper?
|
190
|
+
arity.equal?(2)
|
191
|
+
end
|
192
|
+
|
163
193
|
# @return [Array]
|
164
194
|
def to_ast
|
165
195
|
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"
|