super-smart-tool 0.0.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 +7 -0
- data/dry-types-1.9.1/CHANGELOG.md +1145 -0
- data/dry-types-1.9.1/LICENSE +20 -0
- data/dry-types-1.9.1/README.md +17 -0
- data/dry-types-1.9.1/dry-types.gemspec +45 -0
- data/dry-types-1.9.1/lib/dry/types/any.rb +41 -0
- data/dry-types-1.9.1/lib/dry/types/array/constructor.rb +24 -0
- data/dry-types-1.9.1/lib/dry/types/array/member.rb +118 -0
- data/dry-types-1.9.1/lib/dry/types/array.rb +32 -0
- data/dry-types-1.9.1/lib/dry/types/builder.rb +221 -0
- data/dry-types-1.9.1/lib/dry/types/builder_methods.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/coercions/json.rb +57 -0
- data/dry-types-1.9.1/lib/dry/types/coercions/params.rb +168 -0
- data/dry-types-1.9.1/lib/dry/types/coercions.rb +105 -0
- data/dry-types-1.9.1/lib/dry/types/compat.rb +1 -0
- data/dry-types-1.9.1/lib/dry/types/compiler.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/composition.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/constrained/coercible.rb +59 -0
- data/dry-types-1.9.1/lib/dry/types/constrained.rb +146 -0
- data/dry-types-1.9.1/lib/dry/types/constraints.rb +30 -0
- data/dry-types-1.9.1/lib/dry/types/constructor/function.rb +203 -0
- data/dry-types-1.9.1/lib/dry/types/constructor/wrapper.rb +88 -0
- data/dry-types-1.9.1/lib/dry/types/constructor.rb +195 -0
- data/dry-types-1.9.1/lib/dry/types/container.rb +12 -0
- data/dry-types-1.9.1/lib/dry/types/core.rb +106 -0
- data/dry-types-1.9.1/lib/dry/types/decorator.rb +94 -0
- data/dry-types-1.9.1/lib/dry/types/default.rb +122 -0
- data/dry-types-1.9.1/lib/dry/types/enum.rb +121 -0
- data/dry-types-1.9.1/lib/dry/types/errors.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/extensions/maybe.rb +128 -0
- data/dry-types-1.9.1/lib/dry/types/extensions/monads.rb +34 -0
- data/dry-types-1.9.1/lib/dry/types/extensions.rb +9 -0
- data/dry-types-1.9.1/lib/dry/types/fn_container.rb +37 -0
- data/dry-types-1.9.1/lib/dry/types/hash/constructor.rb +25 -0
- data/dry-types-1.9.1/lib/dry/types/hash.rb +134 -0
- data/dry-types-1.9.1/lib/dry/types/implication.rb +64 -0
- data/dry-types-1.9.1/lib/dry/types/inflector.rb +9 -0
- data/dry-types-1.9.1/lib/dry/types/intersection.rb +102 -0
- data/dry-types-1.9.1/lib/dry/types/json.rb +35 -0
- data/dry-types-1.9.1/lib/dry/types/lax.rb +66 -0
- data/dry-types-1.9.1/lib/dry/types/map.rb +134 -0
- data/dry-types-1.9.1/lib/dry/types/meta.rb +53 -0
- data/dry-types-1.9.1/lib/dry/types/module.rb +127 -0
- data/dry-types-1.9.1/lib/dry/types/nominal.rb +201 -0
- data/dry-types-1.9.1/lib/dry/types/options.rb +34 -0
- data/dry-types-1.9.1/lib/dry/types/params.rb +38 -0
- data/dry-types-1.9.1/lib/dry/types/predicate_inferrer.rb +235 -0
- data/dry-types-1.9.1/lib/dry/types/predicate_registry.rb +32 -0
- data/dry-types-1.9.1/lib/dry/types/primitive_inferrer.rb +75 -0
- data/dry-types-1.9.1/lib/dry/types/printable.rb +14 -0
- data/dry-types-1.9.1/lib/dry/types/printer/composition.rb +44 -0
- data/dry-types-1.9.1/lib/dry/types/printer.rb +313 -0
- data/dry-types-1.9.1/lib/dry/types/result.rb +72 -0
- data/dry-types-1.9.1/lib/dry/types/schema/key.rb +138 -0
- data/dry-types-1.9.1/lib/dry/types/schema.rb +408 -0
- data/dry-types-1.9.1/lib/dry/types/spec/types.rb +164 -0
- data/dry-types-1.9.1/lib/dry/types/sum.rb +105 -0
- data/dry-types-1.9.1/lib/dry/types/type.rb +53 -0
- data/dry-types-1.9.1/lib/dry/types/version.rb +7 -0
- data/dry-types-1.9.1/lib/dry/types.rb +259 -0
- data/dry-types-1.9.1/lib/dry-types.rb +3 -0
- data/super-smart-tool.gemspec +11 -0
- metadata +101 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# PrimitiveInferrer returns the list of classes matching a type.
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
class PrimitiveInferrer
|
|
9
|
+
extend Core::Cache
|
|
10
|
+
|
|
11
|
+
# Compiler reduces type AST into a list of primitives
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
class Compiler
|
|
15
|
+
# @api private
|
|
16
|
+
def visit(node)
|
|
17
|
+
meth, rest = node
|
|
18
|
+
public_send(:"visit_#{meth}", rest)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def visit_nominal(node) = node[0]
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
def visit_hash(_) = ::Hash
|
|
26
|
+
alias_method :visit_schema, :visit_hash
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
def visit_array(_) = ::Array
|
|
30
|
+
|
|
31
|
+
# @api private
|
|
32
|
+
def visit_lax(node) = visit(node)
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
def visit_constructor(node) = visit(node[0])
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
def visit_enum(node) = visit(node[0])
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
def visit_sum(node)
|
|
42
|
+
left, right = node
|
|
43
|
+
|
|
44
|
+
[visit(left), visit(right)].flatten(1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @api private
|
|
48
|
+
def visit_constrained(node) = visit(node[0])
|
|
49
|
+
|
|
50
|
+
# @api private
|
|
51
|
+
def visit_any(_) = ::Object
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [Compiler]
|
|
55
|
+
# @api private
|
|
56
|
+
attr_reader :compiler
|
|
57
|
+
|
|
58
|
+
# @api private
|
|
59
|
+
def initialize
|
|
60
|
+
@compiler = Compiler.new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Infer primitives from the provided type
|
|
64
|
+
#
|
|
65
|
+
# @return [Array[Class]]
|
|
66
|
+
#
|
|
67
|
+
# @api private
|
|
68
|
+
def [](type)
|
|
69
|
+
self.class.fetch_or_store(type) do
|
|
70
|
+
Array(compiler.visit(type.to_ast)).freeze
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# @api private
|
|
6
|
+
class Printer
|
|
7
|
+
# @api private
|
|
8
|
+
class Composition
|
|
9
|
+
def initialize(printer, composition_class)
|
|
10
|
+
@printer = printer
|
|
11
|
+
@composition_class = composition_class
|
|
12
|
+
freeze
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def visit(composition)
|
|
16
|
+
visit_constructors(composition) do |constructors|
|
|
17
|
+
@printer.visit_options(composition.options, composition.meta) do |opts|
|
|
18
|
+
yield "#{@composition_class.composition_name}<#{constructors}#{opts}>"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def visit_constructors(composition)
|
|
26
|
+
visit_constructor(composition.left) do |left|
|
|
27
|
+
visit_constructor(composition.right) do |right|
|
|
28
|
+
yield "#{left} #{@composition_class.operator} #{right}"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def visit_constructor(type, &)
|
|
34
|
+
case type
|
|
35
|
+
when @composition_class
|
|
36
|
+
visit_constructors(type, &)
|
|
37
|
+
else
|
|
38
|
+
@printer.visit(type, &)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/types/printer/composition"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
# rubocop:disable Metrics/AbcSize
|
|
7
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
8
|
+
module Types
|
|
9
|
+
# @api private
|
|
10
|
+
class Printer
|
|
11
|
+
MAPPING = {
|
|
12
|
+
Nominal => :visit_nominal,
|
|
13
|
+
Constructor => :visit_constructor,
|
|
14
|
+
[
|
|
15
|
+
Constrained,
|
|
16
|
+
Constrained::Coercible
|
|
17
|
+
] => :visit_constrained,
|
|
18
|
+
Types::Hash => :visit_hash,
|
|
19
|
+
Schema => :visit_schema,
|
|
20
|
+
Schema::Key => :visit_key,
|
|
21
|
+
Map => :visit_map,
|
|
22
|
+
Array => :visit_array,
|
|
23
|
+
Array::Member => :visit_array_member,
|
|
24
|
+
Lax => :visit_lax,
|
|
25
|
+
Enum => :visit_enum,
|
|
26
|
+
[Default, Default::Callable] => :visit_default,
|
|
27
|
+
[
|
|
28
|
+
Sum,
|
|
29
|
+
Sum::Constrained,
|
|
30
|
+
Intersection,
|
|
31
|
+
Intersection::Constrained,
|
|
32
|
+
Implication,
|
|
33
|
+
Implication::Constrained
|
|
34
|
+
] => :visit_composition,
|
|
35
|
+
Any.class => :visit_any
|
|
36
|
+
}.flat_map { |k, v| Array(k).map { |kk| [kk, v] } }.to_h
|
|
37
|
+
|
|
38
|
+
def initialize
|
|
39
|
+
@composition_printers = {}
|
|
40
|
+
freeze
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def call(type)
|
|
44
|
+
output = "".dup
|
|
45
|
+
visit(type) { |str| output << str }
|
|
46
|
+
"#<Dry::Types[#{output}]>"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def visit(type, &)
|
|
50
|
+
print_with = MAPPING.fetch(type.class) do
|
|
51
|
+
if type.class < Constructor
|
|
52
|
+
:visit_constructor
|
|
53
|
+
elsif type.is_a?(Type)
|
|
54
|
+
return yield type.inspect
|
|
55
|
+
else
|
|
56
|
+
raise ArgumentError, "Do not know how to print #{type.class}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
send(print_with, type, &)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def visit_any(_) = yield "Any"
|
|
63
|
+
|
|
64
|
+
def visit_array(type)
|
|
65
|
+
visit_options(EMPTY_HASH, type.meta) do |opts|
|
|
66
|
+
yield "Array#{opts}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def visit_array_member(array)
|
|
71
|
+
visit(array.member) do |type|
|
|
72
|
+
visit_options(EMPTY_HASH, array.meta) do |opts|
|
|
73
|
+
yield "Array<#{type}#{opts}>"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def visit_constructor(constructor)
|
|
79
|
+
visit(constructor.type) do |type|
|
|
80
|
+
visit_callable(constructor.fn.fn) do |fn|
|
|
81
|
+
options = constructor.options.dup
|
|
82
|
+
options.delete(:fn)
|
|
83
|
+
|
|
84
|
+
visit_options(options) do |opts|
|
|
85
|
+
yield "Constructor<#{type} fn=#{fn}#{opts}>"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def visit_constrained(constrained)
|
|
92
|
+
visit(constrained.type) do |type|
|
|
93
|
+
options = constrained.options.dup
|
|
94
|
+
rule = options.delete(:rule)
|
|
95
|
+
|
|
96
|
+
visit_options(options) do |_opts|
|
|
97
|
+
yield "Constrained<#{type} rule=[#{rule}]>"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def visit_composition(composition, &)
|
|
103
|
+
klass = composition.class
|
|
104
|
+
@composition_printers[klass] = Composition.new(self, klass)
|
|
105
|
+
@composition_printers[klass].visit(composition, &)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def visit_sum_constructors(sum)
|
|
109
|
+
visit_sum_constructor(sum.left) do |left|
|
|
110
|
+
visit_sum_constructor(sum.right) do |right|
|
|
111
|
+
yield "#{left} #{sum.class.operator} #{right}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def visit_sum_constructor(type, &)
|
|
117
|
+
if type.is_a?(Dry::Types::Sum)
|
|
118
|
+
visit_sum_constructors(type, &)
|
|
119
|
+
else
|
|
120
|
+
visit(type, &)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def visit_enum(enum)
|
|
125
|
+
visit(enum.type) do |type|
|
|
126
|
+
options = enum.options.dup
|
|
127
|
+
mapping = options.delete(:mapping)
|
|
128
|
+
|
|
129
|
+
visit_options(options) do |opts|
|
|
130
|
+
if mapping == enum.inverted_mapping
|
|
131
|
+
yield "Enum(#{enum.joined_values})<#{type}#{opts}>"
|
|
132
|
+
else
|
|
133
|
+
mapping_str = mapping.map { |key, value|
|
|
134
|
+
"#{key.inspect}=>#{value.inspect}"
|
|
135
|
+
}.join(", ")
|
|
136
|
+
yield "Enum<#{type} mapping={#{mapping_str}}#{opts}>"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def visit_default(default)
|
|
143
|
+
visit(default.type) do |type|
|
|
144
|
+
visit_options(default.options) do |opts|
|
|
145
|
+
if default.is_a?(Default::Callable)
|
|
146
|
+
visit_callable(default.value) do |fn|
|
|
147
|
+
yield "Default<#{type} value_fn=#{fn}#{opts}>"
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
yield "Default<#{type} value=#{default.value.inspect}#{opts}>"
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def visit_nominal(type)
|
|
157
|
+
visit_options(type.options, type.meta) do |opts|
|
|
158
|
+
yield "Nominal<#{type.primitive}#{opts}>"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def visit_lax(lax)
|
|
163
|
+
visit(lax.type) do |type|
|
|
164
|
+
yield "Lax<#{type}>"
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def visit_callable(callable)
|
|
169
|
+
fn = callable.is_a?(String) ? FnContainer[callable] : callable
|
|
170
|
+
|
|
171
|
+
case fn
|
|
172
|
+
when ::Method
|
|
173
|
+
yield "#{fn.receiver}.#{fn.name}"
|
|
174
|
+
when ::Proc
|
|
175
|
+
path, line = fn.source_location
|
|
176
|
+
|
|
177
|
+
if line&.zero?
|
|
178
|
+
yield ".#{path}"
|
|
179
|
+
elsif path
|
|
180
|
+
yield "#{path.sub("#{Dir.pwd}/", EMPTY_STRING)}:#{line}"
|
|
181
|
+
else
|
|
182
|
+
match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(?<name>\w+)\)(:? \(lambda\))?>\z/)
|
|
183
|
+
|
|
184
|
+
if match
|
|
185
|
+
yield ".#{match[:name]}"
|
|
186
|
+
elsif fn.lambda?
|
|
187
|
+
yield "(lambda)"
|
|
188
|
+
else
|
|
189
|
+
yield "(proc)"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
else
|
|
193
|
+
call = fn.method(:call)
|
|
194
|
+
|
|
195
|
+
if call.owner == fn.class
|
|
196
|
+
yield "#{fn.class}#call"
|
|
197
|
+
else
|
|
198
|
+
yield "#{fn}.call"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def visit_schema(schema)
|
|
204
|
+
options = schema.options.dup
|
|
205
|
+
size = schema.count
|
|
206
|
+
key_fn_str = ""
|
|
207
|
+
type_fn_str = ""
|
|
208
|
+
strict_str = ""
|
|
209
|
+
|
|
210
|
+
strict_str = "strict " if options.delete(:strict)
|
|
211
|
+
|
|
212
|
+
if (key_fn = options.delete(:key_transform_fn))
|
|
213
|
+
visit_callable(key_fn) do |fn|
|
|
214
|
+
key_fn_str = "key_fn=#{fn} "
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if (type_fn = options.delete(:type_transform_fn))
|
|
219
|
+
visit_callable(type_fn) do |fn|
|
|
220
|
+
type_fn_str = "type_fn=#{fn} "
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
keys = options.delete(:keys)
|
|
225
|
+
|
|
226
|
+
visit_options(options, schema.meta) do |opts|
|
|
227
|
+
opts = "#{opts[1..]} " unless opts.empty?
|
|
228
|
+
schema_parameters = "#{key_fn_str}#{type_fn_str}#{strict_str}#{opts}"
|
|
229
|
+
|
|
230
|
+
header = "Schema<#{schema_parameters}keys={"
|
|
231
|
+
|
|
232
|
+
if size.zero?
|
|
233
|
+
yield "#{header}}>"
|
|
234
|
+
else
|
|
235
|
+
yield header.dup << keys.map { |key|
|
|
236
|
+
visit(key) { |type| type }
|
|
237
|
+
}.join(" ") << "}>"
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def visit_map(map)
|
|
243
|
+
visit(map.key_type) do |key|
|
|
244
|
+
visit(map.value_type) do |value|
|
|
245
|
+
options = map.options.dup
|
|
246
|
+
options.delete(:key_type)
|
|
247
|
+
options.delete(:value_type)
|
|
248
|
+
|
|
249
|
+
visit_options(options) do |_opts|
|
|
250
|
+
yield "Map<#{key} => #{value}>"
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def visit_key(key)
|
|
257
|
+
visit(key.type) do |type|
|
|
258
|
+
if key.required?
|
|
259
|
+
yield "#{key.name}: #{type}"
|
|
260
|
+
else
|
|
261
|
+
yield "#{key.name}?: #{type}"
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def visit_hash(hash)
|
|
267
|
+
options = hash.options.dup
|
|
268
|
+
type_fn_str = ""
|
|
269
|
+
|
|
270
|
+
if (type_fn = options.delete(:type_transform_fn))
|
|
271
|
+
visit_callable(type_fn) do |fn|
|
|
272
|
+
type_fn_str = "type_fn=#{fn}"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
visit_options(options, hash.meta) do |opts|
|
|
277
|
+
if opts.empty? && type_fn_str.empty?
|
|
278
|
+
yield "Hash"
|
|
279
|
+
else
|
|
280
|
+
yield "Hash<#{type_fn_str}#{opts}>"
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def visit_options(options, meta = EMPTY_HASH) # rubocop:disable Metrics/PerceivedComplexity
|
|
286
|
+
if options.empty? && meta.empty?
|
|
287
|
+
yield ""
|
|
288
|
+
else
|
|
289
|
+
opts = options.empty? ? "" : " options=#{options.inspect}"
|
|
290
|
+
|
|
291
|
+
if meta.empty?
|
|
292
|
+
yield opts
|
|
293
|
+
else
|
|
294
|
+
values = meta.map do |key, value|
|
|
295
|
+
case key
|
|
296
|
+
when Symbol
|
|
297
|
+
"#{key}: #{value.inspect}"
|
|
298
|
+
else
|
|
299
|
+
"#{key.inspect}=>#{value.inspect}"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
yield "#{opts} meta={#{values.join(", ")}}"
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
PRINTER = Printer.new
|
|
310
|
+
end
|
|
311
|
+
# rubocop:enable Metrics/AbcSize
|
|
312
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
313
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Result class used by {Type#try}
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
class Result
|
|
9
|
+
include ::Dry::Equalizer(:input, immutable: true)
|
|
10
|
+
|
|
11
|
+
# @return [Object]
|
|
12
|
+
attr_reader :input
|
|
13
|
+
|
|
14
|
+
# @param [Object] input
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
def initialize(input)
|
|
18
|
+
@input = input
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Success result
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
class Success < Result
|
|
25
|
+
# @return [true]
|
|
26
|
+
#
|
|
27
|
+
# @api public
|
|
28
|
+
def success? = true
|
|
29
|
+
|
|
30
|
+
# @return [false]
|
|
31
|
+
#
|
|
32
|
+
# @api public
|
|
33
|
+
def failure? = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Failure result
|
|
37
|
+
#
|
|
38
|
+
# @api public
|
|
39
|
+
class Failure < Result
|
|
40
|
+
include ::Dry::Equalizer(:input, :error, immutable: true)
|
|
41
|
+
|
|
42
|
+
# @return [#to_s]
|
|
43
|
+
attr_reader :error
|
|
44
|
+
|
|
45
|
+
# @param [Object] input
|
|
46
|
+
#
|
|
47
|
+
# @param [#to_s] error
|
|
48
|
+
#
|
|
49
|
+
# @api private
|
|
50
|
+
def initialize(input, error)
|
|
51
|
+
super(input)
|
|
52
|
+
@error = error
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @return [String]
|
|
56
|
+
#
|
|
57
|
+
# @api private
|
|
58
|
+
def to_s = error.to_s
|
|
59
|
+
|
|
60
|
+
# @return [false]
|
|
61
|
+
#
|
|
62
|
+
# @api public
|
|
63
|
+
def success? = false
|
|
64
|
+
|
|
65
|
+
# @return [true]
|
|
66
|
+
#
|
|
67
|
+
# @api public
|
|
68
|
+
def failure? = true
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Types
|
|
5
|
+
# Schema is a hash with explicit member types defined
|
|
6
|
+
#
|
|
7
|
+
# @api public
|
|
8
|
+
class Schema < Hash
|
|
9
|
+
# Proxy type for schema keys. Contains only key name and
|
|
10
|
+
# whether it's required or not. All other calls deletaged
|
|
11
|
+
# to the wrapped type.
|
|
12
|
+
#
|
|
13
|
+
# @see Dry::Types::Schema
|
|
14
|
+
class Key
|
|
15
|
+
extend ::Dry::Core::Deprecations[:"dry-types"]
|
|
16
|
+
include Type
|
|
17
|
+
include Dry::Equalizer(:name, :type, :options, inspect: false, immutable: true)
|
|
18
|
+
include Decorator
|
|
19
|
+
include Builder
|
|
20
|
+
include Printable
|
|
21
|
+
|
|
22
|
+
# @return [Symbol]
|
|
23
|
+
attr_reader :name
|
|
24
|
+
|
|
25
|
+
# @api private
|
|
26
|
+
def initialize(type, name, required: Undefined, **options)
|
|
27
|
+
required = Undefined.default(required) do
|
|
28
|
+
type.meta.fetch(:required) { !type.meta.fetch(:omittable, false) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
unless name.is_a?(::Symbol)
|
|
32
|
+
raise ::ArgumentError,
|
|
33
|
+
"Schemas can only contain symbol keys, #{name.inspect} given"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
super
|
|
37
|
+
@name = name
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
def call_safe(input, &) = type.call_safe(input, &)
|
|
42
|
+
|
|
43
|
+
# @api private
|
|
44
|
+
def call_unsafe(input) = type.call_unsafe(input)
|
|
45
|
+
|
|
46
|
+
# @see Dry::Types::Nominal#try
|
|
47
|
+
#
|
|
48
|
+
# @api public
|
|
49
|
+
def try(input, &) = type.try(input, &)
|
|
50
|
+
|
|
51
|
+
# Whether the key is required in schema input
|
|
52
|
+
#
|
|
53
|
+
# @return [Boolean]
|
|
54
|
+
#
|
|
55
|
+
# @api public
|
|
56
|
+
def required? = options.fetch(:required)
|
|
57
|
+
|
|
58
|
+
# Control whether the key is required
|
|
59
|
+
#
|
|
60
|
+
# @overload required
|
|
61
|
+
# @return [Boolean]
|
|
62
|
+
#
|
|
63
|
+
# @overload required(required)
|
|
64
|
+
# Change key's "requireness"
|
|
65
|
+
#
|
|
66
|
+
# @param [Boolean] required New value
|
|
67
|
+
# @return [Dry::Types::Schema::Key]
|
|
68
|
+
#
|
|
69
|
+
# @api public
|
|
70
|
+
def required(required = Undefined)
|
|
71
|
+
if Undefined.equal?(required)
|
|
72
|
+
options.fetch(:required)
|
|
73
|
+
else
|
|
74
|
+
with(required: required)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Make key not required
|
|
79
|
+
#
|
|
80
|
+
# @return [Dry::Types::Schema::Key]
|
|
81
|
+
#
|
|
82
|
+
# @api public
|
|
83
|
+
def omittable = required(false)
|
|
84
|
+
|
|
85
|
+
# Turn key into a lax type. Lax types are not strict hence such keys are not required
|
|
86
|
+
#
|
|
87
|
+
# @return [Lax]
|
|
88
|
+
#
|
|
89
|
+
# @api public
|
|
90
|
+
def lax = __new__(type.lax).required(false)
|
|
91
|
+
|
|
92
|
+
# Make wrapped type optional
|
|
93
|
+
#
|
|
94
|
+
# @return [Key]
|
|
95
|
+
#
|
|
96
|
+
# @api public
|
|
97
|
+
def optional = __new__(type.optional)
|
|
98
|
+
|
|
99
|
+
# Dump to internal AST representation
|
|
100
|
+
#
|
|
101
|
+
# @return [Array]
|
|
102
|
+
#
|
|
103
|
+
# @api public
|
|
104
|
+
def to_ast(meta: true)
|
|
105
|
+
[
|
|
106
|
+
:key,
|
|
107
|
+
[
|
|
108
|
+
name,
|
|
109
|
+
required,
|
|
110
|
+
type.to_ast(meta: meta)
|
|
111
|
+
]
|
|
112
|
+
]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @see Dry::Types::Meta#meta
|
|
116
|
+
#
|
|
117
|
+
# @api public
|
|
118
|
+
def meta(data = Undefined)
|
|
119
|
+
if Undefined.equal?(data) || !data.key?(:omittable)
|
|
120
|
+
super
|
|
121
|
+
else
|
|
122
|
+
self.class.warn(
|
|
123
|
+
"Using meta for making schema keys is deprecated, " \
|
|
124
|
+
"please use .omittable or .required(false) instead" \
|
|
125
|
+
"\n" + Core::Deprecations::STACK.()
|
|
126
|
+
)
|
|
127
|
+
super.required(!data[:omittable])
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
# @api private
|
|
134
|
+
def decorate?(response) = response.is_a?(Type)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|