dry-schema 0.1.0 → 1.0.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 +166 -0
- data/LICENSE +1 -1
- data/README.md +13 -4
- data/config/errors.yml +59 -54
- data/lib/dry/schema/compiler.rb +24 -0
- data/lib/dry/schema/config.rb +23 -27
- data/lib/dry/schema/constants.rb +27 -3
- data/lib/dry/schema/dsl.rb +36 -49
- data/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
- data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +84 -0
- data/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
- data/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
- data/lib/dry/schema/extensions/hints.rb +56 -0
- data/lib/dry/schema/extensions/monads.rb +15 -4
- data/lib/dry/schema/extensions.rb +6 -0
- data/lib/dry/schema/json.rb +4 -2
- data/lib/dry/schema/key.rb +8 -10
- data/lib/dry/schema/key_coercer.rb +6 -2
- data/lib/dry/schema/key_map.rb +5 -4
- data/lib/dry/schema/macros/array.rb +39 -0
- data/lib/dry/schema/macros/core.rb +2 -0
- data/lib/dry/schema/macros/dsl.rb +149 -4
- data/lib/dry/schema/macros/each.rb +12 -1
- data/lib/dry/schema/macros/filled.rb +14 -7
- data/lib/dry/schema/macros/hash.rb +6 -32
- data/lib/dry/schema/macros/key.rb +38 -60
- data/lib/dry/schema/macros/maybe.rb +5 -3
- data/lib/dry/schema/macros/optional.rb +8 -1
- data/lib/dry/schema/macros/required.rb +3 -1
- data/lib/dry/schema/macros/schema.rb +46 -0
- data/lib/dry/schema/macros/value.rb +18 -2
- data/lib/dry/schema/macros.rb +4 -0
- data/lib/dry/schema/message/or.rb +54 -0
- data/lib/dry/schema/message.rb +56 -72
- data/lib/dry/schema/message_compiler/visitor_opts.rb +28 -1
- data/lib/dry/schema/message_compiler.rb +85 -74
- data/lib/dry/schema/message_set.rb +82 -50
- data/lib/dry/schema/messages/abstract.rb +84 -44
- data/lib/dry/schema/messages/i18n.rb +45 -9
- data/lib/dry/schema/messages/namespaced.rb +22 -5
- data/lib/dry/schema/messages/template.rb +68 -0
- data/lib/dry/schema/messages/yaml.rb +76 -22
- data/lib/dry/schema/messages.rb +19 -24
- data/lib/dry/schema/namespaced_rule.rb +37 -0
- data/lib/dry/schema/params.rb +4 -2
- data/lib/dry/schema/path.rb +102 -0
- data/lib/dry/schema/predicate.rb +15 -0
- data/lib/dry/schema/predicate_inferrer.rb +116 -23
- data/lib/dry/schema/predicate_registry.rb +2 -0
- data/lib/dry/schema/processor.rb +70 -39
- data/lib/dry/schema/result.rb +16 -31
- data/lib/dry/schema/rule_applier.rb +10 -3
- data/lib/dry/schema/trace.rb +16 -3
- data/lib/dry/schema/type_registry.rb +5 -3
- data/lib/dry/schema/types.rb +3 -1
- data/lib/dry/schema/value_coercer.rb +10 -4
- data/lib/dry/schema/version.rb +3 -1
- data/lib/dry/schema.rb +39 -13
- data/lib/dry-schema.rb +2 -0
- metadata +43 -47
data/lib/dry/schema/dsl.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/initializer'
|
|
2
4
|
|
|
3
5
|
require 'dry/schema/constants'
|
|
@@ -50,29 +52,23 @@ module Dry
|
|
|
50
52
|
|
|
51
53
|
include ::Dry::Equalizer(:options)
|
|
52
54
|
|
|
53
|
-
#
|
|
54
|
-
# @return [Compiler] The rule compiler object
|
|
55
|
+
# @return [Compiler] The rule compiler object
|
|
55
56
|
option :compiler, default: -> { Compiler.new }
|
|
56
57
|
|
|
57
|
-
#
|
|
58
|
-
# @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
|
|
58
|
+
# @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
|
|
59
59
|
option :processor_type, default: -> { Processor }
|
|
60
60
|
|
|
61
|
-
#
|
|
62
|
-
# @return [Array] An array with macros defined within the DSL
|
|
61
|
+
# @return [Array] An array with macros defined within the DSL
|
|
63
62
|
option :macros, default: -> { EMPTY_ARRAY.dup }
|
|
64
63
|
|
|
65
|
-
#
|
|
66
|
-
# @return [Compiler] A key=>type map defined within the DSL
|
|
64
|
+
# @return [Compiler] A key=>type map defined within the DSL
|
|
67
65
|
option :types, default: -> { EMPTY_HASH.dup }
|
|
68
66
|
|
|
69
|
-
#
|
|
70
|
-
# @return [DSL] An optional parent DSL object that will be used to merge keys and rules
|
|
67
|
+
# @return [DSL] An optional parent DSL object that will be used to merge keys and rules
|
|
71
68
|
option :parent, optional: true
|
|
72
69
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
option :config, optional: true, default: -> { Config.new }
|
|
70
|
+
# @return [Config] Configuration object exposed via `#configure` method
|
|
71
|
+
option :config, optional: true, default: proc { parent ? parent.config.dup : Config.new }
|
|
76
72
|
|
|
77
73
|
# Build a new DSL object and evaluate provided block
|
|
78
74
|
#
|
|
@@ -90,7 +86,7 @@ module Dry
|
|
|
90
86
|
# @return [DSL]
|
|
91
87
|
#
|
|
92
88
|
# @api public
|
|
93
|
-
def self.new(options
|
|
89
|
+
def self.new(**options, &block)
|
|
94
90
|
dsl = super
|
|
95
91
|
dsl.instance_eval(&block) if block
|
|
96
92
|
dsl
|
|
@@ -101,7 +97,7 @@ module Dry
|
|
|
101
97
|
# @example
|
|
102
98
|
# Dry::Schema.define do
|
|
103
99
|
# configure do |config|
|
|
104
|
-
# config.messages = :i18n
|
|
100
|
+
# config.messages.backend = :i18n
|
|
105
101
|
# end
|
|
106
102
|
# end
|
|
107
103
|
#
|
|
@@ -160,6 +156,8 @@ module Dry
|
|
|
160
156
|
#
|
|
161
157
|
# @api public
|
|
162
158
|
def key(name, macro:, &block)
|
|
159
|
+
raise ArgumentError, "Key +#{name}+ is not a symbol" unless name.is_a?(::Symbol)
|
|
160
|
+
|
|
163
161
|
set_type(name, Types::Any)
|
|
164
162
|
|
|
165
163
|
macro = macro.new(
|
|
@@ -203,7 +201,7 @@ module Dry
|
|
|
203
201
|
#
|
|
204
202
|
# @api public
|
|
205
203
|
def array
|
|
206
|
-
-> member_type { type_registry[
|
|
204
|
+
-> member_type { type_registry['array'].of(resolve_type(member_type)) }
|
|
207
205
|
end
|
|
208
206
|
|
|
209
207
|
# Return type schema used by the value coercer
|
|
@@ -212,7 +210,11 @@ module Dry
|
|
|
212
210
|
#
|
|
213
211
|
# @api private
|
|
214
212
|
def type_schema
|
|
215
|
-
|
|
213
|
+
if parent
|
|
214
|
+
parent.type_schema.schema(types)
|
|
215
|
+
else
|
|
216
|
+
type_registry['hash'].schema(types).lax
|
|
217
|
+
end
|
|
216
218
|
end
|
|
217
219
|
|
|
218
220
|
# Return a new DSL instance using the same processor type
|
|
@@ -234,16 +236,25 @@ module Dry
|
|
|
234
236
|
# @api private
|
|
235
237
|
def set_type(name, spec)
|
|
236
238
|
type = resolve_type(spec)
|
|
237
|
-
meta = {
|
|
239
|
+
meta = { required: false, maybe: type.optional? }
|
|
238
240
|
|
|
239
241
|
types[name] = type.meta(meta)
|
|
240
242
|
end
|
|
241
243
|
|
|
242
|
-
#
|
|
244
|
+
# Resolve type object from the provided spec
|
|
245
|
+
#
|
|
246
|
+
# @param [Symbol, Array<Symbol>, Dry::Types::Type] spec
|
|
247
|
+
#
|
|
248
|
+
# @return [Dry::Types::Type]
|
|
243
249
|
#
|
|
244
250
|
# @api private
|
|
245
|
-
def
|
|
246
|
-
|
|
251
|
+
def resolve_type(spec)
|
|
252
|
+
case spec
|
|
253
|
+
when ::Dry::Types::Type then spec
|
|
254
|
+
when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
|
|
255
|
+
else
|
|
256
|
+
type_registry[spec]
|
|
257
|
+
end
|
|
247
258
|
end
|
|
248
259
|
|
|
249
260
|
protected
|
|
@@ -263,16 +274,14 @@ module Dry
|
|
|
263
274
|
#
|
|
264
275
|
# @api protected
|
|
265
276
|
def rules
|
|
266
|
-
macros.map { |m| [m.name, m.to_rule] }.to_h.
|
|
277
|
+
parent_rules.merge(macros.map { |m| [m.name, m.to_rule] }.to_h.compact)
|
|
267
278
|
end
|
|
268
279
|
|
|
269
280
|
# Build a key map from defined types
|
|
270
281
|
#
|
|
271
282
|
# @api protected
|
|
272
283
|
def key_map(types = self.types)
|
|
273
|
-
keys = types.
|
|
274
|
-
arr << key_spec(key_name, types[key_name])
|
|
275
|
-
}
|
|
284
|
+
keys = types.map { |key, type| key_spec(key, type) }
|
|
276
285
|
km = KeyMap.new(keys)
|
|
277
286
|
|
|
278
287
|
if key_map_type
|
|
@@ -336,8 +345,8 @@ module Dry
|
|
|
336
345
|
#
|
|
337
346
|
# @api private
|
|
338
347
|
def key_spec(name, type)
|
|
339
|
-
if type.respond_to?(:
|
|
340
|
-
{ name => key_map(type.
|
|
348
|
+
if type.respond_to?(:keys)
|
|
349
|
+
{ name => key_map(type.name_key_map) }
|
|
341
350
|
elsif type.respond_to?(:member)
|
|
342
351
|
kv = key_spec(name, type.member)
|
|
343
352
|
kv.equal?(name) ? name : kv.flatten(1)
|
|
@@ -346,33 +355,11 @@ module Dry
|
|
|
346
355
|
end
|
|
347
356
|
end
|
|
348
357
|
|
|
349
|
-
# Resolve type object from the provided spec
|
|
350
|
-
#
|
|
351
|
-
# @param [Symbol, Array<Symbol>, Dry::Types::Type]
|
|
352
|
-
#
|
|
353
|
-
# @return [Dry::Types::Type]
|
|
354
|
-
#
|
|
355
|
-
# @api private
|
|
356
|
-
def resolve_type(spec)
|
|
357
|
-
case spec
|
|
358
|
-
when ::Dry::Types::Type then spec
|
|
359
|
-
when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
|
|
360
|
-
else
|
|
361
|
-
type_registry[spec]
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
|
|
365
358
|
# @api private
|
|
366
359
|
def parent_rules
|
|
367
360
|
parent&.rules || EMPTY_HASH
|
|
368
361
|
end
|
|
369
362
|
|
|
370
|
-
# @api private
|
|
371
|
-
def parent_types
|
|
372
|
-
# TODO: this is awful, it'd be nice if we had `Dry::Types::Hash::Schema#merge`
|
|
373
|
-
parent&.type_schema&.member_types || EMPTY_HASH
|
|
374
|
-
end
|
|
375
|
-
|
|
376
363
|
# @api private
|
|
377
364
|
def parent_key_map
|
|
378
365
|
parent&.key_map || EMPTY_ARRAY
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Tweaks AND visitor to enable :hints
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module CompilerMethods
|
|
11
|
+
# @api private
|
|
12
|
+
def visit_and(node)
|
|
13
|
+
super.with(hints: true)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Adds support for processing [:hint, ...] nodes produced by dry-logic
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module MessageCompilerMethods
|
|
11
|
+
HINT_TYPE_EXCLUSION = %i[
|
|
12
|
+
key? nil? bool? str? int? float? decimal?
|
|
13
|
+
date? date_time? time? hash? array?
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
HINT_OTHER_EXCLUSION = %i[format? filled?].freeze
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
attr_reader :hints
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def initialize(*args)
|
|
23
|
+
super
|
|
24
|
+
@hints = @options.fetch(:hints, true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @api private
|
|
28
|
+
def hints?
|
|
29
|
+
hints.equal?(true)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
def filter(messages, opts)
|
|
34
|
+
Array(messages).flatten.map { |msg| msg unless exclude?(msg, opts) }.compact.uniq
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
def exclude?(messages, opts)
|
|
39
|
+
Array(messages).all? do |msg|
|
|
40
|
+
hints = opts
|
|
41
|
+
.hints
|
|
42
|
+
.reject { |hint| msg == hint }
|
|
43
|
+
.reject { |hint| hint.predicate == :filled? }
|
|
44
|
+
|
|
45
|
+
key_failure = opts.key_failure?(msg.path)
|
|
46
|
+
predicate = msg.predicate
|
|
47
|
+
|
|
48
|
+
(HINT_TYPE_EXCLUSION.include?(predicate) && !key_failure) ||
|
|
49
|
+
(msg.predicate == :filled? && key_failure) ||
|
|
50
|
+
(!key_failure && HINT_TYPE_EXCLUSION.include?(predicate) &&
|
|
51
|
+
!hints.empty? && hints.any? { |hint| hint.path == msg.path }) ||
|
|
52
|
+
HINT_OTHER_EXCLUSION.include?(predicate)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @api private
|
|
57
|
+
def message_type(options)
|
|
58
|
+
options[:message_type].equal?(:hint) ? Hint : Message
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @api private
|
|
62
|
+
def visit_hint(node, opts)
|
|
63
|
+
if hints?
|
|
64
|
+
filter(visit(node, opts.(message_type: :hint)), opts)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @api private
|
|
69
|
+
def visit_predicate(node, opts)
|
|
70
|
+
message = super
|
|
71
|
+
opts.current_messages << message
|
|
72
|
+
message
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @api private
|
|
76
|
+
def visit_each(node, opts)
|
|
77
|
+
# TODO: we can still generate a hint for elements here!
|
|
78
|
+
[]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Hint extensions for MessageSet
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module MessageSetMethods
|
|
11
|
+
# Filtered message hints from all messages
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Message::Hint>]
|
|
14
|
+
attr_reader :hints
|
|
15
|
+
|
|
16
|
+
# Configuration option to enable/disable showing errors
|
|
17
|
+
#
|
|
18
|
+
# @return [Boolean]
|
|
19
|
+
attr_reader :failures
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def initialize(messages, options = EMPTY_HASH)
|
|
23
|
+
super
|
|
24
|
+
@hints = messages.select(&:hint?)
|
|
25
|
+
@failures = options.fetch(:failures, true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Dump message set to a hash with either all messages or just hints
|
|
29
|
+
#
|
|
30
|
+
# @see MessageSet#to_h
|
|
31
|
+
# @see ResultMethods#hints
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash<Symbol=>Array<String>>]
|
|
34
|
+
#
|
|
35
|
+
# @api public
|
|
36
|
+
def to_h
|
|
37
|
+
@to_h ||= failures ? messages_map : messages_map(hints)
|
|
38
|
+
end
|
|
39
|
+
alias_method :to_hash, :to_h
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Get errors exclusively without hints
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module ResultMethods
|
|
11
|
+
# Return error messages exclusively
|
|
12
|
+
#
|
|
13
|
+
# @see Result#errors
|
|
14
|
+
#
|
|
15
|
+
# @return [MessageSet]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def errors(options = EMPTY_HASH)
|
|
19
|
+
message_set(options.merge(hints: false))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get all messages including hints
|
|
23
|
+
#
|
|
24
|
+
# @see #message_set
|
|
25
|
+
#
|
|
26
|
+
# @return [MessageSet]
|
|
27
|
+
#
|
|
28
|
+
# @api public
|
|
29
|
+
def messages(options = EMPTY_HASH)
|
|
30
|
+
message_set(options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get hints exclusively without errors
|
|
34
|
+
#
|
|
35
|
+
# @see #message_set
|
|
36
|
+
#
|
|
37
|
+
# @return [MessageSet]
|
|
38
|
+
#
|
|
39
|
+
# @api public
|
|
40
|
+
def hints(options = EMPTY_HASH)
|
|
41
|
+
message_set(options.merge(failures: false))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/schema/compiler'
|
|
4
|
+
require 'dry/schema/message'
|
|
5
|
+
require 'dry/schema/message_compiler'
|
|
6
|
+
|
|
7
|
+
require 'dry/schema/extensions/hints/compiler_methods'
|
|
8
|
+
require 'dry/schema/extensions/hints/message_compiler_methods'
|
|
9
|
+
require 'dry/schema/extensions/hints/message_set_methods'
|
|
10
|
+
require 'dry/schema/extensions/hints/result_methods'
|
|
11
|
+
|
|
12
|
+
module Dry
|
|
13
|
+
module Schema
|
|
14
|
+
# Hint-specific Message extensions
|
|
15
|
+
#
|
|
16
|
+
# @see Message
|
|
17
|
+
#
|
|
18
|
+
# @api public
|
|
19
|
+
class Message
|
|
20
|
+
# Hints extension for Or messages
|
|
21
|
+
#
|
|
22
|
+
# @see Message::Or
|
|
23
|
+
#
|
|
24
|
+
# @api public
|
|
25
|
+
class Or
|
|
26
|
+
# @api private
|
|
27
|
+
def hint?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
def hint?
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# A hint message sub-type
|
|
39
|
+
#
|
|
40
|
+
# @api private
|
|
41
|
+
class Hint < Message
|
|
42
|
+
# @api private
|
|
43
|
+
def hint?
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Hints extensions
|
|
49
|
+
module Extensions
|
|
50
|
+
Compiler.prepend(Hints::CompilerMethods)
|
|
51
|
+
MessageCompiler.prepend(Hints::MessageCompilerMethods)
|
|
52
|
+
MessageSet.prepend(Hints::MessageSetMethods)
|
|
53
|
+
Result.prepend(Hints::ResultMethods)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/monads/result'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
4
6
|
module Schema
|
|
7
|
+
# Monad extension for Result
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
5
10
|
class Result
|
|
6
11
|
include Dry::Monads::Result::Mixin
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
# Turn result into a monad
|
|
14
|
+
#
|
|
15
|
+
# This makes result objects work with dry-monads (or anything with a compatible interface)
|
|
16
|
+
#
|
|
17
|
+
# @return [Dry::Monads::Success,Dry::Monads::Failure]
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def to_monad
|
|
9
21
|
if success?
|
|
10
|
-
Success(
|
|
22
|
+
Success(self)
|
|
11
23
|
else
|
|
12
|
-
Failure(
|
|
24
|
+
Failure(self)
|
|
13
25
|
end
|
|
14
26
|
end
|
|
15
|
-
alias_method :to_result, :to_monad
|
|
16
27
|
end
|
|
17
28
|
end
|
|
18
29
|
end
|
data/lib/dry/schema/json.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/processor'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,7 +7,7 @@ module Dry
|
|
|
5
7
|
# JSON schema type
|
|
6
8
|
#
|
|
7
9
|
# @see Processor
|
|
8
|
-
# @see Schema
|
|
10
|
+
# @see Schema#JSON
|
|
9
11
|
#
|
|
10
12
|
# @api public
|
|
11
13
|
class JSON < Processor
|
|
@@ -13,4 +15,4 @@ module Dry
|
|
|
13
15
|
config.type_registry = config.type_registry.namespaced(:json)
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
|
-
end
|
|
18
|
+
end
|
data/lib/dry/schema/key.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Dry
|
|
2
4
|
module Schema
|
|
3
5
|
# Key objects used by key maps
|
|
@@ -10,19 +12,13 @@ module Dry
|
|
|
10
12
|
|
|
11
13
|
include Dry.Equalizer(:name, :coercer)
|
|
12
14
|
|
|
13
|
-
#
|
|
14
|
-
# @return [Symbol] The key identifier
|
|
15
|
-
# @api public
|
|
15
|
+
# @return [Symbol] The key identifier
|
|
16
16
|
attr_reader :id
|
|
17
17
|
|
|
18
|
-
#
|
|
19
|
-
# @return [Symbol, String, Object] The actual key name expected in an input hash
|
|
20
|
-
# @api public
|
|
18
|
+
# @return [Symbol, String, Object] The actual key name expected in an input hash
|
|
21
19
|
attr_reader :name
|
|
22
20
|
|
|
23
|
-
#
|
|
24
|
-
# @return [Proc, #call] A key name coercer function
|
|
25
|
-
# @api public
|
|
21
|
+
# @return [Proc, #call] A key name coercer function
|
|
26
22
|
attr_reader :coercer
|
|
27
23
|
|
|
28
24
|
# @api private
|
|
@@ -44,6 +40,8 @@ module Dry
|
|
|
44
40
|
|
|
45
41
|
# @api private
|
|
46
42
|
def read(source)
|
|
43
|
+
return unless source.is_a?(::Hash)
|
|
44
|
+
|
|
47
45
|
if source.key?(name)
|
|
48
46
|
yield(source[name])
|
|
49
47
|
elsif source.key?(coerced_name)
|
|
@@ -163,4 +161,4 @@ module Dry
|
|
|
163
161
|
end
|
|
164
162
|
end
|
|
165
163
|
end
|
|
166
|
-
end
|
|
164
|
+
end
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/core/cache'
|
|
4
|
+
require 'dry/equalizer'
|
|
2
5
|
|
|
3
6
|
module Dry
|
|
4
7
|
module Schema
|
|
@@ -7,6 +10,7 @@ module Dry
|
|
|
7
10
|
# @api private
|
|
8
11
|
class KeyCoercer
|
|
9
12
|
extend Dry::Core::Cache
|
|
13
|
+
include ::Dry::Equalizer(:key_map, :coercer)
|
|
10
14
|
|
|
11
15
|
TO_SYM = :to_sym.to_proc.freeze
|
|
12
16
|
|
|
@@ -26,10 +30,10 @@ module Dry
|
|
|
26
30
|
def initialize(key_map, &coercer)
|
|
27
31
|
@key_map = key_map.coercible(&coercer)
|
|
28
32
|
end
|
|
29
|
-
|
|
33
|
+
|
|
30
34
|
# @api private
|
|
31
35
|
def call(source)
|
|
32
|
-
key_map.write(
|
|
36
|
+
key_map.write(source.to_h)
|
|
33
37
|
end
|
|
34
38
|
alias_method :[], :call
|
|
35
39
|
end
|
data/lib/dry/schema/key_map.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/equalizer'
|
|
2
4
|
require 'dry/core/cache'
|
|
3
5
|
require 'dry/schema/constants'
|
|
@@ -19,8 +21,7 @@ module Dry
|
|
|
19
21
|
include Dry.Equalizer(:keys)
|
|
20
22
|
include Enumerable
|
|
21
23
|
|
|
22
|
-
#
|
|
23
|
-
# @return [Array<Key>] A list of defined key objects
|
|
24
|
+
# @return [Array<Key>] A list of defined key objects
|
|
24
25
|
attr_reader :keys
|
|
25
26
|
|
|
26
27
|
# Coerce a list of key specs into a key map
|
|
@@ -39,7 +40,7 @@ module Dry
|
|
|
39
40
|
|
|
40
41
|
# Build new, or returned a cached instance of a key map
|
|
41
42
|
#
|
|
42
|
-
# @param [Array<Symbol
|
|
43
|
+
# @param [Array<Symbol>, Array, Hash<Symbol=>Array>] args
|
|
43
44
|
#
|
|
44
45
|
# @return [KeyMap]
|
|
45
46
|
def self.new(*args)
|
|
@@ -130,4 +131,4 @@ module Dry
|
|
|
130
131
|
end
|
|
131
132
|
end
|
|
132
133
|
end
|
|
133
|
-
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/schema/macros/dsl'
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
module Macros
|
|
8
|
+
# Macro used to specify predicates for each element of an array
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class Array < DSL
|
|
12
|
+
# @api private
|
|
13
|
+
def value(*args, **opts, &block)
|
|
14
|
+
type(:array)
|
|
15
|
+
|
|
16
|
+
extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
|
|
17
|
+
type(schema_dsl.array[type_spec]) if type_spec
|
|
18
|
+
|
|
19
|
+
is_hash_block = type_spec.equal?(:hash)
|
|
20
|
+
|
|
21
|
+
if predicates.any? || opts.any? || !is_hash_block
|
|
22
|
+
super(*predicates, type_spec: type_spec, **opts, &(is_hash_block ? nil : block))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
hash(&block) if is_hash_block
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @api private
|
|
32
|
+
def to_ast(*)
|
|
33
|
+
[:and, [trace.array?.to_ast, [:each, trace.to_ast]]]
|
|
34
|
+
end
|
|
35
|
+
alias_method :ast, :to_ast
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|