dry-schema 1.6.2 → 1.9.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 +53 -0
- data/README.md +4 -3
- data/dry-schema.gemspec +16 -14
- data/lib/dry/schema/compiler.rb +1 -1
- data/lib/dry/schema/config.rb +9 -9
- data/lib/dry/schema/dsl.rb +7 -4
- data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +9 -4
- data/lib/dry/schema/extensions/hints.rb +11 -9
- data/lib/dry/schema/extensions/info/schema_compiler.rb +10 -1
- data/lib/dry/schema/extensions/json_schema/schema_compiler.rb +232 -0
- data/lib/dry/schema/extensions/json_schema.rb +29 -0
- data/lib/dry/schema/extensions/struct.rb +1 -1
- data/lib/dry/schema/extensions.rb +4 -0
- data/lib/dry/schema/key.rb +75 -70
- data/lib/dry/schema/key_coercer.rb +2 -2
- data/lib/dry/schema/key_validator.rb +46 -20
- data/lib/dry/schema/macros/array.rb +4 -0
- data/lib/dry/schema/macros/core.rb +1 -1
- data/lib/dry/schema/macros/dsl.rb +17 -15
- data/lib/dry/schema/macros/hash.rb +1 -1
- data/lib/dry/schema/macros/key.rb +2 -2
- data/lib/dry/schema/macros/schema.rb +2 -0
- data/lib/dry/schema/macros/value.rb +13 -1
- data/lib/dry/schema/message/or/multi_path.rb +7 -5
- data/lib/dry/schema/message_compiler.rb +13 -10
- data/lib/dry/schema/messages/abstract.rb +9 -9
- data/lib/dry/schema/messages/i18n.rb +98 -96
- data/lib/dry/schema/messages/namespaced.rb +1 -0
- data/lib/dry/schema/messages/yaml.rb +165 -151
- data/lib/dry/schema/path.rb +10 -60
- data/lib/dry/schema/predicate.rb +2 -2
- data/lib/dry/schema/predicate_inferrer.rb +2 -0
- data/lib/dry/schema/primitive_inferrer.rb +2 -0
- data/lib/dry/schema/processor.rb +6 -6
- data/lib/dry/schema/processor_steps.rb +7 -3
- data/lib/dry/schema/result.rb +38 -31
- data/lib/dry/schema/step.rb +14 -33
- data/lib/dry/schema/trace.rb +5 -1
- data/lib/dry/schema/type_registry.rb +1 -2
- data/lib/dry/schema/version.rb +1 -1
- metadata +11 -8
data/lib/dry/schema/path.rb
CHANGED
@@ -32,10 +32,10 @@ module Dry
|
|
32
32
|
new(spec.split(DOT).map(&:to_sym))
|
33
33
|
when Hash
|
34
34
|
new(keys_from_hash(spec))
|
35
|
-
when
|
35
|
+
when self
|
36
36
|
spec
|
37
37
|
else
|
38
|
-
raise ArgumentError, "+spec+ must be either a Symbol, Array, Hash or a
|
38
|
+
raise ArgumentError, "+spec+ must be either a Symbol, Array, Hash or a #{name}"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -60,24 +60,9 @@ module Dry
|
|
60
60
|
|
61
61
|
# @api private
|
62
62
|
def to_h(value = EMPTY_ARRAY.dup)
|
63
|
-
|
64
|
-
last_idx = keys.size - 1
|
65
|
-
hash = EMPTY_HASH.dup
|
66
|
-
node = hash
|
67
|
-
|
68
|
-
while curr_idx <= last_idx
|
69
|
-
node =
|
70
|
-
node[keys[curr_idx]] =
|
71
|
-
if curr_idx == last_idx
|
72
|
-
value.is_a?(Array) ? value : [value]
|
73
|
-
else
|
74
|
-
EMPTY_HASH.dup
|
75
|
-
end
|
76
|
-
|
77
|
-
curr_idx += 1
|
78
|
-
end
|
63
|
+
value = [value] unless value.is_a?(Array)
|
79
64
|
|
80
|
-
|
65
|
+
keys.reverse_each.reduce(value) { |result, key| {key => result} }
|
81
66
|
end
|
82
67
|
|
83
68
|
# @api private
|
@@ -85,59 +70,27 @@ module Dry
|
|
85
70
|
keys.each(&block)
|
86
71
|
end
|
87
72
|
|
88
|
-
# @api private
|
89
|
-
def index(key)
|
90
|
-
keys.index(key)
|
91
|
-
end
|
92
|
-
|
93
|
-
def without_index
|
94
|
-
self.class.new(to_a[0..-2])
|
95
|
-
end
|
96
|
-
|
97
73
|
# @api private
|
98
74
|
def include?(other)
|
99
|
-
|
100
|
-
false
|
101
|
-
elsif index?
|
102
|
-
if other.index?
|
103
|
-
last.equal?(other.last)
|
104
|
-
else
|
105
|
-
without_index.include?(other)
|
106
|
-
end
|
107
|
-
elsif other.index? && key_matches(other, :select).size < 2
|
108
|
-
false
|
109
|
-
else
|
110
|
-
self >= other && !other.key_matches(self).include?(nil)
|
111
|
-
end
|
75
|
+
keys[0, other.keys.length].eql?(other.keys)
|
112
76
|
end
|
113
77
|
|
114
78
|
# @api private
|
115
79
|
def <=>(other)
|
116
|
-
|
80
|
+
return keys.length <=> other.keys.length if include?(other) || other.include?(self)
|
117
81
|
|
118
|
-
|
82
|
+
first_uncommon_index = (self & other).keys.length
|
119
83
|
|
120
|
-
|
121
|
-
|
122
|
-
res.size < count ? 1 : -1
|
84
|
+
keys[first_uncommon_index] <=> other.keys[first_uncommon_index]
|
123
85
|
end
|
124
86
|
|
125
87
|
# @api private
|
126
88
|
def &(other)
|
127
|
-
unless same_root?(other)
|
128
|
-
raise ArgumentError, "#{other.inspect} doesn't have the same root #{inspect}"
|
129
|
-
end
|
130
|
-
|
131
89
|
self.class.new(
|
132
|
-
|
90
|
+
keys.take_while.with_index { |key, index| other.keys[index].eql?(key) }
|
133
91
|
)
|
134
92
|
end
|
135
93
|
|
136
|
-
# @api private
|
137
|
-
def key_matches(other, meth = :map)
|
138
|
-
public_send(meth) { |key| (idx = other.index(key)) && keys[idx].equal?(key) }
|
139
|
-
end
|
140
|
-
|
141
94
|
# @api private
|
142
95
|
def last
|
143
96
|
keys.last
|
@@ -148,10 +101,7 @@ module Dry
|
|
148
101
|
root.equal?(other.root)
|
149
102
|
end
|
150
103
|
|
151
|
-
|
152
|
-
def index?
|
153
|
-
last.is_a?(Integer)
|
154
|
-
end
|
104
|
+
EMPTY = new(EMPTY_ARRAY).freeze
|
155
105
|
end
|
156
106
|
end
|
157
107
|
end
|
data/lib/dry/schema/predicate.rb
CHANGED
data/lib/dry/schema/processor.rb
CHANGED
@@ -28,8 +28,8 @@ module Dry
|
|
28
28
|
include Dry::Logic::Operators
|
29
29
|
|
30
30
|
setting :key_map_type
|
31
|
-
setting :type_registry_namespace, :strict
|
32
|
-
setting :filter_empty_string, false
|
31
|
+
setting :type_registry_namespace, default: :strict
|
32
|
+
setting :filter_empty_string, default: false
|
33
33
|
|
34
34
|
option :steps, default: -> { ProcessorSteps.new }
|
35
35
|
|
@@ -53,7 +53,7 @@ module Dry
|
|
53
53
|
# @api public
|
54
54
|
def define(&block)
|
55
55
|
@definition ||= DSL.new(
|
56
|
-
processor_type: self, parent: superclass.definition, **config, &block
|
56
|
+
processor_type: self, parent: superclass.definition, **config.to_h, &block
|
57
57
|
)
|
58
58
|
self
|
59
59
|
end
|
@@ -84,14 +84,14 @@ module Dry
|
|
84
84
|
#
|
85
85
|
# @api public
|
86
86
|
def call(input)
|
87
|
-
Result.new(input,
|
87
|
+
Result.new(input.dup, message_compiler: message_compiler) do |result|
|
88
88
|
steps.call(result)
|
89
89
|
end
|
90
90
|
end
|
91
91
|
alias_method :[], :call
|
92
92
|
|
93
93
|
# @api public
|
94
|
-
def xor(
|
94
|
+
def xor(_other)
|
95
95
|
raise NotImplementedError, "composing schemas using `xor` operator is not supported yet"
|
96
96
|
end
|
97
97
|
alias_method :^, :xor
|
@@ -123,7 +123,7 @@ module Dry
|
|
123
123
|
# @api public
|
124
124
|
def inspect
|
125
125
|
<<~STR.strip
|
126
|
-
#<#{self.class.name} keys=#{key_map.map(&:dump)} rules=#{rules.
|
126
|
+
#<#{self.class.name} keys=#{key_map.map(&:dump)} rules=#{rules.transform_values(&:to_s)}>
|
127
127
|
STR
|
128
128
|
end
|
129
129
|
|
@@ -87,6 +87,7 @@ module Dry
|
|
87
87
|
def after(name, &block)
|
88
88
|
after_steps[name] ||= EMPTY_ARRAY.dup
|
89
89
|
after_steps[name] << Step.new(type: :after, name: name, executor: block)
|
90
|
+
after_steps[name].sort_by!(&:path)
|
90
91
|
self
|
91
92
|
end
|
92
93
|
|
@@ -100,6 +101,7 @@ module Dry
|
|
100
101
|
def before(name, &block)
|
101
102
|
before_steps[name] ||= EMPTY_ARRAY.dup
|
102
103
|
before_steps[name] << Step.new(type: :before, name: name, executor: block)
|
104
|
+
before_steps[name].sort_by!(&:path)
|
103
105
|
self
|
104
106
|
end
|
105
107
|
|
@@ -120,18 +122,20 @@ module Dry
|
|
120
122
|
# @api private
|
121
123
|
def merge_callbacks(left, right)
|
122
124
|
left.merge(right) do |_key, oldval, newval|
|
123
|
-
oldval + newval
|
125
|
+
(oldval + newval).sort_by(&:path)
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
127
129
|
# @api private
|
128
130
|
def import_callbacks(path, other)
|
129
131
|
other.before_steps.each do |name, steps|
|
130
|
-
|
132
|
+
before_steps[name] ||= []
|
133
|
+
before_steps[name].concat(steps.map { |step| step.scoped(path) }).sort_by!(&:path)
|
131
134
|
end
|
132
135
|
|
133
136
|
other.after_steps.each do |name, steps|
|
134
|
-
|
137
|
+
after_steps[name] ||= []
|
138
|
+
after_steps[name].concat(steps.map { |step| step.scoped(path) }).sort_by!(&:path)
|
135
139
|
end
|
136
140
|
end
|
137
141
|
end
|
data/lib/dry/schema/result.rb
CHANGED
@@ -15,24 +15,21 @@ module Dry
|
|
15
15
|
class Result
|
16
16
|
include Dry::Equalizer(:output, :errors)
|
17
17
|
|
18
|
-
extend Dry::Initializer
|
18
|
+
extend Dry::Initializer[undefined: false]
|
19
19
|
|
20
20
|
# @api private
|
21
|
-
param :output
|
21
|
+
param :output, reader: false
|
22
22
|
|
23
|
-
#
|
23
|
+
# A list of failure ASTs produced by rule result objects
|
24
24
|
#
|
25
|
-
# @return [Hash]
|
26
|
-
alias_method :to_h, :output
|
27
|
-
|
28
25
|
# @api private
|
29
|
-
|
26
|
+
option :result_ast, default: -> { EMPTY_ARRAY.dup }
|
30
27
|
|
31
28
|
# @api private
|
32
29
|
option :message_compiler
|
33
30
|
|
34
31
|
# @api private
|
35
|
-
option :
|
32
|
+
option :path, optional: true, reader: false
|
36
33
|
|
37
34
|
# @api private
|
38
35
|
def self.new(*, **)
|
@@ -48,8 +45,8 @@ module Dry
|
|
48
45
|
# @return [Result]
|
49
46
|
#
|
50
47
|
# @api private
|
51
|
-
def at(
|
52
|
-
new(Path[path
|
48
|
+
def at(at_path, &block)
|
49
|
+
new(@output, path: Path.new([*path, *Path[at_path]]), &block)
|
53
50
|
end
|
54
51
|
|
55
52
|
# @api private
|
@@ -57,7 +54,7 @@ module Dry
|
|
57
54
|
self.class.new(
|
58
55
|
output,
|
59
56
|
message_compiler: message_compiler,
|
60
|
-
|
57
|
+
result_ast: result_ast,
|
61
58
|
**opts,
|
62
59
|
&block
|
63
60
|
)
|
@@ -70,14 +67,35 @@ module Dry
|
|
70
67
|
end
|
71
68
|
|
72
69
|
# @api private
|
73
|
-
def
|
74
|
-
@
|
70
|
+
def path
|
71
|
+
@path || Path::EMPTY
|
72
|
+
end
|
73
|
+
|
74
|
+
# Dump result to a hash returning processed and validated data
|
75
|
+
#
|
76
|
+
# @return [Hash]
|
77
|
+
def output
|
78
|
+
path.equal?(Path::EMPTY) ? @output : @output.dig(*path)
|
79
|
+
end
|
80
|
+
alias_method :to_h, :output
|
81
|
+
|
82
|
+
# @api private
|
83
|
+
def replace(value)
|
84
|
+
if value.is_a?(output.class)
|
85
|
+
output.replace(value)
|
86
|
+
elsif path.equal?(Path::EMPTY)
|
87
|
+
@output = value
|
88
|
+
else
|
89
|
+
value_holder = path.keys.length > 1 ? @output.dig(*path.to_a[0..-2]) : @output
|
90
|
+
|
91
|
+
value_holder[path.last] = value
|
92
|
+
end
|
93
|
+
|
75
94
|
self
|
76
95
|
end
|
77
96
|
|
78
97
|
# @api private
|
79
98
|
def concat(other)
|
80
|
-
results.concat(other)
|
81
99
|
result_ast.concat(other.map(&:to_ast))
|
82
100
|
self
|
83
101
|
end
|
@@ -164,16 +182,14 @@ module Dry
|
|
164
182
|
#
|
165
183
|
# @api public
|
166
184
|
def inspect
|
167
|
-
"#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect}>"
|
185
|
+
"#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect} path=#{path.keys.inspect}>"
|
168
186
|
end
|
169
187
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
output
|
176
|
-
end
|
188
|
+
# Pattern matching support
|
189
|
+
#
|
190
|
+
# @api private
|
191
|
+
def deconstruct_keys(_)
|
192
|
+
output
|
177
193
|
end
|
178
194
|
|
179
195
|
# Add a new error AST node
|
@@ -182,15 +198,6 @@ module Dry
|
|
182
198
|
def add_error(node)
|
183
199
|
result_ast << node
|
184
200
|
end
|
185
|
-
|
186
|
-
private
|
187
|
-
|
188
|
-
# A list of failure ASTs produced by rule result objects
|
189
|
-
#
|
190
|
-
# @api private
|
191
|
-
def result_ast
|
192
|
-
@result_ast ||= results.map(&:to_ast)
|
193
|
-
end
|
194
201
|
end
|
195
202
|
end
|
196
203
|
end
|
data/lib/dry/schema/step.rb
CHANGED
@@ -17,53 +17,34 @@ module Dry
|
|
17
17
|
attr_reader :executor
|
18
18
|
|
19
19
|
# @api private
|
20
|
-
|
21
|
-
# @api private
|
22
|
-
attr_reader :path
|
23
|
-
|
24
|
-
# @api private
|
25
|
-
attr_reader :step
|
26
|
-
|
27
|
-
# @api private
|
28
|
-
def initialize(path, step)
|
29
|
-
@path = Path[path]
|
30
|
-
@step = step
|
31
|
-
end
|
32
|
-
|
33
|
-
# @api private
|
34
|
-
def scoped(new_path)
|
35
|
-
self.class.new(Path[[*new_path, *path]], step)
|
36
|
-
end
|
37
|
-
|
38
|
-
# @api private
|
39
|
-
def call(result)
|
40
|
-
result.at(path) do |scoped_result|
|
41
|
-
output = step.(scoped_result).to_h
|
42
|
-
target = Array(path)[0..-2].reduce(result) { |a, e| a[e] }
|
43
|
-
|
44
|
-
target.update(path.last => output)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
20
|
+
attr_reader :path
|
48
21
|
|
49
22
|
# @api private
|
50
|
-
def initialize(type:, name:, executor:)
|
23
|
+
def initialize(type:, name:, executor:, path: Path::EMPTY)
|
51
24
|
@type = type
|
52
25
|
@name = name
|
53
26
|
@executor = executor
|
27
|
+
@path = path
|
54
28
|
validate_name(name)
|
55
29
|
end
|
56
30
|
|
57
31
|
# @api private
|
58
32
|
def call(result)
|
59
|
-
|
60
|
-
|
33
|
+
scoped_result = path.equal?(Path::EMPTY) ? result : result.at(path)
|
34
|
+
|
35
|
+
output = executor.(scoped_result)
|
36
|
+
scoped_result.replace(output) if output.is_a?(Hash)
|
61
37
|
output
|
62
38
|
end
|
63
39
|
|
64
40
|
# @api private
|
65
|
-
def scoped(
|
66
|
-
|
41
|
+
def scoped(parent_path)
|
42
|
+
self.class.new(
|
43
|
+
type: type,
|
44
|
+
name: name,
|
45
|
+
executor: executor,
|
46
|
+
path: Path.new([*parent_path, *path])
|
47
|
+
)
|
67
48
|
end
|
68
49
|
|
69
50
|
private
|
data/lib/dry/schema/trace.rb
CHANGED
@@ -89,6 +89,10 @@ module Dry
|
|
89
89
|
captures.map(&:to_ast).map(&compiler.method(:visit)).reduce(:and)
|
90
90
|
end
|
91
91
|
|
92
|
+
def respond_to_missing?(meth, include_private = false)
|
93
|
+
super || (meth.to_s.end_with?(QUESTION_MARK) && compuiler.support?(meth))
|
94
|
+
end
|
95
|
+
|
92
96
|
# @api private
|
93
97
|
def method_missing(meth, *args, &block)
|
94
98
|
if meth.to_s.end_with?(QUESTION_MARK)
|
@@ -96,7 +100,7 @@ module Dry
|
|
96
100
|
::Kernel.raise InvalidSchemaError, "#{meth} predicate cannot be used in this context"
|
97
101
|
end
|
98
102
|
|
99
|
-
unless compiler.
|
103
|
+
unless compiler.support?(meth)
|
100
104
|
::Kernel.raise ::ArgumentError, "#{meth} predicate is not defined"
|
101
105
|
end
|
102
106
|
|
data/lib/dry/schema/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-schema
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -30,20 +30,20 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.13'
|
34
34
|
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version: 0.
|
36
|
+
version: 0.13.0
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: '0.
|
43
|
+
version: '0.13'
|
44
44
|
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 0.
|
46
|
+
version: 0.13.0
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: dry-core
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,10 +148,11 @@ dependencies:
|
|
148
148
|
- - ">="
|
149
149
|
- !ruby/object:Gem::Version
|
150
150
|
version: '0'
|
151
|
-
description:
|
151
|
+
description: |+
|
152
152
|
dry-schema provides a DSL for defining schemas with keys and rules that should be applied to
|
153
153
|
values. It supports coercion, input sanitization, custom types and localized error messages
|
154
154
|
(with or without I18n gem). It's also used as the schema engine in dry-validation.
|
155
|
+
|
155
156
|
email:
|
156
157
|
- piotr.solnica@gmail.com
|
157
158
|
executables: []
|
@@ -177,6 +178,8 @@ files:
|
|
177
178
|
- lib/dry/schema/extensions/hints/result_methods.rb
|
178
179
|
- lib/dry/schema/extensions/info.rb
|
179
180
|
- lib/dry/schema/extensions/info/schema_compiler.rb
|
181
|
+
- lib/dry/schema/extensions/json_schema.rb
|
182
|
+
- lib/dry/schema/extensions/json_schema/schema_compiler.rb
|
180
183
|
- lib/dry/schema/extensions/monads.rb
|
181
184
|
- lib/dry/schema/extensions/struct.rb
|
182
185
|
- lib/dry/schema/json.rb
|
@@ -245,7 +248,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
245
248
|
requirements:
|
246
249
|
- - ">="
|
247
250
|
- !ruby/object:Gem::Version
|
248
|
-
version: 2.
|
251
|
+
version: 2.7.0
|
249
252
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
250
253
|
requirements:
|
251
254
|
- - ">="
|