dry-schema 1.6.0 → 1.7.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 +53 -0
- data/README.md +5 -4
- data/config/errors.yml +2 -0
- data/dry-schema.gemspec +15 -13
- data/lib/dry/schema/key.rb +5 -1
- data/lib/dry/schema/key_validator.rb +6 -1
- data/lib/dry/schema/macros/dsl.rb +1 -0
- data/lib/dry/schema/macros/value.rb +6 -1
- data/lib/dry/schema/message_compiler.rb +4 -13
- data/lib/dry/schema/messages/yaml.rb +8 -1
- data/lib/dry/schema/path.rb +10 -60
- data/lib/dry/schema/processor.rb +3 -3
- data/lib/dry/schema/processor_steps.rb +7 -3
- data/lib/dry/schema/result.rb +33 -24
- data/lib/dry/schema/step.rb +14 -33
- data/lib/dry/schema/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 377172612c5f08c6325b7d71c749296505cbb3811894496da15ee5ed1aa1b69a
|
4
|
+
data.tar.gz: 8b55f71218c418f073b8daf84a64a8d31aa02035908c7cd301df2780f5a2c2c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07152d8d51f7f93dff3409b7c14b89f6a374158ed3ecc5d22dc2103a579b653c6ba085f6ec8b9464bcc406b1c123a669a4112b2b3d541b644cdaf65492ccb5a3
|
7
|
+
data.tar.gz: dc9e7d840f6c40436d3234698c5ce5d04e23952678ffb36694e715dcbc9e455b1a0efb4a81919a9f01c7d0ebfe4f37dd88f611b04e1d4e7b1c576f84f5ffba1d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,58 @@
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
2
|
|
3
|
+
## 1.7.1 2021-08-29
|
4
|
+
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
|
8
|
+
- [internal] Use explicit `#to_h` conversion of Dry::Configurable::Config, to ensure compatibility with upcoming dry-configurable 0.13.0 release (via #371) (@timriley)
|
9
|
+
|
10
|
+
[Compare v1.7.0...v1.7.1](https://github.com/dry-rb/dry-schema/compare/v1.7.0...v1.7.1)
|
11
|
+
|
12
|
+
## 1.7.0 2021-06-29
|
13
|
+
|
14
|
+
This release ships with a bunch of internal refactorings that should improve performance but if you see any unexpected behavior please do report issues.
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
- Handle arrays of hashes where Array constructor coerces non-Hash input (#351 fixed via #354) (@ojab)
|
19
|
+
- Run outer schema processor steps before inner ones (issue #350 fixed via #361) (@ojab)
|
20
|
+
- Fix key validator false negatives on empty collections (see #363) (@Drenmi)
|
21
|
+
- Prevent error message YAML files from being parsed multiple times (issue #352 via #364) (@alassek)
|
22
|
+
- Using constructor types should work fine now ie `required(:foo).filled(Types::Params::Integer.constructor(&:succ))` (issue #280 fixed via #365) (@solnic)
|
23
|
+
- Handle non-Hash to Hash transformation in `before(:key_coercer)` (issue #350 fixed via #362) (@ojab)
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
- [internal] `Dry::Schema::Path` clean up and performance improvements (via #358) (@ojab)
|
28
|
+
- [internal] simplify and speed up handling of steps in nested schemas (via #360) (@ojab)
|
29
|
+
|
30
|
+
[Compare v1.6.2...v1.7.0](https://github.com/dry-rb/dry-schema/compare/v1.6.2...v1.7.0)
|
31
|
+
|
32
|
+
## 1.6.2 2021-04-15
|
33
|
+
|
34
|
+
|
35
|
+
### Added
|
36
|
+
|
37
|
+
- A default error message for `respond_to?` predicate (@rindek)
|
38
|
+
|
39
|
+
### Fixed
|
40
|
+
|
41
|
+
- Using `respond_to?` predicate in blocks works now (@rindek)
|
42
|
+
|
43
|
+
|
44
|
+
[Compare v1.6.1...v1.6.2](https://github.com/dry-rb/dry-schema/compare/v1.6.1...v1.6.2)
|
45
|
+
|
46
|
+
## 1.6.1 2021-02-02
|
47
|
+
|
48
|
+
|
49
|
+
### Fixed
|
50
|
+
|
51
|
+
- Messages#[] handles meta/no meta cases more gracefully and has better interoperability with the I18n backend. This brings MessageCompiler#visit_unexpected_key up to parity with MessageCompiler#visit_predicate. Uses visit_predicate as basis for visit_unexpected_key. (@robhanlon22)
|
52
|
+
|
53
|
+
|
54
|
+
[Compare v1.6.0...v1.6.1](https://github.com/dry-rb/dry-schema/compare/v1.6.0...v1.6.1)
|
55
|
+
|
3
56
|
## 1.6.0 2021-01-21
|
4
57
|
|
5
58
|
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
<!--- this file is synced from dry-rb/template-gem project -->
|
1
2
|
[gem]: https://rubygems.org/gems/dry-schema
|
2
3
|
[actions]: https://github.com/dry-rb/dry-schema/actions
|
3
4
|
[codacy]: https://www.codacy.com/gh/dry-rb/dry-schema
|
@@ -7,22 +8,22 @@
|
|
7
8
|
# dry-schema [][chat]
|
8
9
|
|
9
10
|
[][gem]
|
10
|
-
[][actions]
|
11
12
|
[][codacy]
|
12
13
|
[][codacy]
|
13
14
|
[][inchpages]
|
14
15
|
|
15
16
|
## Links
|
16
17
|
|
17
|
-
* [User documentation](
|
18
|
+
* [User documentation](https://dry-rb.org/gems/dry-schema)
|
18
19
|
* [API documentation](http://rubydoc.info/gems/dry-schema)
|
19
20
|
|
20
21
|
## Supported Ruby versions
|
21
22
|
|
22
23
|
This library officially supports the following Ruby versions:
|
23
24
|
|
24
|
-
* MRI
|
25
|
-
* jruby
|
25
|
+
* MRI `>= 2.6.0`
|
26
|
+
* ~~jruby~~ `>= 9.3` (we are waiting for [2.6 support](https://github.com/jruby/jruby/issues/6161))
|
26
27
|
|
27
28
|
## License
|
28
29
|
|
data/config/errors.yml
CHANGED
data/dry-schema.gemspec
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# this file is managed by dry-rb/devtools project
|
3
2
|
|
4
|
-
|
3
|
+
# this file is synced from dry-rb/template-gem project
|
4
|
+
|
5
|
+
lib = File.expand_path("lib", __dir__)
|
5
6
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require
|
7
|
+
require "dry/schema/version"
|
7
8
|
|
8
9
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name =
|
10
|
+
spec.name = "dry-schema"
|
10
11
|
spec.authors = ["Piotr Solnica"]
|
11
12
|
spec.email = ["piotr.solnica@gmail.com"]
|
12
|
-
spec.license =
|
13
|
+
spec.license = "MIT"
|
13
14
|
spec.version = Dry::Schema::VERSION.dup
|
14
15
|
|
15
16
|
spec.summary = "Coercion and validation for data structures"
|
@@ -17,19 +18,20 @@ Gem::Specification.new do |spec|
|
|
17
18
|
dry-schema provides a DSL for defining schemas with keys and rules that should be applied to
|
18
19
|
values. It supports coercion, input sanitization, custom types and localized error messages
|
19
20
|
(with or without I18n gem). It's also used as the schema engine in dry-validation.
|
21
|
+
|
20
22
|
TEXT
|
21
|
-
spec.homepage =
|
23
|
+
spec.homepage = "https://dry-rb.org/gems/dry-schema"
|
22
24
|
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-schema.gemspec", "lib/**/*", "config/*.yml"]
|
23
|
-
spec.bindir =
|
25
|
+
spec.bindir = "bin"
|
24
26
|
spec.executables = []
|
25
|
-
spec.require_paths = [
|
27
|
+
spec.require_paths = ["lib"]
|
26
28
|
|
27
|
-
spec.metadata[
|
28
|
-
spec.metadata[
|
29
|
-
spec.metadata[
|
30
|
-
spec.metadata[
|
29
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
30
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-schema/blob/master/CHANGELOG.md"
|
31
|
+
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-schema"
|
32
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-schema/issues"
|
31
33
|
|
32
|
-
spec.required_ruby_version = ">= 2.
|
34
|
+
spec.required_ruby_version = ">= 2.6.0"
|
33
35
|
|
34
36
|
# to update dependencies edit project.yml
|
35
37
|
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
data/lib/dry/schema/key.rb
CHANGED
@@ -151,7 +151,11 @@ module Dry
|
|
151
151
|
# @api private
|
152
152
|
def write(source, target)
|
153
153
|
read(source) { |value|
|
154
|
-
target[coerced_name] = value.is_a?(::Array)
|
154
|
+
target[coerced_name] = if value.is_a?(::Array)
|
155
|
+
value.map { |el| el.is_a?(::Hash) ? member.write(el) : el }
|
156
|
+
else
|
157
|
+
value
|
158
|
+
end
|
155
159
|
}
|
156
160
|
end
|
157
161
|
|
@@ -51,9 +51,14 @@ module Dry
|
|
51
51
|
hash.flat_map { |key, _|
|
52
52
|
case (value = hash[key])
|
53
53
|
when Hash
|
54
|
+
next key.to_s if value.empty?
|
55
|
+
|
54
56
|
[key].product(key_paths(hash[key])).map { |keys| keys.join(DOT) }
|
55
57
|
when Array
|
56
|
-
hashes_or_arrays = value.select { |e| e.is_a?(Array) || e.is_a?(Hash) }
|
58
|
+
hashes_or_arrays = value.select { |e| (e.is_a?(Array) || e.is_a?(Hash)) && !e.empty? }
|
59
|
+
|
60
|
+
next key.to_s if hashes_or_arrays.empty?
|
61
|
+
|
57
62
|
hashes_or_arrays.flat_map.with_index { |el, idx|
|
58
63
|
key_paths(el).map { |path| ["#{key}[#{idx}]", *path].join(DOT) }
|
59
64
|
}
|
@@ -11,9 +11,14 @@ module Dry
|
|
11
11
|
# @api private
|
12
12
|
class Value < DSL
|
13
13
|
# @api private
|
14
|
-
def call(*
|
14
|
+
def call(*args, **opts, &block)
|
15
|
+
types, predicates = args.partition { |arg| arg.is_a?(Dry::Types::Type) }
|
16
|
+
|
17
|
+
constructor = types.select { |type| type.is_a?(Dry::Types::Constructor) }.reduce(:>>)
|
15
18
|
schema = predicates.detect { |predicate| predicate.is_a?(Processor) }
|
16
19
|
|
20
|
+
schema_dsl.set_type(name, constructor) if constructor
|
21
|
+
|
17
22
|
type_spec = opts[:type_spec]
|
18
23
|
|
19
24
|
if schema
|
@@ -4,8 +4,9 @@ require "dry/initializer"
|
|
4
4
|
|
5
5
|
require "dry/schema/constants"
|
6
6
|
require "dry/schema/message"
|
7
|
-
require "dry/schema/message_set"
|
8
7
|
require "dry/schema/message_compiler/visitor_opts"
|
8
|
+
require "dry/schema/message_set"
|
9
|
+
require "dry/schema/path"
|
9
10
|
|
10
11
|
module Dry
|
11
12
|
module Schema
|
@@ -109,18 +110,8 @@ module Dry
|
|
109
110
|
end
|
110
111
|
|
111
112
|
# @api private
|
112
|
-
def visit_unexpected_key(node,
|
113
|
-
|
114
|
-
|
115
|
-
msg = messages.translate("errors.unexpected_key")
|
116
|
-
|
117
|
-
Message.new(
|
118
|
-
path: path,
|
119
|
-
meta: msg[:meta] || EMPTY_HASH,
|
120
|
-
text: msg[:text],
|
121
|
-
predicate: nil,
|
122
|
-
input: input
|
123
|
-
)
|
113
|
+
def visit_unexpected_key(node, opts)
|
114
|
+
visit_predicate([:unexpected_key, []], opts.dup.update(path: Path[node.first]))
|
124
115
|
end
|
125
116
|
|
126
117
|
# @api private
|
@@ -71,6 +71,11 @@ module Dry
|
|
71
71
|
@cache ||= Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
|
72
72
|
end
|
73
73
|
|
74
|
+
# @api private
|
75
|
+
def self.source_cache
|
76
|
+
@source_cache ||= Concurrent::Map.new
|
77
|
+
end
|
78
|
+
|
74
79
|
# @api private
|
75
80
|
def initialize(data: EMPTY_HASH, config: nil)
|
76
81
|
super()
|
@@ -179,7 +184,9 @@ module Dry
|
|
179
184
|
|
180
185
|
# @api private
|
181
186
|
def load_translations(path)
|
182
|
-
data = self.class.
|
187
|
+
data = self.class.source_cache.fetch_or_store(path) do
|
188
|
+
self.class.flat_hash(YAML.load_file(path)).freeze
|
189
|
+
end
|
183
190
|
|
184
191
|
return data unless custom_top_namespace?(path)
|
185
192
|
|
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/processor.rb
CHANGED
@@ -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
|
@@ -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,7 +182,7 @@ 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
188
|
if RUBY_VERSION >= "2.7"
|
@@ -182,15 +200,6 @@ module Dry
|
|
182
200
|
def add_error(node)
|
183
201
|
result_ast << node
|
184
202
|
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
203
|
end
|
195
204
|
end
|
196
205
|
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/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.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -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: []
|
@@ -245,14 +246,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
245
246
|
requirements:
|
246
247
|
- - ">="
|
247
248
|
- !ruby/object:Gem::Version
|
248
|
-
version: 2.
|
249
|
+
version: 2.6.0
|
249
250
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
250
251
|
requirements:
|
251
252
|
- - ">="
|
252
253
|
- !ruby/object:Gem::Version
|
253
254
|
version: '0'
|
254
255
|
requirements: []
|
255
|
-
rubygems_version: 3.1.
|
256
|
+
rubygems_version: 3.1.6
|
256
257
|
signing_key:
|
257
258
|
specification_version: 4
|
258
259
|
summary: Coercion and validation for data structures
|