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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/logic/operators'
|
|
2
4
|
|
|
3
5
|
require 'dry/schema/macros/core'
|
|
@@ -14,10 +16,37 @@ module Dry
|
|
|
14
16
|
undef :eql?
|
|
15
17
|
undef :nil?
|
|
16
18
|
|
|
17
|
-
#
|
|
19
|
+
# @!attribute [r] chain
|
|
20
|
+
# Indicate if the macro should append its rules to the provided trace
|
|
21
|
+
# @return [Boolean]
|
|
22
|
+
# @api private
|
|
18
23
|
option :chain, default: -> { true }
|
|
19
24
|
|
|
20
|
-
#
|
|
25
|
+
# @!attribute [r] predicate_inferrer
|
|
26
|
+
# PredicateInferrer is used to infer predicate type-check from a type spec
|
|
27
|
+
# @return [PredicateInferrer]
|
|
28
|
+
# @api private
|
|
29
|
+
option :predicate_inferrer, default: proc { PredicateInferrer.new(compiler.predicates) }
|
|
30
|
+
|
|
31
|
+
# @overload value(*predicates, **predicate_opts)
|
|
32
|
+
# Set predicates without and with arguments
|
|
33
|
+
#
|
|
34
|
+
# @param [Array<Symbol>] predicates
|
|
35
|
+
# @param [Hash] predicate_opts
|
|
36
|
+
#
|
|
37
|
+
# @example with a predicate
|
|
38
|
+
# required(:name).value(:filled?)
|
|
39
|
+
#
|
|
40
|
+
# @example with a predicate with arguments
|
|
41
|
+
# required(:name).value(min_size?: 2)
|
|
42
|
+
#
|
|
43
|
+
# @example with a predicate with and without arguments
|
|
44
|
+
# required(:name).value(:filled?, min_size?: 2)
|
|
45
|
+
#
|
|
46
|
+
# @example with a block
|
|
47
|
+
# required(:name).value { filled? & min_size?(2) }
|
|
48
|
+
#
|
|
49
|
+
# @return [Macros::Core]
|
|
21
50
|
#
|
|
22
51
|
# @api public
|
|
23
52
|
def value(*predicates, **opts, &block)
|
|
@@ -28,6 +57,14 @@ module Dry
|
|
|
28
57
|
|
|
29
58
|
# Prepends `:filled?` predicate
|
|
30
59
|
#
|
|
60
|
+
# @example with a type spec
|
|
61
|
+
# required(:name).filled(:string)
|
|
62
|
+
#
|
|
63
|
+
# @example with a type spec and a predicate
|
|
64
|
+
# required(:name).filled(:string, format?: /\w+/)
|
|
65
|
+
#
|
|
66
|
+
# @return [Macros::Core]
|
|
67
|
+
#
|
|
31
68
|
# @api public
|
|
32
69
|
def filled(*args, &block)
|
|
33
70
|
append_macro(Macros::Filled) do |macro|
|
|
@@ -35,10 +72,35 @@ module Dry
|
|
|
35
72
|
end
|
|
36
73
|
end
|
|
37
74
|
|
|
38
|
-
# Specify a nested
|
|
75
|
+
# Specify a nested hash without enforced `hash?` type-check
|
|
76
|
+
#
|
|
77
|
+
# This is a simpler building block than `hash` macro, use it
|
|
78
|
+
# when you want to provide `hash?` type-check with other rules
|
|
79
|
+
# manually.
|
|
80
|
+
#
|
|
81
|
+
# @example
|
|
82
|
+
# required(:tags).value(:hash, min_size?: 1).schema do
|
|
83
|
+
# required(:name).value(:string)
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# @return [Macros::Core]
|
|
39
87
|
#
|
|
40
88
|
# @api public
|
|
41
89
|
def schema(*args, &block)
|
|
90
|
+
append_macro(Macros::Schema) do |macro|
|
|
91
|
+
macro.call(*args, &block)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Specify a nested hash with enforced `hash?` type-check
|
|
96
|
+
#
|
|
97
|
+
# @example
|
|
98
|
+
# required(:tags).hash do
|
|
99
|
+
# required(:name).value(:string)
|
|
100
|
+
# end
|
|
101
|
+
#
|
|
102
|
+
# @api public
|
|
103
|
+
def hash(*args, &block)
|
|
42
104
|
append_macro(Macros::Hash) do |macro|
|
|
43
105
|
macro.call(*args, &block)
|
|
44
106
|
end
|
|
@@ -46,6 +108,20 @@ module Dry
|
|
|
46
108
|
|
|
47
109
|
# Specify predicates that should be applied to each element of an array
|
|
48
110
|
#
|
|
111
|
+
# This is a simpler building block than `array` macro, use it
|
|
112
|
+
# when you want to provide `array?` type-check with other rules
|
|
113
|
+
# manually.
|
|
114
|
+
#
|
|
115
|
+
# @example a list of strings
|
|
116
|
+
# required(:tags).value(:array, min_size?: 2).each(:str?)
|
|
117
|
+
#
|
|
118
|
+
# @example a list of hashes
|
|
119
|
+
# required(:tags).value(:array, min_size?: 2).each(:hash) do
|
|
120
|
+
# required(:name).filled(:string)
|
|
121
|
+
# end
|
|
122
|
+
#
|
|
123
|
+
# @return [Macros::Core]
|
|
124
|
+
#
|
|
49
125
|
# @api public
|
|
50
126
|
def each(*args, &block)
|
|
51
127
|
append_macro(Macros::Each) do |macro|
|
|
@@ -53,10 +129,44 @@ module Dry
|
|
|
53
129
|
end
|
|
54
130
|
end
|
|
55
131
|
|
|
132
|
+
# Like `each` but sets `array?` type-check
|
|
133
|
+
#
|
|
134
|
+
# @example a list of strings
|
|
135
|
+
# required(:tags).array(:str?)
|
|
136
|
+
#
|
|
137
|
+
# @example a list of hashes
|
|
138
|
+
# required(:tags).array(:hash) do
|
|
139
|
+
# required(:name).filled(:string)
|
|
140
|
+
# end
|
|
141
|
+
#
|
|
142
|
+
# @return [Macros::Core]
|
|
143
|
+
#
|
|
144
|
+
# @api public
|
|
145
|
+
def array(*args, &block)
|
|
146
|
+
append_macro(Macros::Array) do |macro|
|
|
147
|
+
macro.value(*args, &block)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Set type spec
|
|
152
|
+
#
|
|
153
|
+
# @example
|
|
154
|
+
# required(:name).type(:string).value(min_size?: 2)
|
|
155
|
+
#
|
|
156
|
+
# @param [Symbol, Array, Dry::Types::Type] spec
|
|
157
|
+
#
|
|
158
|
+
# @return [Macros::Key]
|
|
159
|
+
#
|
|
160
|
+
# @api public
|
|
161
|
+
def type(spec)
|
|
162
|
+
schema_dsl.set_type(name, spec)
|
|
163
|
+
self
|
|
164
|
+
end
|
|
165
|
+
|
|
56
166
|
private
|
|
57
167
|
|
|
58
168
|
# @api private
|
|
59
|
-
def append_macro(macro_type
|
|
169
|
+
def append_macro(macro_type)
|
|
60
170
|
macro = macro_type.new(schema_dsl: schema_dsl, name: name)
|
|
61
171
|
|
|
62
172
|
yield(macro)
|
|
@@ -68,6 +178,41 @@ module Dry
|
|
|
68
178
|
macro
|
|
69
179
|
end
|
|
70
180
|
end
|
|
181
|
+
|
|
182
|
+
# @api private
|
|
183
|
+
def extract_type_spec(*args, nullable: false, set_type: true)
|
|
184
|
+
type_spec = args[0]
|
|
185
|
+
|
|
186
|
+
is_type_spec = type_spec.is_a?(Dry::Schema::Processor) ||
|
|
187
|
+
type_spec.is_a?(Symbol) &&
|
|
188
|
+
type_spec.to_s.end_with?(QUESTION_MARK)
|
|
189
|
+
|
|
190
|
+
type_spec = nil if is_type_spec
|
|
191
|
+
|
|
192
|
+
predicates = Array(type_spec ? args[1..-1] : args)
|
|
193
|
+
|
|
194
|
+
if type_spec
|
|
195
|
+
resolved_type = schema_dsl.resolve_type(
|
|
196
|
+
nullable && !type_spec.is_a?(::Array) ? [:nil, type_spec] : type_spec
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
type(resolved_type) if set_type
|
|
200
|
+
|
|
201
|
+
type_predicates = predicate_inferrer[resolved_type]
|
|
202
|
+
|
|
203
|
+
unless type_predicates.empty? || predicates.include?(type_predicates)
|
|
204
|
+
if type_predicates.is_a?(::Array) && type_predicates.size.equal?(1)
|
|
205
|
+
predicates.unshift(type_predicates[0])
|
|
206
|
+
else
|
|
207
|
+
predicates.unshift(type_predicates)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
return self if predicates.empty?
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
yield(*predicates, type_spec: type_spec)
|
|
215
|
+
end
|
|
71
216
|
end
|
|
72
217
|
end
|
|
73
218
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/dsl'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,8 +7,17 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# Macro used to specify predicates for each element of an array
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Each < DSL
|
|
12
|
+
# @api private
|
|
13
|
+
def value(*args, **opts)
|
|
14
|
+
extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
|
|
15
|
+
type(schema_dsl.array[type_spec]) if type_spec
|
|
16
|
+
|
|
17
|
+
super(*predicates, type_spec: type_spec, **opts)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
10
21
|
# @api private
|
|
11
22
|
def to_ast(*)
|
|
12
23
|
[:each, trace.to_ast]
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/value'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,18 +7,23 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# Macro used to prepend `:filled?` predicate
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Filled < Value
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
# @api private
|
|
13
|
+
def call(*predicates, **opts, &block)
|
|
14
|
+
if predicates.include?(:empty?)
|
|
15
|
+
raise ::Dry::Schema::InvalidSchemaError, 'Using filled with empty? predicate is invalid'
|
|
13
16
|
end
|
|
14
17
|
|
|
15
|
-
if
|
|
16
|
-
raise ::Dry::Schema::InvalidSchemaError,
|
|
18
|
+
if predicates.include?(:filled?)
|
|
19
|
+
raise ::Dry::Schema::InvalidSchemaError, 'Using filled with filled? is redundant'
|
|
17
20
|
end
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
if opts[:type_spec]
|
|
23
|
+
value(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts, &block)
|
|
24
|
+
else
|
|
25
|
+
value(:filled?, *predicates, **opts, &block)
|
|
26
|
+
end
|
|
20
27
|
end
|
|
21
28
|
end
|
|
22
29
|
end
|
|
@@ -1,44 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/schema/macros/schema'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
4
6
|
module Schema
|
|
5
7
|
module Macros
|
|
6
8
|
# Macro used to specify a nested schema
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
9
|
-
class Hash <
|
|
10
|
+
# @api private
|
|
11
|
+
class Hash < Schema
|
|
10
12
|
# @api private
|
|
11
13
|
def call(*args, &block)
|
|
12
14
|
trace << hash?
|
|
13
|
-
|
|
14
|
-
super(*args) unless args.empty?
|
|
15
|
-
|
|
16
|
-
if block
|
|
17
|
-
definition = schema_dsl.new(&block)
|
|
18
|
-
|
|
19
|
-
parent_type = schema_dsl.types[name]
|
|
20
|
-
definition_schema = definition.type_schema
|
|
21
|
-
|
|
22
|
-
schema_type =
|
|
23
|
-
if parent_type.respond_to?(:of)
|
|
24
|
-
parent_type.of(definition_schema)
|
|
25
|
-
else
|
|
26
|
-
definition_schema
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
final_type =
|
|
30
|
-
if schema_dsl.maybe?(parent_type)
|
|
31
|
-
schema_type.optional
|
|
32
|
-
else
|
|
33
|
-
schema_type
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
schema_dsl.set_type(name, final_type)
|
|
37
|
-
|
|
38
|
-
trace << definition.to_rule
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
self
|
|
15
|
+
super(*args, &block)
|
|
42
16
|
end
|
|
43
17
|
end
|
|
44
18
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/predicate_inferrer'
|
|
2
4
|
require 'dry/schema/processor'
|
|
3
5
|
require 'dry/schema/macros/dsl'
|
|
@@ -6,18 +8,20 @@ require 'dry/schema/constants'
|
|
|
6
8
|
module Dry
|
|
7
9
|
module Schema
|
|
8
10
|
module Macros
|
|
9
|
-
# Base macro for specifying rules applied to a value found under
|
|
10
|
-
#
|
|
11
|
-
# @see DSL#key
|
|
11
|
+
# Base macro for specifying rules applied to a value found under a key
|
|
12
12
|
#
|
|
13
13
|
# @api public
|
|
14
14
|
class Key < DSL
|
|
15
|
+
# @return [Schema::DSL]
|
|
16
|
+
# @api private
|
|
15
17
|
option :filter_schema, optional: true, default: proc { schema_dsl&.new }
|
|
16
18
|
|
|
17
|
-
# Specify predicates that should be
|
|
18
|
-
# before coercion is applied
|
|
19
|
+
# Specify predicates that should be applied before coercion
|
|
19
20
|
#
|
|
20
|
-
# @
|
|
21
|
+
# @example check format before coercing to a date
|
|
22
|
+
# required(:publish_date).filter(format?: /\d{4}-\d{2}-\d{2}).value(:date)
|
|
23
|
+
#
|
|
24
|
+
# @see Macros::Key#value
|
|
21
25
|
#
|
|
22
26
|
# @return [Macros::Key]
|
|
23
27
|
#
|
|
@@ -27,59 +31,67 @@ module Dry
|
|
|
27
31
|
self
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
#
|
|
34
|
+
# @overload value(type_spec, *predicates, **predicate_opts)
|
|
35
|
+
# Set type specification and predicates
|
|
31
36
|
#
|
|
32
|
-
#
|
|
37
|
+
# @param [Symbol,Types::Type,Array] type_spec
|
|
38
|
+
# @param [Array<Symbol>] predicates
|
|
39
|
+
# @param [Hash] predicate_opts
|
|
40
|
+
#
|
|
41
|
+
# @example with a predicate
|
|
42
|
+
# required(:name).value(:string, :filled?)
|
|
43
|
+
#
|
|
44
|
+
# @example with a predicate with arguments
|
|
45
|
+
# required(:name).value(:string, min_size?: 2)
|
|
46
|
+
#
|
|
47
|
+
# @example with a block
|
|
48
|
+
# required(:name).value(:string) { filled? & min_size?(2) }
|
|
33
49
|
#
|
|
34
50
|
# @return [Macros::Key]
|
|
35
51
|
#
|
|
52
|
+
# @see Macros::DSL#value
|
|
53
|
+
#
|
|
36
54
|
# @api public
|
|
37
55
|
def value(*args, **opts, &block)
|
|
38
|
-
extract_type_spec(*args) do |*predicates
|
|
56
|
+
extract_type_spec(*args) do |*predicates, type_spec:|
|
|
39
57
|
super(*predicates, **opts, &block)
|
|
40
58
|
end
|
|
41
59
|
end
|
|
42
60
|
|
|
43
61
|
# Set type specification and predicates for a filled value
|
|
44
62
|
#
|
|
45
|
-
# @
|
|
63
|
+
# @example
|
|
64
|
+
# required(:name).filled(:string)
|
|
65
|
+
#
|
|
66
|
+
# @see Macros::Key#value
|
|
46
67
|
#
|
|
47
68
|
# @return [Macros::Key]
|
|
48
69
|
#
|
|
49
70
|
# @api public
|
|
50
71
|
def filled(*args, **opts, &block)
|
|
51
|
-
extract_type_spec(*args) do |*predicates
|
|
52
|
-
super(*predicates, **opts, &block)
|
|
72
|
+
extract_type_spec(*args) do |*predicates, type_spec:|
|
|
73
|
+
super(*predicates, type_spec: type_spec, **opts, &block)
|
|
53
74
|
end
|
|
54
75
|
end
|
|
55
76
|
|
|
56
77
|
# Set type specification and predicates for a maybe value
|
|
57
78
|
#
|
|
58
|
-
# @
|
|
79
|
+
# @example
|
|
80
|
+
# required(:name).maybe(:string)
|
|
81
|
+
#
|
|
82
|
+
# @see Macros::Key#value
|
|
59
83
|
#
|
|
60
84
|
# @return [Macros::Key]
|
|
61
85
|
#
|
|
62
86
|
# @api public
|
|
63
87
|
def maybe(*args, **opts, &block)
|
|
64
|
-
extract_type_spec(*args, nullable: true) do |*predicates
|
|
88
|
+
extract_type_spec(*args, nullable: true) do |*predicates, type_spec:|
|
|
65
89
|
append_macro(Macros::Maybe) do |macro|
|
|
66
90
|
macro.call(*predicates, **opts, &block)
|
|
67
91
|
end
|
|
68
92
|
end
|
|
69
93
|
end
|
|
70
94
|
|
|
71
|
-
# Set type spec
|
|
72
|
-
#
|
|
73
|
-
# @param [Symbol, Array, Dry::Types::Type]
|
|
74
|
-
#
|
|
75
|
-
# @return [Macros::Key]
|
|
76
|
-
#
|
|
77
|
-
# @api public
|
|
78
|
-
def type(spec)
|
|
79
|
-
schema_dsl.set_type(name, spec)
|
|
80
|
-
self
|
|
81
|
-
end
|
|
82
|
-
|
|
83
95
|
# Coerce macro to a rule
|
|
84
96
|
#
|
|
85
97
|
# @return [Dry::Logic::Rule]
|
|
@@ -97,40 +109,6 @@ module Dry
|
|
|
97
109
|
def to_ast
|
|
98
110
|
[:predicate, [:key?, [[:name, name], [:input, Undefined]]]]
|
|
99
111
|
end
|
|
100
|
-
|
|
101
|
-
private
|
|
102
|
-
|
|
103
|
-
# @api private
|
|
104
|
-
def extract_type_spec(*args, nullable: false)
|
|
105
|
-
type_spec = args[0]
|
|
106
|
-
|
|
107
|
-
if type_spec.kind_of?(Schema::Processor) || type_spec.is_a?(Symbol) && type_spec.to_s.end_with?(QUESTION_MARK)
|
|
108
|
-
type_spec = nil
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
predicates = Array(type_spec ? args[1..-1] : args)
|
|
112
|
-
|
|
113
|
-
if type_spec
|
|
114
|
-
type(nullable && !type_spec.is_a?(::Array) ? [:nil, type_spec] : type_spec)
|
|
115
|
-
type_predicates = PredicateInferrer[schema_dsl.types[name]]
|
|
116
|
-
|
|
117
|
-
unless predicates.include?(type_predicates)
|
|
118
|
-
type_predicates.each do |pred|
|
|
119
|
-
unless compiler.supports?(pred)
|
|
120
|
-
raise ArgumentError, "Predicate +#{pred.inspect}+ inferred from #{type_spec.inspect} type spec is not supported"
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
if type_predicates.size.equal?(1)
|
|
125
|
-
predicates.unshift(type_predicates[0])
|
|
126
|
-
else
|
|
127
|
-
predicates.unshift(type_predicates)
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
yield(*predicates)
|
|
133
|
-
end
|
|
134
112
|
end
|
|
135
113
|
end
|
|
136
114
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/dsl'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,16 +7,16 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# Macro used to specify predicates for a value that can be `nil`
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Maybe < DSL
|
|
10
12
|
# @api private
|
|
11
13
|
def call(*args, **opts, &block)
|
|
12
14
|
if args.include?(:empty?)
|
|
13
|
-
raise ::Dry::Schema::InvalidSchemaError,
|
|
15
|
+
raise ::Dry::Schema::InvalidSchemaError, 'Using maybe with empty? predicate is invalid'
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
if args.include?(:nil?)
|
|
17
|
-
raise ::Dry::Schema::InvalidSchemaError,
|
|
19
|
+
raise ::Dry::Schema::InvalidSchemaError, 'Using maybe with nil? predicate is redundant'
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
value(*args, **opts, &block)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/key'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,12 +7,17 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# A Key specialization used for keys that can be skipped
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Optional < Key
|
|
10
12
|
# @api private
|
|
11
13
|
def operation
|
|
12
14
|
:then
|
|
13
15
|
end
|
|
16
|
+
|
|
17
|
+
# @api private
|
|
18
|
+
def to_rule
|
|
19
|
+
super unless trace.captures.empty?
|
|
20
|
+
end
|
|
14
21
|
end
|
|
15
22
|
end
|
|
16
23
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/key'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,7 +7,7 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# A Key specialization used for keys that must be present
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Required < Key
|
|
10
12
|
# @api private
|
|
11
13
|
def operation
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/schema/macros/value'
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
module Macros
|
|
8
|
+
# Macro used to specify a nested schema
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class Schema < Value
|
|
12
|
+
# @api private
|
|
13
|
+
def call(*args, &block)
|
|
14
|
+
super(*args) unless args.empty?
|
|
15
|
+
|
|
16
|
+
if block
|
|
17
|
+
definition = schema_dsl.new(&block)
|
|
18
|
+
|
|
19
|
+
parent_type = schema_dsl.types[name]
|
|
20
|
+
definition_schema = definition.type_schema
|
|
21
|
+
|
|
22
|
+
schema_type =
|
|
23
|
+
if parent_type.respond_to?(:of)
|
|
24
|
+
parent_type.of(definition_schema)
|
|
25
|
+
else
|
|
26
|
+
definition_schema
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
final_type =
|
|
30
|
+
if parent_type.optional?
|
|
31
|
+
schema_type.optional
|
|
32
|
+
else
|
|
33
|
+
schema_type
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
schema_dsl.set_type(name, final_type)
|
|
37
|
+
|
|
38
|
+
trace << definition.to_rule
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'dry/schema/macros/dsl'
|
|
2
4
|
|
|
3
5
|
module Dry
|
|
@@ -5,12 +7,26 @@ module Dry
|
|
|
5
7
|
module Macros
|
|
6
8
|
# A macro used for specifying predicates to be applied to values from a hash
|
|
7
9
|
#
|
|
8
|
-
# @api
|
|
10
|
+
# @api private
|
|
9
11
|
class Value < DSL
|
|
10
12
|
# @api private
|
|
11
13
|
def call(*predicates, **opts, &block)
|
|
12
|
-
|
|
14
|
+
schema = predicates.detect { |predicate| predicate.is_a?(Processor) }
|
|
15
|
+
|
|
16
|
+
if schema
|
|
17
|
+
current_type = schema_dsl.types[name]
|
|
18
|
+
|
|
19
|
+
updated_type =
|
|
20
|
+
if current_type.respond_to?(:of)
|
|
21
|
+
current_type.of(schema.type_schema)
|
|
22
|
+
else
|
|
23
|
+
schema.type_schema
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
schema_dsl.set_type(name, updated_type)
|
|
27
|
+
end
|
|
13
28
|
|
|
29
|
+
trace.evaluate(*predicates, **opts)
|
|
14
30
|
trace.append(new(chain: false).instance_exec(&block)) if block
|
|
15
31
|
|
|
16
32
|
if trace.captures.empty?
|
data/lib/dry/schema/macros.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/schema/macros/array'
|
|
1
4
|
require 'dry/schema/macros/each'
|
|
2
5
|
require 'dry/schema/macros/filled'
|
|
6
|
+
require 'dry/schema/macros/schema'
|
|
3
7
|
require 'dry/schema/macros/hash'
|
|
4
8
|
require 'dry/schema/macros/maybe'
|
|
5
9
|
require 'dry/schema/macros/optional'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/equalizer'
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
# Message objects used by message sets
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
class Message
|
|
11
|
+
# A message sub-type used by OR operations
|
|
12
|
+
#
|
|
13
|
+
# @api public
|
|
14
|
+
class Or
|
|
15
|
+
# @api private
|
|
16
|
+
attr_reader :left
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
attr_reader :right
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
attr_reader :path
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
attr_reader :messages
|
|
26
|
+
|
|
27
|
+
# @api private
|
|
28
|
+
def initialize(left, right, messages)
|
|
29
|
+
@left = left
|
|
30
|
+
@right = right
|
|
31
|
+
@messages = messages
|
|
32
|
+
@path = left.path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Dump a message into a string
|
|
36
|
+
#
|
|
37
|
+
# @see Message#dump
|
|
38
|
+
#
|
|
39
|
+
# @return [String]
|
|
40
|
+
#
|
|
41
|
+
# @api public
|
|
42
|
+
def dump
|
|
43
|
+
to_a.map(&:dump).join(" #{messages[:or][:text]} ")
|
|
44
|
+
end
|
|
45
|
+
alias to_s dump
|
|
46
|
+
|
|
47
|
+
# @api private
|
|
48
|
+
def to_a
|
|
49
|
+
[left, right]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|