dry-schema 1.13.1 → 1.14.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 +57 -0
- data/README.md +2 -2
- data/config/errors.yml +10 -0
- data/dry-schema.gemspec +15 -17
- data/lib/dry/schema/config.rb +9 -7
- data/lib/dry/schema/constants.rb +2 -2
- data/lib/dry/schema/dsl.rb +29 -21
- data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +1 -1
- data/lib/dry/schema/extensions/info/schema_compiler.rb +16 -7
- data/lib/dry/schema/extensions/json_schema/schema_compiler.rb +4 -4
- data/lib/dry/schema/extensions/monads.rb +1 -1
- data/lib/dry/schema/extensions/struct.rb +28 -6
- data/lib/dry/schema/key.rb +4 -4
- data/lib/dry/schema/key_coercer.rb +3 -3
- data/lib/dry/schema/key_map.rb +5 -5
- data/lib/dry/schema/key_validator.rb +21 -10
- data/lib/dry/schema/macros/core.rb +2 -2
- data/lib/dry/schema/macros/dsl.rb +1 -1
- data/lib/dry/schema/macros/filled.rb +1 -2
- data/lib/dry/schema/macros/hash.rb +1 -1
- data/lib/dry/schema/macros/schema.rb +3 -3
- data/lib/dry/schema/macros/value.rb +12 -12
- data/lib/dry/schema/message.rb +4 -4
- data/lib/dry/schema/message_compiler.rb +8 -8
- data/lib/dry/schema/message_set.rb +8 -8
- data/lib/dry/schema/messages/abstract.rb +12 -12
- data/lib/dry/schema/messages/namespaced.rb +1 -1
- data/lib/dry/schema/messages/template.rb +2 -2
- data/lib/dry/schema/messages/yaml.rb +3 -3
- data/lib/dry/schema/path.rb +12 -12
- data/lib/dry/schema/predicate.rb +4 -4
- data/lib/dry/schema/predicate_registry.rb +2 -2
- data/lib/dry/schema/processor.rb +10 -10
- data/lib/dry/schema/processor_steps.rb +1 -1
- data/lib/dry/schema/result.rb +6 -6
- data/lib/dry/schema/rule_applier.rb +1 -1
- data/lib/dry/schema/trace.rb +1 -1
- data/lib/dry/schema/type_container.rb +2 -2
- data/lib/dry/schema/type_registry.rb +1 -1
- data/lib/dry/schema/types.rb +1 -1
- data/lib/dry/schema/types_merger.rb +18 -62
- data/lib/dry/schema/value_coercer.rb +1 -1
- data/lib/dry/schema/version.rb +1 -1
- data/lib/dry/schema.rb +12 -11
- metadata +21 -79
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b6967bfe7737c11a9df20fc846698f42a911e454cb8c1621d20bc69d93e53dca
|
|
4
|
+
data.tar.gz: 0a97d750986808ca32535839e779485018d9a7eba1d6a5f09f2128e93f8bbaef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b42eb4a52a991fdf5605d8dc69b6b7362578ef140e272a2cff4ef0fc3852e01797714dfbe2507f9aa05ef6885b738413bd8f93536e4aabb8cccbd1c8a23e7cf1
|
|
7
|
+
data.tar.gz: a59cc89f18b981031fc7df87e0c3df9692512617dc0bdc35a417a5dce55196788262969ea742da0957a0616a9a0f648c78b4905abb031977570da0751002572b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
<!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
|
|
2
2
|
|
|
3
|
+
## 1.14.0 2025-01-06
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Better support for sets in `excluded_from?` and `included_in?` predicates (via #480) (@flash-gordon)
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fix info extension for maybe macro (via #484) (@flash-gordon + @santiagodoldan)
|
|
13
|
+
- Missing message for :uri? predicate (via #477) (@timjnh)
|
|
14
|
+
- JSON schema output for `maybe(:array)` (via #463) (@tomgi)
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Set minimum Ruby version to 3.1 (@flash-gordon)
|
|
19
|
+
- `KeyValidator` works faster for large schemas (via #461) (@radarek)
|
|
20
|
+
|
|
21
|
+
[Compare v1.13.4...v1.14.0](https://github.com/dry-rb/dry-schema/compare/v1.13.4...v1.14.0)
|
|
22
|
+
|
|
23
|
+
## 1.13.4 2024-05-22
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- Default error message for `:uri?` (issue #476 via #477) (@timjnh)
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Fix json-schema type of objects nested under arrays (issue #400 fixed via #462) (@tomgi)
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- i18n backend is no longer eager-loaded (via 85a9e0b) (@adam12)
|
|
37
|
+
|
|
38
|
+
[Compare v1.13.3...v1.13.4](https://github.com/dry-rb/dry-schema/compare/v1.13.3...v1.13.4)
|
|
39
|
+
|
|
40
|
+
## 1.13.3 2023-08-26
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- Fix struct extension for nested struct definitions (via #466) (@flash-gordon)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
[Compare v1.13.2...v1.13.3](https://github.com/dry-rb/dry-schema/compare/v1.13.2...v1.13.3)
|
|
49
|
+
|
|
50
|
+
## 1.13.2 2023-05-31
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- Fix custom predicates setting (via #460) (@solnic)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
[Compare v1.13.1...v1.13.2](https://github.com/dry-rb/dry-schema/compare/v1.13.1...v1.13.2)
|
|
59
|
+
|
|
3
60
|
## 1.13.1 2023-04-07
|
|
4
61
|
|
|
5
62
|
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[gem]: https://rubygems.org/gems/dry-schema
|
|
3
3
|
[actions]: https://github.com/dry-rb/dry-schema/actions
|
|
4
4
|
|
|
5
|
-
# dry-schema [][gem] [][gem] [][actions]
|
|
6
6
|
|
|
7
7
|
## Links
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
This library officially supports the following Ruby versions:
|
|
16
16
|
|
|
17
|
-
* MRI `>= 3.
|
|
17
|
+
* MRI `>= 3.1`
|
|
18
18
|
* jruby `>= 9.4` (not tested on CI)
|
|
19
19
|
|
|
20
20
|
## License
|
data/config/errors.yml
CHANGED
|
@@ -105,7 +105,17 @@ en:
|
|
|
105
105
|
default: "must be %{size} bytes long"
|
|
106
106
|
range: "must be within %{size_left} - %{size_right} bytes long"
|
|
107
107
|
|
|
108
|
+
uri?: "is not a valid URI"
|
|
109
|
+
|
|
110
|
+
uuid_v1?: "is not a valid UUID"
|
|
111
|
+
|
|
112
|
+
uuid_v2?: "is not a valid UUID"
|
|
113
|
+
|
|
114
|
+
uuid_v3?: "is not a valid UUID"
|
|
115
|
+
|
|
108
116
|
uuid_v4?: "is not a valid UUID"
|
|
109
117
|
|
|
118
|
+
uuid_v5?: "is not a valid UUID"
|
|
119
|
+
|
|
110
120
|
not:
|
|
111
121
|
empty?: "cannot be empty"
|
data/dry-schema.gemspec
CHANGED
|
@@ -21,28 +21,26 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
|
|
22
22
|
TEXT
|
|
23
23
|
spec.homepage = "https://dry-rb.org/gems/dry-schema"
|
|
24
|
-
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-schema.gemspec",
|
|
24
|
+
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-schema.gemspec",
|
|
25
|
+
"lib/**/*", "config/*.yml"]
|
|
25
26
|
spec.bindir = "bin"
|
|
26
27
|
spec.executables = []
|
|
27
28
|
spec.require_paths = ["lib"]
|
|
28
29
|
|
|
29
|
-
spec.metadata["allowed_push_host"]
|
|
30
|
-
spec.metadata["changelog_uri"]
|
|
31
|
-
spec.metadata["source_code_uri"]
|
|
32
|
-
spec.metadata["bug_tracker_uri"]
|
|
30
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
31
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-schema/blob/main/CHANGELOG.md"
|
|
32
|
+
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-schema"
|
|
33
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-schema/issues"
|
|
34
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
33
35
|
|
|
34
|
-
spec.required_ruby_version = ">=
|
|
36
|
+
spec.required_ruby_version = ">= 3.1"
|
|
35
37
|
|
|
36
38
|
# to update dependencies edit project.yml
|
|
37
|
-
spec.
|
|
38
|
-
spec.
|
|
39
|
-
spec.
|
|
40
|
-
spec.
|
|
41
|
-
spec.
|
|
42
|
-
spec.
|
|
43
|
-
spec.
|
|
44
|
-
|
|
45
|
-
spec.add_development_dependency "bundler"
|
|
46
|
-
spec.add_development_dependency "rake"
|
|
47
|
-
spec.add_development_dependency "rspec"
|
|
39
|
+
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
|
40
|
+
spec.add_dependency "dry-configurable", "~> 1.0", ">= 1.0.1"
|
|
41
|
+
spec.add_dependency "dry-core", "~> 1.1"
|
|
42
|
+
spec.add_dependency "dry-initializer", "~> 3.2"
|
|
43
|
+
spec.add_dependency "dry-logic", "~> 1.5"
|
|
44
|
+
spec.add_dependency "dry-types", "~> 1.8"
|
|
45
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
|
48
46
|
end
|
data/lib/dry/schema/config.rb
CHANGED
|
@@ -12,8 +12,8 @@ module Dry
|
|
|
12
12
|
#
|
|
13
13
|
# @api public
|
|
14
14
|
class Config
|
|
15
|
-
include Dry::Configurable
|
|
16
|
-
include Dry::Equalizer(:to_h, inspect: false)
|
|
15
|
+
include ::Dry::Configurable
|
|
16
|
+
include ::Dry::Equalizer(:to_h, inspect: false)
|
|
17
17
|
|
|
18
18
|
# @!method predicates
|
|
19
19
|
#
|
|
@@ -22,7 +22,9 @@ module Dry
|
|
|
22
22
|
# @return [Schema::PredicateRegistry]
|
|
23
23
|
#
|
|
24
24
|
# @api public
|
|
25
|
-
setting :predicates, default:
|
|
25
|
+
setting :predicates, default: PredicateRegistry.new, constructor: -> predicates {
|
|
26
|
+
predicates.is_a?(PredicateRegistry) ? predicates : PredicateRegistry.new(predicates)
|
|
27
|
+
}
|
|
26
28
|
|
|
27
29
|
# @!method types
|
|
28
30
|
#
|
|
@@ -31,7 +33,7 @@ module Dry
|
|
|
31
33
|
# @return [Hash]
|
|
32
34
|
#
|
|
33
35
|
# @api public
|
|
34
|
-
setting :types, default: Dry::Types
|
|
36
|
+
setting :types, default: ::Dry::Types
|
|
35
37
|
|
|
36
38
|
# @!method messages
|
|
37
39
|
#
|
|
@@ -43,7 +45,7 @@ module Dry
|
|
|
43
45
|
setting :messages do
|
|
44
46
|
setting :backend, default: :yaml
|
|
45
47
|
setting :namespace
|
|
46
|
-
setting :load_paths, default: Set[DEFAULT_MESSAGES_PATH], constructor: :dup.to_proc
|
|
48
|
+
setting :load_paths, default: ::Set[DEFAULT_MESSAGES_PATH], constructor: :dup.to_proc
|
|
47
49
|
setting :top_namespace, default: DEFAULT_MESSAGES_ROOT
|
|
48
50
|
setting :default_locale
|
|
49
51
|
end
|
|
@@ -72,9 +74,9 @@ module Dry
|
|
|
72
74
|
# Forward to the underlying config object
|
|
73
75
|
#
|
|
74
76
|
# @api private
|
|
75
|
-
def method_missing(meth,
|
|
77
|
+
def method_missing(meth, ...)
|
|
76
78
|
super unless config.respond_to?(meth)
|
|
77
|
-
config.public_send(meth,
|
|
79
|
+
config.public_send(meth, ...)
|
|
78
80
|
end
|
|
79
81
|
end
|
|
80
82
|
end
|
data/lib/dry/schema/constants.rb
CHANGED
|
@@ -30,10 +30,10 @@ module Dry
|
|
|
30
30
|
DEFAULT_MESSAGES_ROOT = "dry_schema"
|
|
31
31
|
|
|
32
32
|
# An error raised when DSL is used in an incorrect way
|
|
33
|
-
InvalidSchemaError = Class.new(StandardError)
|
|
33
|
+
InvalidSchemaError = ::Class.new(::StandardError)
|
|
34
34
|
|
|
35
35
|
# An error raised when a localized message cannot be found
|
|
36
|
-
MissingMessageError = Class.new(StandardError) do
|
|
36
|
+
MissingMessageError = ::Class.new(::StandardError) do
|
|
37
37
|
# @api private
|
|
38
38
|
def initialize(path, paths = [])
|
|
39
39
|
*rest, rule = path
|
data/lib/dry/schema/dsl.rb
CHANGED
|
@@ -37,10 +37,7 @@ module Dry
|
|
|
37
37
|
class DSL
|
|
38
38
|
Types = Schema::Types
|
|
39
39
|
|
|
40
|
-
extend Dry::Initializer
|
|
41
|
-
|
|
42
|
-
# @return [Compiler] The rule compiler object
|
|
43
|
-
option :compiler, default: -> { Compiler.new }
|
|
40
|
+
extend ::Dry::Initializer
|
|
44
41
|
|
|
45
42
|
# @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
|
|
46
43
|
option :processor_type, default: -> { Processor }
|
|
@@ -81,9 +78,10 @@ module Dry
|
|
|
81
78
|
# @return [DSL]
|
|
82
79
|
#
|
|
83
80
|
# @api public
|
|
84
|
-
def self.new(**options, &
|
|
81
|
+
def self.new(**options, &)
|
|
85
82
|
dsl = super
|
|
86
|
-
dsl.instance_eval(&
|
|
83
|
+
dsl.instance_eval(&) if block_given?
|
|
84
|
+
dsl.instance_variable_set("@compiler", options[:compiler]) if options[:compiler]
|
|
87
85
|
dsl
|
|
88
86
|
end
|
|
89
87
|
|
|
@@ -101,11 +99,21 @@ module Dry
|
|
|
101
99
|
# @return [DSL]
|
|
102
100
|
#
|
|
103
101
|
# @api public
|
|
104
|
-
def configure(&
|
|
105
|
-
config.configure(&
|
|
102
|
+
def configure(&)
|
|
103
|
+
config.configure(&)
|
|
106
104
|
self
|
|
107
105
|
end
|
|
108
106
|
|
|
107
|
+
# @api private
|
|
108
|
+
def compiler
|
|
109
|
+
@compiler ||= Compiler.new(predicates)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @api private
|
|
113
|
+
def predicates
|
|
114
|
+
@predicates ||= config.predicates
|
|
115
|
+
end
|
|
116
|
+
|
|
109
117
|
# Return a macro with the provided name
|
|
110
118
|
#
|
|
111
119
|
# @param [Symbol] name
|
|
@@ -133,8 +141,8 @@ module Dry
|
|
|
133
141
|
# @return [Macros::Required]
|
|
134
142
|
#
|
|
135
143
|
# @api public
|
|
136
|
-
def required(name, &
|
|
137
|
-
key(name, macro: Macros::Required, &
|
|
144
|
+
def required(name, &)
|
|
145
|
+
key(name, macro: Macros::Required, &)
|
|
138
146
|
end
|
|
139
147
|
|
|
140
148
|
# Define an optional key
|
|
@@ -149,8 +157,8 @@ module Dry
|
|
|
149
157
|
# @return [Macros::Optional]
|
|
150
158
|
#
|
|
151
159
|
# @api public
|
|
152
|
-
def optional(name, &
|
|
153
|
-
key(name, macro: Macros::Optional, &
|
|
160
|
+
def optional(name, &)
|
|
161
|
+
key(name, macro: Macros::Optional, &)
|
|
154
162
|
end
|
|
155
163
|
|
|
156
164
|
# A generic method for defining keys
|
|
@@ -162,7 +170,7 @@ module Dry
|
|
|
162
170
|
# @return [Macros::Key]
|
|
163
171
|
#
|
|
164
172
|
# @api public
|
|
165
|
-
def key(name, macro:, &
|
|
173
|
+
def key(name, macro:, &)
|
|
166
174
|
raise ArgumentError, "Key +#{name}+ is not a symbol" unless name.is_a?(::Symbol)
|
|
167
175
|
|
|
168
176
|
set_type(name, Types::Any.meta(default: true))
|
|
@@ -174,7 +182,7 @@ module Dry
|
|
|
174
182
|
filter_schema_dsl: filter_schema_dsl
|
|
175
183
|
)
|
|
176
184
|
|
|
177
|
-
macro.value(&
|
|
185
|
+
macro.value(&) if block_given?
|
|
178
186
|
macros << macro
|
|
179
187
|
macro
|
|
180
188
|
end
|
|
@@ -241,8 +249,8 @@ module Dry
|
|
|
241
249
|
# @return [DSL]
|
|
242
250
|
#
|
|
243
251
|
# @api public
|
|
244
|
-
def before(key, &
|
|
245
|
-
steps.before(key, &
|
|
252
|
+
def before(key, &)
|
|
253
|
+
steps.before(key, &)
|
|
246
254
|
self
|
|
247
255
|
end
|
|
248
256
|
|
|
@@ -256,8 +264,8 @@ module Dry
|
|
|
256
264
|
# @return [DSL]
|
|
257
265
|
#
|
|
258
266
|
# @api public
|
|
259
|
-
def after(key, &
|
|
260
|
-
steps.after(key, &
|
|
267
|
+
def after(key, &)
|
|
268
|
+
steps.after(key, &)
|
|
261
269
|
self
|
|
262
270
|
end
|
|
263
271
|
|
|
@@ -293,8 +301,8 @@ module Dry
|
|
|
293
301
|
# @return [Dry::Types::Safe]
|
|
294
302
|
#
|
|
295
303
|
# @api private
|
|
296
|
-
def new(klass: self.class, **options, &
|
|
297
|
-
klass.new(**options, processor_type: processor_type, config: config, &
|
|
304
|
+
def new(klass: self.class, **options, &)
|
|
305
|
+
klass.new(**options, processor_type: processor_type, config: config, &)
|
|
298
306
|
end
|
|
299
307
|
|
|
300
308
|
# Set a type for the given key name
|
|
@@ -493,7 +501,7 @@ module Dry
|
|
|
493
501
|
def default_config
|
|
494
502
|
parents.each_cons(2) do |left, right|
|
|
495
503
|
unless left.config == right.config
|
|
496
|
-
raise ArgumentError,
|
|
504
|
+
raise ::ArgumentError,
|
|
497
505
|
"Parent configs differ, left=#{left.inspect}, right=#{right.inspect}"
|
|
498
506
|
end
|
|
499
507
|
end
|
|
@@ -70,20 +70,25 @@ module Dry
|
|
|
70
70
|
|
|
71
71
|
# @api private
|
|
72
72
|
def visit_implication(node, opts = EMPTY_HASH)
|
|
73
|
-
node
|
|
74
|
-
|
|
73
|
+
case node
|
|
74
|
+
in [:not, [:predicate, [:nil?, _]]], el
|
|
75
|
+
visit(el, {**opts, nullable: true})
|
|
76
|
+
else
|
|
77
|
+
node.each do |el|
|
|
78
|
+
visit(el, {**opts, required: false})
|
|
79
|
+
end
|
|
75
80
|
end
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
# @api private
|
|
79
84
|
def visit_each(node, opts = EMPTY_HASH)
|
|
80
|
-
visit(node, opts
|
|
85
|
+
visit(node, {**opts, member: true})
|
|
81
86
|
end
|
|
82
87
|
|
|
83
88
|
# @api private
|
|
84
89
|
def visit_key(node, opts = EMPTY_HASH)
|
|
85
90
|
name, rest = node
|
|
86
|
-
visit(rest, opts
|
|
91
|
+
visit(rest, {**opts, key: name, required: true})
|
|
87
92
|
end
|
|
88
93
|
|
|
89
94
|
# @api private
|
|
@@ -93,19 +98,23 @@ module Dry
|
|
|
93
98
|
key = opts[:key]
|
|
94
99
|
|
|
95
100
|
if name.equal?(:key?)
|
|
96
|
-
keys[rest[0][1]] = {
|
|
101
|
+
keys[rest[0][1]] = {
|
|
102
|
+
required: opts.fetch(:required, true)
|
|
103
|
+
}
|
|
97
104
|
else
|
|
98
105
|
type = PREDICATE_TO_TYPE[name]
|
|
99
|
-
|
|
106
|
+
nullable = opts.fetch(:nullable, false)
|
|
107
|
+
assign_type(key, type, nullable) if type
|
|
100
108
|
end
|
|
101
109
|
end
|
|
102
110
|
|
|
103
111
|
# @api private
|
|
104
|
-
def assign_type(key, type)
|
|
112
|
+
def assign_type(key, type, nullable)
|
|
105
113
|
if keys[key][:type]
|
|
106
114
|
keys[key][:member] = type
|
|
107
115
|
else
|
|
108
116
|
keys[key][:type] = type
|
|
117
|
+
keys[key][:nullable] = nullable
|
|
109
118
|
end
|
|
110
119
|
end
|
|
111
120
|
end
|
|
@@ -9,7 +9,7 @@ module Dry
|
|
|
9
9
|
# @api private
|
|
10
10
|
class SchemaCompiler
|
|
11
11
|
# An error raised when a predicate cannot be converted
|
|
12
|
-
UnknownConversionError = Class.new(StandardError)
|
|
12
|
+
UnknownConversionError = ::Class.new(::StandardError)
|
|
13
13
|
|
|
14
14
|
IDENTITY = ->(v, _) { v }.freeze
|
|
15
15
|
TO_INTEGER = ->(v, _) { v.to_i }.freeze
|
|
@@ -90,14 +90,14 @@ module Dry
|
|
|
90
90
|
def visit_set(node, opts = EMPTY_HASH)
|
|
91
91
|
target = (key = opts[:key]) ? self.class.new(loose: loose?) : self
|
|
92
92
|
|
|
93
|
-
node.map { |child| target.visit(child, opts) }
|
|
93
|
+
node.map { |child| target.visit(child, opts.except(:member)) }
|
|
94
94
|
|
|
95
95
|
return unless key
|
|
96
96
|
|
|
97
97
|
target_info = opts[:member] ? {items: target.to_h} : target.to_h
|
|
98
98
|
type = opts[:member] ? "array" : "object"
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
merge_opts!(keys[key], {type: type, **target_info})
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
# @api private
|
|
@@ -210,7 +210,7 @@ module Dry
|
|
|
210
210
|
orig_type = orig_opts[:type]
|
|
211
211
|
|
|
212
212
|
if orig_type && new_type && orig_type != new_type
|
|
213
|
-
new_opts[:type] = [orig_type, new_type]
|
|
213
|
+
new_opts[:type] = [orig_type, new_type].flatten.uniq
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
orig_opts.merge!(new_opts)
|
|
@@ -5,24 +5,46 @@ require "dry/struct"
|
|
|
5
5
|
module Dry
|
|
6
6
|
module Schema
|
|
7
7
|
module Macros
|
|
8
|
+
class StructToSchema < ::Dry::Struct::Compiler
|
|
9
|
+
def call(struct)
|
|
10
|
+
visit(struct.to_ast)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# strip away structs from AST
|
|
14
|
+
def visit_struct(node)
|
|
15
|
+
_, ast = node
|
|
16
|
+
visit(ast)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Hash.option :struct_compiler, default: proc { StructToSchema.new(schema_dsl.config.types) }
|
|
21
|
+
|
|
8
22
|
Hash.prepend(::Module.new {
|
|
9
23
|
def call(*args)
|
|
10
|
-
if args.size >= 1 &&
|
|
24
|
+
if args.size >= 1 && struct?(args[0])
|
|
11
25
|
if block_given?
|
|
12
|
-
raise ArgumentError, "blocks are not supported when using "\
|
|
26
|
+
raise ArgumentError, "blocks are not supported when using " \
|
|
13
27
|
"a struct class (#{name.inspect} => #{args[0]})"
|
|
14
28
|
end
|
|
15
29
|
|
|
16
|
-
|
|
17
|
-
|
|
30
|
+
schema = struct_compiler.(args[0])
|
|
31
|
+
|
|
32
|
+
super(schema, *args.drop(1))
|
|
33
|
+
type(schema_dsl.types[name].constructor(schema))
|
|
18
34
|
else
|
|
19
35
|
super
|
|
20
36
|
end
|
|
21
37
|
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def struct?(type)
|
|
42
|
+
type.is_a?(::Class) && type <= ::Dry::Struct
|
|
43
|
+
end
|
|
22
44
|
})
|
|
23
45
|
end
|
|
24
46
|
|
|
25
|
-
PredicateInferrer::Compiler.
|
|
26
|
-
PrimitiveInferrer::Compiler.
|
|
47
|
+
PredicateInferrer::Compiler.alias_method(:visit_struct, :visit_hash)
|
|
48
|
+
PrimitiveInferrer::Compiler.alias_method(:visit_struct, :visit_hash)
|
|
27
49
|
end
|
|
28
50
|
end
|
data/lib/dry/schema/key.rb
CHANGED
|
@@ -6,11 +6,11 @@ module Dry
|
|
|
6
6
|
#
|
|
7
7
|
# @api public
|
|
8
8
|
class Key
|
|
9
|
-
extend Dry::Core::Cache
|
|
9
|
+
extend ::Dry::Core::Cache
|
|
10
10
|
|
|
11
11
|
DEFAULT_COERCER = :itself.to_proc.freeze
|
|
12
12
|
|
|
13
|
-
include Dry.Equalizer(:name, :coercer)
|
|
13
|
+
include ::Dry.Equalizer(:name, :coercer)
|
|
14
14
|
|
|
15
15
|
# @return [Symbol] The key identifier
|
|
16
16
|
attr_reader :id
|
|
@@ -90,7 +90,7 @@ module Dry
|
|
|
90
90
|
#
|
|
91
91
|
# @api private
|
|
92
92
|
class Hash < self
|
|
93
|
-
include Dry.Equalizer(:name, :members, :coercer)
|
|
93
|
+
include ::Dry.Equalizer(:name, :members, :coercer)
|
|
94
94
|
|
|
95
95
|
# @api private
|
|
96
96
|
attr_reader :members
|
|
@@ -137,7 +137,7 @@ module Dry
|
|
|
137
137
|
#
|
|
138
138
|
# @api private
|
|
139
139
|
class Array < self
|
|
140
|
-
include Dry.Equalizer(:name, :member, :coercer)
|
|
140
|
+
include ::Dry.Equalizer(:name, :member, :coercer)
|
|
141
141
|
|
|
142
142
|
attr_reader :member
|
|
143
143
|
|
|
@@ -9,7 +9,7 @@ module Dry
|
|
|
9
9
|
#
|
|
10
10
|
# @api private
|
|
11
11
|
class KeyCoercer
|
|
12
|
-
extend Dry::Core::Cache
|
|
12
|
+
extend ::Dry::Core::Cache
|
|
13
13
|
include ::Dry::Equalizer(:key_map, :coercer)
|
|
14
14
|
|
|
15
15
|
TO_SYM = :to_sym.to_proc.freeze
|
|
@@ -27,8 +27,8 @@ module Dry
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# @api private
|
|
30
|
-
def initialize(key_map, &
|
|
31
|
-
@key_map = key_map.coercible(&
|
|
30
|
+
def initialize(key_map, &)
|
|
31
|
+
@key_map = key_map.coercible(&)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# @api private
|
data/lib/dry/schema/key_map.rb
CHANGED
|
@@ -13,10 +13,10 @@ module Dry
|
|
|
13
13
|
#
|
|
14
14
|
# @api public
|
|
15
15
|
class KeyMap
|
|
16
|
-
extend Dry::Core::Cache
|
|
16
|
+
extend ::Dry::Core::Cache
|
|
17
17
|
|
|
18
|
-
include Dry.Equalizer(:keys)
|
|
19
|
-
include Enumerable
|
|
18
|
+
include ::Dry.Equalizer(:keys)
|
|
19
|
+
include ::Enumerable
|
|
20
20
|
|
|
21
21
|
# @return [Array<Key>] A list of defined key objects
|
|
22
22
|
attr_reader :keys
|
|
@@ -105,8 +105,8 @@ module Dry
|
|
|
105
105
|
# Iterate over keys
|
|
106
106
|
#
|
|
107
107
|
# @api public
|
|
108
|
-
def each(&
|
|
109
|
-
keys.each(&
|
|
108
|
+
def each(&)
|
|
109
|
+
keys.each(&)
|
|
110
110
|
end
|
|
111
111
|
|
|
112
112
|
# Return a new key map merged with the provided one
|
|
@@ -7,10 +7,10 @@ module Dry
|
|
|
7
7
|
module Schema
|
|
8
8
|
# @api private
|
|
9
9
|
class KeyValidator
|
|
10
|
-
extend Dry::Initializer
|
|
10
|
+
extend ::Dry::Initializer
|
|
11
11
|
|
|
12
|
-
INDEX_REGEX = /\[\d+\]
|
|
13
|
-
DIGIT_REGEX = /\A\d+\z
|
|
12
|
+
INDEX_REGEX = /\[\d+\]/
|
|
13
|
+
DIGIT_REGEX = /\A\d+\z/
|
|
14
14
|
BRACKETS = "[]"
|
|
15
15
|
|
|
16
16
|
# @api private
|
|
@@ -21,7 +21,7 @@ module Dry
|
|
|
21
21
|
input = result.to_h
|
|
22
22
|
|
|
23
23
|
input_paths = key_paths(input)
|
|
24
|
-
key_paths = key_map.to_dot_notation
|
|
24
|
+
key_paths = key_map.to_dot_notation.sort
|
|
25
25
|
|
|
26
26
|
input_paths.each do |path|
|
|
27
27
|
error_path = validate_path(key_paths, path)
|
|
@@ -40,20 +40,31 @@ module Dry
|
|
|
40
40
|
def validate_path(key_paths, path)
|
|
41
41
|
if path[INDEX_REGEX]
|
|
42
42
|
key = path.gsub(INDEX_REGEX, BRACKETS)
|
|
43
|
-
|
|
44
|
-
if key_paths.none? { paths_match?(key, _1) }
|
|
43
|
+
if none_key_paths_match?(key_paths, key)
|
|
45
44
|
arr = path.gsub(INDEX_REGEX) { ".#{_1[1]}" }
|
|
46
45
|
arr.split(DOT).map { DIGIT_REGEX.match?(_1) ? Integer(_1, 10) : _1.to_sym }
|
|
47
46
|
end
|
|
48
|
-
elsif
|
|
47
|
+
elsif none_key_paths_match?(key_paths, path)
|
|
49
48
|
path
|
|
50
49
|
end
|
|
51
50
|
end
|
|
52
51
|
|
|
53
52
|
# @api private
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
def none_key_paths_match?(key_paths, path)
|
|
54
|
+
!any_key_paths_match?(key_paths, path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @api private
|
|
58
|
+
def any_key_paths_match?(key_paths, path)
|
|
59
|
+
find_path(key_paths, path, false) ||
|
|
60
|
+
find_path(key_paths, path + DOT, true) ||
|
|
61
|
+
find_path(key_paths, path + BRACKETS, true)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def find_path(key_paths, path, prefix_match)
|
|
66
|
+
key = key_paths.bsearch { |key_path| key_path >= path }
|
|
67
|
+
prefix_match ? key&.start_with?(path) : key == path
|
|
57
68
|
end
|
|
58
69
|
|
|
59
70
|
# @api private
|
|
@@ -19,10 +19,10 @@ module Dry
|
|
|
19
19
|
option :compiler, default: proc { Compiler.new }
|
|
20
20
|
|
|
21
21
|
# @api private
|
|
22
|
-
option :
|
|
22
|
+
option :schema_dsl, optional: true
|
|
23
23
|
|
|
24
24
|
# @api private
|
|
25
|
-
option :
|
|
25
|
+
option :trace, default: proc { Trace.new(schema_dsl&.compiler || Compiler.new) }
|
|
26
26
|
|
|
27
27
|
# @api private
|
|
28
28
|
def new(**options)
|