dry-schema 1.5.5 → 1.7.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 +84 -0
- data/LICENSE +1 -1
- data/README.md +4 -3
- data/config/errors.yml +2 -0
- data/dry-schema.gemspec +17 -16
- data/lib/dry/schema/config.rb +1 -1
- data/lib/dry/schema/key.rb +5 -1
- data/lib/dry/schema/key_coercer.rb +1 -1
- data/lib/dry/schema/key_map.rb +1 -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.rb +1 -1
- data/lib/dry/schema/message/or/multi_path.rb +1 -1
- data/lib/dry/schema/message/or/single_path.rb +1 -1
- data/lib/dry/schema/message_compiler.rb +8 -14
- data/lib/dry/schema/message_set.rb +1 -1
- data/lib/dry/schema/messages/abstract.rb +1 -1
- data/lib/dry/schema/messages/template.rb +1 -1
- data/lib/dry/schema/messages/yaml.rb +9 -2
- data/lib/dry/schema/path.rb +10 -63
- data/lib/dry/schema/predicate.rb +1 -1
- data/lib/dry/schema/processor.rb +2 -2
- data/lib/dry/schema/processor_steps.rb +7 -3
- data/lib/dry/schema/result.rb +34 -25
- data/lib/dry/schema/step.rb +14 -33
- data/lib/dry/schema/value_coercer.rb +1 -1
- data/lib/dry/schema/version.rb +1 -1
- metadata +15 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 305c1a8710478195cc35c3de75a1f50834c2cc044b0a6d62bb7c95845ff48db8
|
4
|
+
data.tar.gz: 62254023ae1e6ee82ecf7023e3900906a75ebda86676c59dfa24269d337af7f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08c894bf7682d81654ecd9034ae71e14c10e314997196cf11ee39ddaadf799aabeba33e7398aeb54961ba4b19a67e0d68cb3142ebccc182df18dd64ebca3c725'
|
7
|
+
data.tar.gz: 947264e7d824feededff8608e6e090592287503cb699d11ab31183955c963a01c1ceaeaa977c19d8add8e719a329ed1afbe72d8dd51f86603750c2502fd41a98
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,87 @@
|
|
1
|
+
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
2
|
+
|
3
|
+
## 1.7.0 2021-06-29
|
4
|
+
|
5
|
+
This release ships with a bunch of internal refactorings that should improve performance but if you see any unexpected behavior please do report issues.
|
6
|
+
|
7
|
+
### Fixed
|
8
|
+
|
9
|
+
- Handle arrays of hashes where Array constructor coerces non-Hash input (#351 fixed via #354) (@ojab)
|
10
|
+
- Run outer schema processor steps before inner ones (issue #350 fixed via #361) (@ojab)
|
11
|
+
- Fix key validator false negatives on empty collections (see #363) (@Drenmi)
|
12
|
+
- Prevent error message YAML files from being parsed multiple times (issue #352 via #364) (@alassek)
|
13
|
+
- Using constructor types should work fine now ie `required(:foo).filled(Types::Params::Integer.constructor(&:succ))` (issue #280 fixed via #365) (@solnic)
|
14
|
+
- Handle non-Hash to Hash transformation in `before(:key_coercer)` (issue #350 fixed via #362) (@ojab)
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- [internal] `Dry::Schema::Path` clean up and performance improvements (via #358) (@ojab)
|
19
|
+
- [internal] simplify and speed up handling of steps in nested schemas (via #360) (@ojab)
|
20
|
+
|
21
|
+
[Compare v1.6.2...v1.7.0](https://github.com/dry-rb/dry-schema/compare/v1.6.2...v1.7.0)
|
22
|
+
|
23
|
+
## 1.6.2 2021-04-15
|
24
|
+
|
25
|
+
|
26
|
+
### Added
|
27
|
+
|
28
|
+
- A default error message for `respond_to?` predicate (@rindek)
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
|
32
|
+
- Using `respond_to?` predicate in blocks works now (@rindek)
|
33
|
+
|
34
|
+
|
35
|
+
[Compare v1.6.1...v1.6.2](https://github.com/dry-rb/dry-schema/compare/v1.6.1...v1.6.2)
|
36
|
+
|
37
|
+
## 1.6.1 2021-02-02
|
38
|
+
|
39
|
+
|
40
|
+
### Fixed
|
41
|
+
|
42
|
+
- 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)
|
43
|
+
|
44
|
+
|
45
|
+
[Compare v1.6.0...v1.6.1](https://github.com/dry-rb/dry-schema/compare/v1.6.0...v1.6.1)
|
46
|
+
|
47
|
+
## 1.6.0 2021-01-21
|
48
|
+
|
49
|
+
|
50
|
+
### Fixed
|
51
|
+
|
52
|
+
- Using sum types with a i18n backend no longer crashes (issue #328 fixes via #331) (@tylerhunt)
|
53
|
+
|
54
|
+
### Changed
|
55
|
+
|
56
|
+
- Inferring predicates from class names is deprecated. It's very unlikely your code depends on it,
|
57
|
+
however, if it does, you'll get an exception with instructions. (@flash-gordon)
|
58
|
+
|
59
|
+
If you see an exception and don't rely on inferring, just disable it with:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Dry::Schema::PredicateInferrer::Compiler.infer_predicate_by_class_name false
|
63
|
+
```
|
64
|
+
|
65
|
+
Otherwise, enable it explicitly:
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
Dry::Schema::PredicateInferrer::Compiler.infer_predicate_by_class_name true
|
69
|
+
```
|
70
|
+
|
71
|
+
See https://github.com/dry-rb/dry-schema/issues/335 for rationale.
|
72
|
+
|
73
|
+
[Compare v1.5.6...v1.6.0](https://github.com/dry-rb/dry-schema/compare/v1.5.6...v1.6.0)
|
74
|
+
|
75
|
+
## 1.5.6 2020-10-21
|
76
|
+
|
77
|
+
|
78
|
+
### Fixed
|
79
|
+
|
80
|
+
- Fixed stack error which was a regression introduced in 1.5.5 (issue #322 fixed via #323) (@flash-gordon)
|
81
|
+
|
82
|
+
|
83
|
+
[Compare v1.5.5...v1.5.6](https://github.com/dry-rb/dry-schema/compare/v1.5.5...v1.5.6)
|
84
|
+
|
1
85
|
## 1.5.5 2020-10-08
|
2
86
|
|
3
87
|
|
data/LICENSE
CHANGED
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
|
@@ -14,15 +15,15 @@
|
|
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,28 +18,28 @@ 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"
|
36
38
|
spec.add_runtime_dependency "dry-configurable", "~> 0.8", ">= 0.8.3"
|
37
|
-
spec.add_runtime_dependency "dry-core", "~> 0.
|
38
|
-
spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
|
39
|
+
spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
|
39
40
|
spec.add_runtime_dependency "dry-initializer", "~> 3.0"
|
40
41
|
spec.add_runtime_dependency "dry-logic", "~> 1.0"
|
41
|
-
spec.add_runtime_dependency "dry-types", "~> 1.
|
42
|
+
spec.add_runtime_dependency "dry-types", "~> 1.5"
|
42
43
|
|
43
44
|
spec.add_development_dependency "bundler"
|
44
45
|
spec.add_development_dependency "rake"
|
data/lib/dry/schema/config.rb
CHANGED
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
|
|
data/lib/dry/schema/key_map.rb
CHANGED
@@ -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
|
data/lib/dry/schema/message.rb
CHANGED
@@ -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
|
@@ -131,7 +122,10 @@ module Dry
|
|
131
122
|
|
132
123
|
# @api private
|
133
124
|
def or_translator
|
134
|
-
@or_translator ||= proc { |k|
|
125
|
+
@or_translator ||= proc { |k|
|
126
|
+
message = messages.translate(k, **default_lookup_options)
|
127
|
+
message.is_a?(Hash) ? message[:text] : message
|
128
|
+
}
|
135
129
|
end
|
136
130
|
|
137
131
|
# @api private
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "yaml"
|
4
4
|
require "pathname"
|
5
5
|
|
6
|
-
require "dry/equalizer"
|
6
|
+
require "dry/core/equalizer"
|
7
7
|
require "dry/schema/constants"
|
8
8
|
require "dry/schema/messages/abstract"
|
9
9
|
|
@@ -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
|
@@ -86,61 +71,26 @@ module Dry
|
|
86
71
|
end
|
87
72
|
|
88
73
|
# @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
|
-
# @api private
|
98
|
-
#
|
99
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
100
74
|
def include?(other)
|
101
|
-
|
102
|
-
return last.equal?(other.last) if index? && other.index?
|
103
|
-
return without_index.include?(other) if index?
|
104
|
-
|
105
|
-
if !index? && other.index?
|
106
|
-
path = key_matches(other, :select)
|
107
|
-
|
108
|
-
return false unless path.size > 1
|
109
|
-
|
110
|
-
self.class.new(path).include?(other)
|
111
|
-
end
|
112
|
-
|
113
|
-
self >= other && !other.key_matches(self).include?(nil)
|
75
|
+
keys[0, other.keys.length].eql?(other.keys)
|
114
76
|
end
|
115
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
116
77
|
|
117
78
|
# @api private
|
118
79
|
def <=>(other)
|
119
|
-
|
120
|
-
|
121
|
-
return 0 if keys.eql?(other.keys)
|
80
|
+
return keys.length <=> other.keys.length if include?(other) || other.include?(self)
|
122
81
|
|
123
|
-
|
82
|
+
first_uncommon_index = (self & other).keys.length
|
124
83
|
|
125
|
-
|
84
|
+
keys[first_uncommon_index] <=> other.keys[first_uncommon_index]
|
126
85
|
end
|
127
86
|
|
128
87
|
# @api private
|
129
88
|
def &(other)
|
130
|
-
unless same_root?(other)
|
131
|
-
raise ArgumentError, "#{other.inspect} doesn't have the same root #{inspect}"
|
132
|
-
end
|
133
|
-
|
134
89
|
self.class.new(
|
135
|
-
|
90
|
+
keys.take_while.with_index { |key, index| other.keys[index].eql?(key) }
|
136
91
|
)
|
137
92
|
end
|
138
93
|
|
139
|
-
# @api private
|
140
|
-
def key_matches(other, meth = :map)
|
141
|
-
public_send(meth) { |key| (idx = other.index(key)) && keys[idx].equal?(key) }
|
142
|
-
end
|
143
|
-
|
144
94
|
# @api private
|
145
95
|
def last
|
146
96
|
keys.last
|
@@ -151,10 +101,7 @@ module Dry
|
|
151
101
|
root.equal?(other.root)
|
152
102
|
end
|
153
103
|
|
154
|
-
|
155
|
-
def index?
|
156
|
-
last.is_a?(Integer)
|
157
|
-
end
|
104
|
+
EMPTY = new(EMPTY_ARRAY).freeze
|
158
105
|
end
|
159
106
|
end
|
160
107
|
end
|
data/lib/dry/schema/predicate.rb
CHANGED
data/lib/dry/schema/processor.rb
CHANGED
@@ -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
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "dry/initializer"
|
4
|
-
require "dry/equalizer"
|
4
|
+
require "dry/core/equalizer"
|
5
5
|
|
6
6
|
require "dry/schema/path"
|
7
7
|
|
@@ -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.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: 2021-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -50,28 +50,20 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '0.
|
54
|
-
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - "~>"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '0.4'
|
61
|
-
- !ruby/object:Gem::Dependency
|
62
|
-
name: dry-equalizer
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
64
|
-
requirements:
|
65
|
-
- - "~>"
|
53
|
+
version: '0.5'
|
54
|
+
- - ">="
|
66
55
|
- !ruby/object:Gem::Version
|
67
|
-
version: '0.
|
56
|
+
version: '0.5'
|
68
57
|
type: :runtime
|
69
58
|
prerelease: false
|
70
59
|
version_requirements: !ruby/object:Gem::Requirement
|
71
60
|
requirements:
|
72
61
|
- - "~>"
|
73
62
|
- !ruby/object:Gem::Version
|
74
|
-
version: '0.
|
63
|
+
version: '0.5'
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0.5'
|
75
67
|
- !ruby/object:Gem::Dependency
|
76
68
|
name: dry-initializer
|
77
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -106,14 +98,14 @@ dependencies:
|
|
106
98
|
requirements:
|
107
99
|
- - "~>"
|
108
100
|
- !ruby/object:Gem::Version
|
109
|
-
version: '1.
|
101
|
+
version: '1.5'
|
110
102
|
type: :runtime
|
111
103
|
prerelease: false
|
112
104
|
version_requirements: !ruby/object:Gem::Requirement
|
113
105
|
requirements:
|
114
106
|
- - "~>"
|
115
107
|
- !ruby/object:Gem::Version
|
116
|
-
version: '1.
|
108
|
+
version: '1.5'
|
117
109
|
- !ruby/object:Gem::Dependency
|
118
110
|
name: bundler
|
119
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,10 +148,11 @@ dependencies:
|
|
156
148
|
- - ">="
|
157
149
|
- !ruby/object:Gem::Version
|
158
150
|
version: '0'
|
159
|
-
description:
|
151
|
+
description: |+
|
160
152
|
dry-schema provides a DSL for defining schemas with keys and rules that should be applied to
|
161
153
|
values. It supports coercion, input sanitization, custom types and localized error messages
|
162
154
|
(with or without I18n gem). It's also used as the schema engine in dry-validation.
|
155
|
+
|
163
156
|
email:
|
164
157
|
- piotr.solnica@gmail.com
|
165
158
|
executables: []
|
@@ -253,14 +246,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
253
246
|
requirements:
|
254
247
|
- - ">="
|
255
248
|
- !ruby/object:Gem::Version
|
256
|
-
version: 2.
|
249
|
+
version: 2.6.0
|
257
250
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
258
251
|
requirements:
|
259
252
|
- - ">="
|
260
253
|
- !ruby/object:Gem::Version
|
261
254
|
version: '0'
|
262
255
|
requirements: []
|
263
|
-
rubygems_version: 3.
|
256
|
+
rubygems_version: 3.1.6
|
264
257
|
signing_key:
|
265
258
|
specification_version: 4
|
266
259
|
summary: Coercion and validation for data structures
|