dry-types 0.15.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 +547 -161
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +27 -30
- data/lib/dry/types/any.rb +23 -12
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +74 -15
- data/lib/dry/types/array.rb +18 -2
- data/lib/dry/types/builder.rb +118 -22
- data/lib/dry/types/builder_methods.rb +46 -16
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +117 -32
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/compiler.rb +44 -21
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constrained.rb +79 -31
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/constructor.rb +110 -61
- data/lib/dry/types/container.rb +6 -1
- data/lib/dry/types/core.rb +34 -11
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +36 -20
- data/lib/dry/types/errors.rb +74 -8
- data/lib/dry/types/extensions/maybe.rb +65 -17
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash/constructor.rb +17 -4
- data/lib/dry/types/hash.rb +32 -20
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +70 -32
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +16 -11
- data/lib/dry/types/nominal.rb +113 -22
- data/lib/dry/types/options.rb +12 -25
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -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 +63 -57
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema/key.rb +62 -36
- data/lib/dry/types/schema.rb +201 -91
- data/lib/dry/types/spec/types.rb +99 -37
- data/lib/dry/types/sum.rb +75 -25
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- data/lib/dry/types.rb +106 -48
- data/lib/dry-types.rb +3 -1
- metadata +55 -78
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.rubocop.yml +0 -43
- data/.travis.yml +0 -28
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -23
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/safe.rb +0 -61
- data/log/.gitkeep +0 -0
data/lib/dry/types/params.rb
CHANGED
@@ -1,53 +1,67 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/types/coercions/params"
|
2
4
|
|
3
5
|
module Dry
|
4
6
|
module Types
|
5
|
-
register(
|
6
|
-
self[
|
7
|
+
register("params.nil") do
|
8
|
+
self["nominal.nil"].constructor(Coercions::Params.method(:to_nil))
|
9
|
+
end
|
10
|
+
|
11
|
+
register("params.date") do
|
12
|
+
self["nominal.date"].constructor(Coercions::Params.method(:to_date))
|
7
13
|
end
|
8
14
|
|
9
|
-
register(
|
10
|
-
self[
|
15
|
+
register("params.date_time") do
|
16
|
+
self["nominal.date_time"].constructor(Coercions::Params.method(:to_date_time))
|
11
17
|
end
|
12
18
|
|
13
|
-
register(
|
14
|
-
self[
|
19
|
+
register("params.time") do
|
20
|
+
self["nominal.time"].constructor(Coercions::Params.method(:to_time))
|
15
21
|
end
|
16
22
|
|
17
|
-
register(
|
18
|
-
self[
|
23
|
+
register("params.true") do
|
24
|
+
self["nominal.true"].constructor(Coercions::Params.method(:to_true))
|
19
25
|
end
|
20
26
|
|
21
|
-
register(
|
22
|
-
self[
|
27
|
+
register("params.false") do
|
28
|
+
self["nominal.false"].constructor(Coercions::Params.method(:to_false))
|
23
29
|
end
|
24
30
|
|
25
|
-
register(
|
26
|
-
self[
|
31
|
+
register("params.bool") do
|
32
|
+
self["params.true"] | self["params.false"]
|
27
33
|
end
|
28
34
|
|
29
|
-
register(
|
30
|
-
|
35
|
+
register("params.integer") do
|
36
|
+
self["nominal.integer"].constructor(Coercions::Params.method(:to_int))
|
31
37
|
end
|
32
38
|
|
33
|
-
register(
|
34
|
-
self[
|
39
|
+
register("params.float") do
|
40
|
+
self["nominal.float"].constructor(Coercions::Params.method(:to_float))
|
35
41
|
end
|
36
42
|
|
37
|
-
register(
|
38
|
-
self[
|
43
|
+
register("params.decimal") do
|
44
|
+
self["nominal.decimal"].constructor(Coercions::Params.method(:to_decimal))
|
39
45
|
end
|
40
46
|
|
41
|
-
register(
|
42
|
-
self[
|
47
|
+
register("params.array") do
|
48
|
+
self["nominal.array"].constructor(Coercions::Params.method(:to_ary))
|
43
49
|
end
|
44
50
|
|
45
|
-
register(
|
46
|
-
self[
|
51
|
+
register("params.hash") do
|
52
|
+
self["nominal.hash"].constructor(Coercions::Params.method(:to_hash))
|
47
53
|
end
|
48
54
|
|
49
|
-
register(
|
50
|
-
self[
|
55
|
+
register("params.symbol") do
|
56
|
+
self["nominal.symbol"].constructor(Coercions::Params.method(:to_symbol))
|
57
|
+
end
|
58
|
+
|
59
|
+
register("params.string", self["string"])
|
60
|
+
|
61
|
+
COERCIBLE.each_key do |name|
|
62
|
+
next if name.equal?(:string)
|
63
|
+
|
64
|
+
register("optional.params.#{name}", self["params.nil"] | self["params.#{name}"])
|
51
65
|
end
|
52
66
|
end
|
53
67
|
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/cache"
|
4
|
+
require "dry/core/class_attributes"
|
5
|
+
require "dry/types/predicate_registry"
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module Types
|
9
|
+
# PredicateInferrer returns the list of predicates used by a type.
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class PredicateInferrer
|
13
|
+
extend Core::Cache
|
14
|
+
|
15
|
+
TYPE_TO_PREDICATE = {
|
16
|
+
::DateTime => :date_time?,
|
17
|
+
::Date => :date?,
|
18
|
+
::Time => :time?,
|
19
|
+
::FalseClass => :false?,
|
20
|
+
::Integer => :int?,
|
21
|
+
::Float => :float?,
|
22
|
+
::NilClass => :nil?,
|
23
|
+
::String => :str?,
|
24
|
+
::TrueClass => :true?,
|
25
|
+
::BigDecimal => :decimal?,
|
26
|
+
::Array => :array?
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
REDUCED_TYPES = {
|
30
|
+
[[[:true?], [:false?]]] => %i[bool?]
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
HASH = %i[hash?].freeze
|
34
|
+
|
35
|
+
ARRAY = %i[array?].freeze
|
36
|
+
|
37
|
+
NIL = %i[nil?].freeze
|
38
|
+
|
39
|
+
# Compiler reduces type AST into a list of predicates
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
class Compiler
|
43
|
+
extend Core::ClassAttributes
|
44
|
+
|
45
|
+
defines :infer_predicate_by_class_name
|
46
|
+
infer_predicate_by_class_name nil
|
47
|
+
|
48
|
+
# @return [PredicateRegistry]
|
49
|
+
# @api private
|
50
|
+
attr_reader :registry
|
51
|
+
|
52
|
+
# @api private
|
53
|
+
def initialize(registry)
|
54
|
+
@registry = registry
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
def infer_predicate(type)
|
59
|
+
pred = TYPE_TO_PREDICATE.fetch(type) do
|
60
|
+
if type.name.nil? || self.class.infer_predicate_by_class_name.equal?(false)
|
61
|
+
nil
|
62
|
+
else
|
63
|
+
candidate = :"#{type.name.split("::").last.downcase}?"
|
64
|
+
|
65
|
+
if registry.key?(candidate)
|
66
|
+
if self.class.infer_predicate_by_class_name
|
67
|
+
candidate
|
68
|
+
else
|
69
|
+
raise ::KeyError, <<~MESSAGE
|
70
|
+
Automatic predicate inferring from class names is deprecated
|
71
|
+
and will be removed in dry-types 2.0.
|
72
|
+
Use `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name true`
|
73
|
+
to restore the previous behavior
|
74
|
+
or `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name false`
|
75
|
+
to explicitly opt-out (i.e. no exception + no inferring).
|
76
|
+
Note: for dry-schema and dry-validation use Dry::Schema::PredicateInferrer::Compiler.
|
77
|
+
MESSAGE
|
78
|
+
end
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if pred.nil?
|
86
|
+
EMPTY_ARRAY
|
87
|
+
else
|
88
|
+
[pred]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @api private
|
93
|
+
def visit(node)
|
94
|
+
meth, rest = node
|
95
|
+
public_send(:"visit_#{meth}", rest)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
def visit_nominal(node)
|
100
|
+
type = node[0]
|
101
|
+
predicate = infer_predicate(type)
|
102
|
+
|
103
|
+
if !predicate.empty? && registry.key?(predicate[0])
|
104
|
+
predicate
|
105
|
+
else
|
106
|
+
[type?: type]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @api private
|
111
|
+
def visit_hash(_)
|
112
|
+
HASH
|
113
|
+
end
|
114
|
+
alias_method :visit_schema, :visit_hash
|
115
|
+
|
116
|
+
# @api private
|
117
|
+
def visit_array(_)
|
118
|
+
ARRAY
|
119
|
+
end
|
120
|
+
|
121
|
+
# @api private
|
122
|
+
def visit_lax(node)
|
123
|
+
visit(node)
|
124
|
+
end
|
125
|
+
|
126
|
+
# @api private
|
127
|
+
def visit_constructor(node)
|
128
|
+
other, * = node
|
129
|
+
visit(other)
|
130
|
+
end
|
131
|
+
|
132
|
+
# @api private
|
133
|
+
def visit_enum(node)
|
134
|
+
other, * = node
|
135
|
+
visit(other)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @api private
|
139
|
+
def visit_sum(node)
|
140
|
+
left_node, right_node, = node
|
141
|
+
left = visit(left_node)
|
142
|
+
right = visit(right_node)
|
143
|
+
|
144
|
+
if left.eql?(NIL)
|
145
|
+
right
|
146
|
+
else
|
147
|
+
[[left, right]]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @api private
|
152
|
+
def visit_constrained(node)
|
153
|
+
other, rules = node
|
154
|
+
predicates = visit(rules)
|
155
|
+
|
156
|
+
if predicates.empty?
|
157
|
+
visit(other)
|
158
|
+
else
|
159
|
+
[*visit(other), *merge_predicates(predicates)]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# @api private
|
164
|
+
def visit_any(_)
|
165
|
+
EMPTY_ARRAY
|
166
|
+
end
|
167
|
+
|
168
|
+
# @api private
|
169
|
+
def visit_and(node)
|
170
|
+
left, right = node
|
171
|
+
visit(left) + visit(right)
|
172
|
+
end
|
173
|
+
|
174
|
+
# @api private
|
175
|
+
def visit_predicate(node)
|
176
|
+
pred, args = node
|
177
|
+
|
178
|
+
if pred.equal?(:type?)
|
179
|
+
EMPTY_ARRAY
|
180
|
+
elsif registry.key?(pred)
|
181
|
+
*curried, _ = args
|
182
|
+
values = curried.map { |_, v| v }
|
183
|
+
|
184
|
+
if values.empty?
|
185
|
+
[pred]
|
186
|
+
else
|
187
|
+
[pred => values[0]]
|
188
|
+
end
|
189
|
+
else
|
190
|
+
EMPTY_ARRAY
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# @api private
|
197
|
+
def merge_predicates(nodes)
|
198
|
+
preds, merged = nodes.each_with_object([[], {}]) do |predicate, (ps, h)|
|
199
|
+
if predicate.is_a?(::Hash)
|
200
|
+
h.update(predicate)
|
201
|
+
else
|
202
|
+
ps << predicate
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
merged.empty? ? preds : [*preds, merged]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# @return [Compiler]
|
211
|
+
# @api private
|
212
|
+
attr_reader :compiler
|
213
|
+
|
214
|
+
# @api private
|
215
|
+
def initialize(registry = PredicateRegistry.new)
|
216
|
+
@compiler = Compiler.new(registry)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Infer predicate identifier from the provided type
|
220
|
+
#
|
221
|
+
# @param [Type] type
|
222
|
+
# @return [Symbol]
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
def [](type)
|
226
|
+
self.class.fetch_or_store(type) do
|
227
|
+
predicates = compiler.visit(type.to_ast)
|
228
|
+
|
229
|
+
if predicates.is_a?(::Hash)
|
230
|
+
predicates
|
231
|
+
else
|
232
|
+
REDUCED_TYPES[predicates] || predicates
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/predicates"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
# A registry with predicate objects from `Dry::Logic::Predicates`
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class PredicateRegistry
|
11
|
+
# @api private
|
12
|
+
attr_reader :predicates
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
attr_reader :has_predicate
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
def initialize(predicates = Logic::Predicates)
|
19
|
+
@predicates = predicates
|
20
|
+
@has_predicate = ::Kernel.instance_method(:respond_to?).bind(@predicates)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def [](name)
|
25
|
+
predicates[name]
|
26
|
+
end
|
27
|
+
|
28
|
+
# @api private
|
29
|
+
def key?(name)
|
30
|
+
has_predicate.(name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/cache"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Types
|
7
|
+
# PrimitiveInferrer returns the list of classes matching a type.
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class PrimitiveInferrer
|
11
|
+
extend Core::Cache
|
12
|
+
|
13
|
+
# Compiler reduces type AST into a list of primitives
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
class Compiler
|
17
|
+
# @api private
|
18
|
+
def visit(node)
|
19
|
+
meth, rest = node
|
20
|
+
public_send(:"visit_#{meth}", rest)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @api private
|
24
|
+
def visit_nominal(node)
|
25
|
+
type, _ = node
|
26
|
+
type
|
27
|
+
end
|
28
|
+
|
29
|
+
# @api private
|
30
|
+
def visit_hash(_)
|
31
|
+
::Hash
|
32
|
+
end
|
33
|
+
alias_method :visit_schema, :visit_hash
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def visit_array(_)
|
37
|
+
::Array
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
def visit_lax(node)
|
42
|
+
visit(node)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def visit_constructor(node)
|
47
|
+
other, * = node
|
48
|
+
visit(other)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @api private
|
52
|
+
def visit_enum(node)
|
53
|
+
other, * = node
|
54
|
+
visit(other)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @api private
|
58
|
+
def visit_sum(node)
|
59
|
+
left, right = node
|
60
|
+
|
61
|
+
[visit(left), visit(right)].flatten(1)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @api private
|
65
|
+
def visit_constrained(node)
|
66
|
+
other, * = node
|
67
|
+
visit(other)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @api private
|
71
|
+
def visit_any(_)
|
72
|
+
::Object
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Compiler]
|
77
|
+
# @api private
|
78
|
+
attr_reader :compiler
|
79
|
+
|
80
|
+
# @api private
|
81
|
+
def initialize
|
82
|
+
@compiler = Compiler.new
|
83
|
+
end
|
84
|
+
|
85
|
+
# Infer primitives from the provided type
|
86
|
+
#
|
87
|
+
# @return [Array[Class]]
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def [](type)
|
91
|
+
self.class.fetch_or_store(type) do
|
92
|
+
Array(compiler.visit(type.to_ast)).freeze
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|