dry-types 1.2.2 → 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 +405 -225
- data/LICENSE +1 -1
- data/README.md +14 -12
- data/dry-types.gemspec +26 -31
- data/lib/dry-types.rb +1 -1
- data/lib/dry/types.rb +55 -40
- 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 +70 -18
- data/lib/dry/types/builder_methods.rb +1 -2
- data/lib/dry/types/coercions.rb +0 -17
- data/lib/dry/types/coercions/json.rb +22 -5
- data/lib/dry/types/coercions/params.rb +20 -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 +48 -32
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +1 -1
- data/lib/dry/types/core.rb +15 -13
- data/lib/dry/types/decorator.rb +2 -9
- 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 +18 -17
- 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 +3 -6
- data/lib/dry/types/map.rb +2 -2
- data/lib/dry/types/meta.rb +3 -3
- data/lib/dry/types/module.rb +6 -6
- data/lib/dry/types/nominal.rb +11 -11
- data/lib/dry/types/params.rb +31 -28
- data/lib/dry/types/predicate_inferrer.rb +52 -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 +3 -3
- data/lib/dry/types/schema.rb +26 -13
- data/lib/dry/types/schema/key.rb +15 -6
- data/lib/dry/types/spec/types.rb +65 -42
- data/lib/dry/types/sum.rb +6 -5
- data/lib/dry/types/type.rb +1 -1
- data/lib/dry/types/version.rb +1 -1
- metadata +27 -78
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/custom_ci.yml +0 -76
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -34
- data/.gitignore +0 -11
- data/.rspec +0 -4
- data/.rubocop.yml +0 -92
- data/.yardopts +0 -9
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -34
- data/Rakefile +0 -22
- data/benchmarks/hash_schemas.rb +0 -55
- data/benchmarks/lax_schema.rb +0 -15
- data/benchmarks/profile_invalid_input.rb +0 -15
- data/benchmarks/profile_lax_schema_valid.rb +0 -16
- data/benchmarks/profile_valid_input.rb +0 -15
- data/benchmarks/schema_valid_vs_invalid.rb +0 -21
- data/benchmarks/setup.rb +0 -17
- data/docsite/source/array-with-member.html.md +0 -13
- data/docsite/source/built-in-types.html.md +0 -116
- data/docsite/source/constraints.html.md +0 -31
- data/docsite/source/custom-types.html.md +0 -93
- data/docsite/source/default-values.html.md +0 -91
- data/docsite/source/enum.html.md +0 -69
- data/docsite/source/extensions.html.md +0 -15
- data/docsite/source/extensions/maybe.html.md +0 -57
- data/docsite/source/extensions/monads.html.md +0 -61
- data/docsite/source/getting-started.html.md +0 -57
- data/docsite/source/hash-schemas.html.md +0 -169
- data/docsite/source/index.html.md +0 -156
- data/docsite/source/map.html.md +0 -17
- data/docsite/source/optional-values.html.md +0 -35
- data/docsite/source/sum.html.md +0 -21
@@ -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
|
data/lib/dry/types/coercions.rb
CHANGED
@@ -8,23 +8,6 @@ module Dry
|
|
8
8
|
module Coercions
|
9
9
|
include Dry::Core::Constants
|
10
10
|
|
11
|
-
# @param [String, Object] input
|
12
|
-
#
|
13
|
-
# @return [nil] if the input is an empty string or nil
|
14
|
-
#
|
15
|
-
# @raise CoercionError
|
16
|
-
#
|
17
|
-
# @api public
|
18
|
-
def to_nil(input, &_block)
|
19
|
-
if input.nil? || empty_str?(input)
|
20
|
-
nil
|
21
|
-
elsif block_given?
|
22
|
-
yield
|
23
|
-
else
|
24
|
-
raise CoercionError, "#{input.inspect} is not nil"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
11
|
# @param [#to_str, Object] input
|
29
12
|
#
|
30
13
|
# @return [Date, Object]
|
@@ -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
|
@@ -14,6 +14,23 @@ module Dry
|
|
14
14
|
module JSON
|
15
15
|
extend Coercions
|
16
16
|
|
17
|
+
# @param [Object] input
|
18
|
+
#
|
19
|
+
# @return [nil] if the input is nil
|
20
|
+
#
|
21
|
+
# @raise CoercionError
|
22
|
+
#
|
23
|
+
# @api public
|
24
|
+
def self.to_nil(input, &_block)
|
25
|
+
if input.nil?
|
26
|
+
nil
|
27
|
+
elsif block_given?
|
28
|
+
yield
|
29
|
+
else
|
30
|
+
raise CoercionError, "#{input.inspect} is not nil"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
17
34
|
# @param [#to_d, Object] input
|
18
35
|
#
|
19
36
|
# @return [BigDecimal,nil]
|
@@ -21,7 +38,7 @@ module Dry
|
|
21
38
|
# @raise CoercionError
|
22
39
|
#
|
23
40
|
# @api public
|
24
|
-
def self.to_decimal(input, &
|
41
|
+
def self.to_decimal(input, &_block)
|
25
42
|
if input.is_a?(::Float)
|
26
43
|
input.to_d
|
27
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
|
@@ -18,6 +18,23 @@ module Dry
|
|
18
18
|
|
19
19
|
extend Coercions
|
20
20
|
|
21
|
+
# @param [Object] input
|
22
|
+
#
|
23
|
+
# @return [nil] if the input is an empty string or nil
|
24
|
+
#
|
25
|
+
# @raise CoercionError
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def self.to_nil(input, &_block)
|
29
|
+
if input.nil? || empty_str?(input)
|
30
|
+
nil
|
31
|
+
elsif block_given?
|
32
|
+
yield
|
33
|
+
else
|
34
|
+
raise CoercionError, "#{input.inspect} is not nil"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
21
38
|
# @param [String, Object] input
|
22
39
|
#
|
23
40
|
# @return [Boolean,Object]
|
@@ -95,7 +112,7 @@ module Dry
|
|
95
112
|
# @raise CoercionError
|
96
113
|
#
|
97
114
|
# @api public
|
98
|
-
def self.to_decimal(input, &
|
115
|
+
def self.to_decimal(input, &_block)
|
99
116
|
to_float(input) do
|
100
117
|
if block_given?
|
101
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
|
@@ -15,7 +16,7 @@ module Dry
|
|
15
16
|
class Safe < Function
|
16
17
|
def call(input, &block)
|
17
18
|
@fn.(input, &block)
|
18
|
-
rescue NoMethodError, TypeError, ArgumentError => e
|
19
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
19
20
|
CoercionError.handle(e, &block)
|
20
21
|
end
|
21
22
|
end
|
@@ -30,10 +31,14 @@ module Dry
|
|
30
31
|
#
|
31
32
|
# @return [Function]
|
32
33
|
def self.call_class(method, public, safe)
|
33
|
-
@cache.fetch_or_store([method, public, safe]
|
34
|
+
@cache.fetch_or_store([method, public, safe]) do
|
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
|
@@ -53,7 +58,7 @@ module Dry
|
|
53
58
|
#
|
54
59
|
# @return [::Module]
|
55
60
|
def self.call_interface(method, safe)
|
56
|
-
@interfaces.fetch_or_store([method, safe]
|
61
|
+
@interfaces.fetch_or_store([method, safe]) do
|
57
62
|
::Module.new do
|
58
63
|
if safe
|
59
64
|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
@@ -65,7 +70,7 @@ module Dry
|
|
65
70
|
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
66
71
|
def call(input, &block)
|
67
72
|
@target.#{method}(input)
|
68
|
-
rescue NoMethodError, TypeError, ArgumentError => error
|
73
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => error
|
69
74
|
CoercionError.handle(error, &block)
|
70
75
|
end
|
71
76
|
RUBY
|
@@ -90,7 +95,7 @@ module Dry
|
|
90
95
|
class PrivateSafeCall < PrivateCall
|
91
96
|
def call(input, &block)
|
92
97
|
@target.send(@name, input)
|
93
|
-
rescue NoMethodError, TypeError, ArgumentError => e
|
98
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
94
99
|
CoercionError.handle(e, &block)
|
95
100
|
end
|
96
101
|
end
|
@@ -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)
|
@@ -146,7 +167,7 @@ module Dry
|
|
146
167
|
last_arg.equal?(:block)
|
147
168
|
end
|
148
169
|
|
149
|
-
include Dry::Equalizer(:fn, immutable: true)
|
170
|
+
include ::Dry::Equalizer(:fn, immutable: true)
|
150
171
|
|
151
172
|
attr_reader :fn
|
152
173
|
|
@@ -160,39 +181,34 @@ 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)
|
166
|
-
[:id,
|
196
|
+
[:id, FnContainer.register(fn)]
|
167
197
|
else
|
168
198
|
[:callable, fn]
|
169
199
|
end
|
170
200
|
end
|
171
201
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
# @return [Function]
|
180
|
-
def <<(other)
|
181
|
-
proc = other.is_a?(::Proc) ? other : other.fn
|
182
|
-
Function[@fn << proc]
|
183
|
-
end
|
184
|
-
else
|
185
|
-
# @return [Function]
|
186
|
-
def >>(other)
|
187
|
-
proc = other.is_a?(::Proc) ? other : other.fn
|
188
|
-
Function[-> x { proc[@fn[x]] }]
|
189
|
-
end
|
202
|
+
# @return [Function]
|
203
|
+
def >>(other)
|
204
|
+
f = Function[other]
|
205
|
+
Function[-> x, &b { f.(self.(x, &b), &b) }]
|
206
|
+
end
|
190
207
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
end
|
208
|
+
# @return [Function]
|
209
|
+
def <<(other)
|
210
|
+
f = Function[other]
|
211
|
+
Function[-> x, &b { self.(f.(x, &b), &b) }]
|
196
212
|
end
|
197
213
|
end
|
198
214
|
end
|