dry-schema 1.9.1 → 1.10.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 +58 -1
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/dry-schema.gemspec +1 -1
- data/lib/dry/schema/dsl.rb +19 -1
- data/lib/dry/schema/extensions/json_schema/schema_compiler.rb +14 -2
- data/lib/dry/schema/macros/array.rb +1 -1
- data/lib/dry/schema/macros/dsl.rb +11 -9
- data/lib/dry/schema/macros/each.rb +5 -3
- data/lib/dry/schema/macros/key.rb +1 -44
- data/lib/dry/schema/macros/schema.rb +18 -7
- data/lib/dry/schema/message/or/multi_path.rb +77 -13
- data/lib/dry/schema/message/or/single_path.rb +15 -5
- data/lib/dry/schema/message/or.rb +2 -2
- data/lib/dry/schema/message_set.rb +1 -1
- data/lib/dry/schema/messages/yaml.rb +2 -2
- data/lib/dry/schema/processor.rb +8 -1
- data/lib/dry/schema/types_merger.rb +125 -0
- data/lib/dry/schema/version.rb +1 -1
- data/lib/dry/schema.rb +2 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8aa8d227152a712ff8580d62686ad5a4b83b4439dc8d58f42651b869e203697c
|
|
4
|
+
data.tar.gz: e22028010d0ac11567a8a615725496f77db8a2acd107350363b20174da01a20c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad3350414c764971f4c0e69ca07044901d67488625c691f2a7396c6d17b33d31a2714e8959d5f07598c03894883b7854090c6d77b182f7569acc7a32b43be3d3
|
|
7
|
+
data.tar.gz: 93320abcdadd4f3c48666507f829d4aaba2c9fc5ae181d2cd68f44e28eb850a8ead7adec29c7be935e17fe4112df09d17ac735dac9f50c49d45e5031f15d1ce6
|
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.10.1 2022-08-22
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Changed
|
|
7
|
+
|
|
8
|
+
- Reverted zeitwerk-related changes that were included in 1.10.0 by an accident (@solnic)
|
|
9
|
+
|
|
10
|
+
[Compare v1.10.0...v1.10.1](https://github.com/dry-rb/dry-schema/compare/v1.10.0...v1.10.1)
|
|
11
|
+
|
|
12
|
+
## 1.10.0 2022-08-16
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Allow nested `filled` and `value` macro usage (via #412) (@robhanlon22)
|
|
18
|
+
- Support for more complex scenarios when composing schemas (via #420) (@robhanlon22)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Fix `or` messages for complex schemas (via #413) (@robhanlon22)
|
|
23
|
+
- Using `filled` with a constrained constructor type works as expected (via #416) (@robhanlon22)
|
|
24
|
+
- Fix types and key maps for composed schemas (via #415) (@robhanlon22)
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Freeze message hash (fixes #417 via #418) (@solnic)
|
|
29
|
+
|
|
30
|
+
[Compare v1.9.3...v1.10.0](https://github.com/dry-rb/dry-schema/compare/v1.9.3...v1.10.0)
|
|
31
|
+
|
|
32
|
+
## 1.9.3 2022-06-23
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Support `anyOf` composition in JSON schema output (@robhanlon22)
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Allow composition of multiple ors (issue #307 fixed via #409) (@robhanlon22)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
[Compare v1.9.2...v1.9.3](https://github.com/dry-rb/dry-schema/compare/v1.9.2...v1.9.3)
|
|
45
|
+
|
|
46
|
+
## 1.9.2 2022-05-28
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Fix loose JSON schemas for nested hashes (via #401) (@tomdalling)
|
|
52
|
+
- Correct spelling error 'mininum' to 'minimum' in json-schema extension (via #404) (@svenanderzen)
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- [performance] YAML message backend allocates less strings (via #399) (@casperisfine)
|
|
57
|
+
|
|
58
|
+
[Compare v1.9.1...v1.9.2](https://github.com/dry-rb/dry-schema/compare/v1.9.1...v1.9.2)
|
|
59
|
+
|
|
3
60
|
## 1.9.1 2022-02-17
|
|
4
61
|
|
|
5
62
|
|
|
@@ -146,7 +203,7 @@ This release ships with a bunch of internal refactorings that should improve per
|
|
|
146
203
|
- Key validation works correctly with a non-nested maybe hashes (issue #311 fixed via #312) (@svobom57)
|
|
147
204
|
|
|
148
205
|
|
|
149
|
-
[Compare v1.5.3...
|
|
206
|
+
[Compare v1.5.3...main](https://github.com/dry-rb/dry-schema/compare/v1.5.3...main)
|
|
150
207
|
|
|
151
208
|
## 1.5.3 2020-08-21
|
|
152
209
|
|
data/LICENSE
CHANGED
data/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[][actions]
|
|
12
12
|
[][codacy]
|
|
13
13
|
[][codacy]
|
|
14
|
-
[][inchpages]
|
|
15
15
|
|
|
16
16
|
## Links
|
|
17
17
|
|
data/dry-schema.gemspec
CHANGED
|
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.require_paths = ["lib"]
|
|
28
28
|
|
|
29
29
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
30
|
-
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-schema/blob/
|
|
30
|
+
spec.metadata["changelog_uri"] = "https://github.com/dry-rb/dry-schema/blob/main/CHANGELOG.md"
|
|
31
31
|
spec.metadata["source_code_uri"] = "https://github.com/dry-rb/dry-schema"
|
|
32
32
|
spec.metadata["bug_tracker_uri"] = "https://github.com/dry-rb/dry-schema/issues"
|
|
33
33
|
|
data/lib/dry/schema/dsl.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "dry/initializer"
|
|
4
|
+
require "dry/logic"
|
|
4
5
|
|
|
5
6
|
require "dry/schema/constants"
|
|
6
7
|
require "dry/schema/path"
|
|
@@ -16,6 +17,7 @@ require "dry/schema/key_coercer"
|
|
|
16
17
|
require "dry/schema/key_validator"
|
|
17
18
|
require "dry/schema/value_coercer"
|
|
18
19
|
require "dry/schema/rule_applier"
|
|
20
|
+
require "dry/schema/types_merger"
|
|
19
21
|
|
|
20
22
|
module Dry
|
|
21
23
|
module Schema
|
|
@@ -316,7 +318,7 @@ module Dry
|
|
|
316
318
|
type = resolve_type(spec)
|
|
317
319
|
meta = {required: false, maybe: type.optional?}
|
|
318
320
|
|
|
319
|
-
types[name] = type.meta(meta)
|
|
321
|
+
@types[name] = type.meta(meta)
|
|
320
322
|
end
|
|
321
323
|
|
|
322
324
|
# Check if a custom type was set under provided key name
|
|
@@ -369,6 +371,18 @@ module Dry
|
|
|
369
371
|
parents.any?(&:filter_rules?)
|
|
370
372
|
end
|
|
371
373
|
|
|
374
|
+
# This DSL's type map merged with any parent type maps
|
|
375
|
+
#
|
|
376
|
+
# @api private
|
|
377
|
+
def types
|
|
378
|
+
[*parents.map(&:types), @types].reduce(:merge)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# @api private
|
|
382
|
+
def merge_types(op_class, lhs, rhs)
|
|
383
|
+
types_merger.(op_class, lhs, rhs)
|
|
384
|
+
end
|
|
385
|
+
|
|
372
386
|
protected
|
|
373
387
|
|
|
374
388
|
# Build a rule applier
|
|
@@ -495,6 +509,10 @@ module Dry
|
|
|
495
509
|
|
|
496
510
|
(parent || Schema).config.dup
|
|
497
511
|
end
|
|
512
|
+
|
|
513
|
+
def types_merger
|
|
514
|
+
@types_merger ||= TypesMerger.new(type_registry)
|
|
515
|
+
end
|
|
498
516
|
end
|
|
499
517
|
end
|
|
500
518
|
end
|
|
@@ -47,7 +47,7 @@ module Dry
|
|
|
47
47
|
pattern: "^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$"
|
|
48
48
|
},
|
|
49
49
|
gt?: {exclusiveMinimum: IDENTITY},
|
|
50
|
-
gteq?: {
|
|
50
|
+
gteq?: {minimum: IDENTITY},
|
|
51
51
|
lt?: {exclusiveMaximum: IDENTITY},
|
|
52
52
|
lteq?: {maximum: IDENTITY},
|
|
53
53
|
odd?: {type: "integer", not: {multipleOf: 2}},
|
|
@@ -88,7 +88,7 @@ module Dry
|
|
|
88
88
|
|
|
89
89
|
# @api private
|
|
90
90
|
def visit_set(node, opts = EMPTY_HASH)
|
|
91
|
-
target = (key = opts[:key]) ? self.class.new : self
|
|
91
|
+
target = (key = opts[:key]) ? self.class.new(loose: loose?) : self
|
|
92
92
|
|
|
93
93
|
node.map { |child| target.visit(child, opts) }
|
|
94
94
|
|
|
@@ -114,6 +114,18 @@ module Dry
|
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
+
# @api private
|
|
118
|
+
def visit_or(node, opts = EMPTY_HASH)
|
|
119
|
+
node.each do |child|
|
|
120
|
+
c = self.class.new(loose: loose?)
|
|
121
|
+
c.keys.update(subschema: {})
|
|
122
|
+
c.visit(child, opts.merge(key: :subschema))
|
|
123
|
+
|
|
124
|
+
any_of = (keys[opts[:key]][:anyOf] ||= [])
|
|
125
|
+
any_of << c.keys[:subschema]
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
117
129
|
# @api private
|
|
118
130
|
def visit_implication(node, opts = EMPTY_HASH)
|
|
119
131
|
node.each do |el|
|
|
@@ -15,7 +15,7 @@ module Dry
|
|
|
15
15
|
def value(*args, **opts, &block)
|
|
16
16
|
type(:array)
|
|
17
17
|
|
|
18
|
-
extract_type_spec(
|
|
18
|
+
extract_type_spec(args, set_type: false) do |*predicates, type_spec:, type_rule:|
|
|
19
19
|
type(schema_dsl.array[type_spec]) if type_spec
|
|
20
20
|
|
|
21
21
|
is_hash_block = type_spec.equal?(:hash)
|
|
@@ -58,12 +58,13 @@ module Dry
|
|
|
58
58
|
# @return [Macros::Core]
|
|
59
59
|
#
|
|
60
60
|
# @api public
|
|
61
|
-
def value(
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
def value(*args, **opts, &block)
|
|
62
|
+
extract_type_spec(args) do |*predicates, type_spec:, type_rule:|
|
|
63
|
+
append_macro(Macros::Value) do |macro|
|
|
64
|
+
macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
65
|
+
end
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
|
-
ruby2_keywords :value if respond_to?(:ruby2_keywords, true)
|
|
67
68
|
|
|
68
69
|
# Prepends `:filled?` predicate
|
|
69
70
|
#
|
|
@@ -76,12 +77,13 @@ module Dry
|
|
|
76
77
|
# @return [Macros::Core]
|
|
77
78
|
#
|
|
78
79
|
# @api public
|
|
79
|
-
def filled(
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
def filled(*args, **opts, &block)
|
|
81
|
+
extract_type_spec(args) do |*predicates, type_spec:, type_rule:|
|
|
82
|
+
append_macro(Macros::Filled) do |macro|
|
|
83
|
+
macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
84
|
+
end
|
|
82
85
|
end
|
|
83
86
|
end
|
|
84
|
-
ruby2_keywords :filled if respond_to?(:ruby2_keywords, true)
|
|
85
87
|
|
|
86
88
|
# Specify a nested hash without enforced `hash?` type-check
|
|
87
89
|
#
|
|
@@ -201,7 +203,7 @@ module Dry
|
|
|
201
203
|
|
|
202
204
|
# @api private
|
|
203
205
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
204
|
-
def extract_type_spec(
|
|
206
|
+
def extract_type_spec(args, nullable: false, set_type: true)
|
|
205
207
|
type_spec = args[0] unless schema_or_predicate?(args[0])
|
|
206
208
|
|
|
207
209
|
predicates = Array(type_spec ? args[1..] : args)
|
|
@@ -11,13 +11,15 @@ module Dry
|
|
|
11
11
|
# @api private
|
|
12
12
|
class Each < DSL
|
|
13
13
|
# @api private
|
|
14
|
-
def value(*args, **opts)
|
|
15
|
-
extract_type_spec(
|
|
14
|
+
def value(*args, **opts, &block)
|
|
15
|
+
extract_type_spec(args, set_type: false) do |*predicates, type_spec:, type_rule:|
|
|
16
16
|
if type_spec && !type_spec.is_a?(Dry::Types::Type)
|
|
17
17
|
type(schema_dsl.array[type_spec])
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
append_macro(Macros::Value) do |macro|
|
|
21
|
+
macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
22
|
+
end
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -32,49 +32,6 @@ module Dry
|
|
|
32
32
|
end
|
|
33
33
|
ruby2_keywords(:filter) if respond_to?(:ruby2_keywords, true)
|
|
34
34
|
|
|
35
|
-
# @overload value(type_spec, *predicates, **predicate_opts)
|
|
36
|
-
# Set type specification and predicates
|
|
37
|
-
#
|
|
38
|
-
# @param [Symbol,Types::Type,Array] type_spec
|
|
39
|
-
# @param [Array<Symbol>] predicates
|
|
40
|
-
# @param [Hash] predicate_opts
|
|
41
|
-
#
|
|
42
|
-
# @example with a predicate
|
|
43
|
-
# required(:name).value(:string, :filled?)
|
|
44
|
-
#
|
|
45
|
-
# @example with a predicate with arguments
|
|
46
|
-
# required(:name).value(:string, min_size?: 2)
|
|
47
|
-
#
|
|
48
|
-
# @example with a block
|
|
49
|
-
# required(:name).value(:string) { filled? & min_size?(2) }
|
|
50
|
-
#
|
|
51
|
-
# @return [Macros::Key]
|
|
52
|
-
#
|
|
53
|
-
# @see Macros::DSL#value
|
|
54
|
-
#
|
|
55
|
-
# @api public
|
|
56
|
-
def value(*args, **opts, &block)
|
|
57
|
-
extract_type_spec(*args) do |*predicates, type_spec:, type_rule:|
|
|
58
|
-
super(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Set type specification and predicates for a filled value
|
|
63
|
-
#
|
|
64
|
-
# @example
|
|
65
|
-
# required(:name).filled(:string)
|
|
66
|
-
#
|
|
67
|
-
# @see Macros::Key#value
|
|
68
|
-
#
|
|
69
|
-
# @return [Macros::Key]
|
|
70
|
-
#
|
|
71
|
-
# @api public
|
|
72
|
-
def filled(*args, **opts, &block)
|
|
73
|
-
extract_type_spec(*args) do |*predicates, type_spec:, type_rule:|
|
|
74
|
-
super(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
35
|
# Set type specification and predicates for a maybe value
|
|
79
36
|
#
|
|
80
37
|
# @example
|
|
@@ -86,7 +43,7 @@ module Dry
|
|
|
86
43
|
#
|
|
87
44
|
# @api public
|
|
88
45
|
def maybe(*args, **opts, &block)
|
|
89
|
-
extract_type_spec(
|
|
46
|
+
extract_type_spec(args, nullable: true) do |*predicates, type_spec:, type_rule:|
|
|
90
47
|
append_macro(Macros::Maybe) do |macro|
|
|
91
48
|
macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
|
|
92
49
|
end
|
|
@@ -30,13 +30,7 @@ module Dry
|
|
|
30
30
|
|
|
31
31
|
# @api private
|
|
32
32
|
def process_operation(op)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
hash_schema = hash_type.schema(
|
|
36
|
-
schemas.map(&:schema_dsl).map(&:types).reduce(:merge)
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
type(hash_schema)
|
|
33
|
+
type(hash_type.schema(merge_operation_types(op)))
|
|
40
34
|
end
|
|
41
35
|
|
|
42
36
|
# @api private
|
|
@@ -44,6 +38,23 @@ module Dry
|
|
|
44
38
|
schema_dsl.resolve_type(:hash)
|
|
45
39
|
end
|
|
46
40
|
|
|
41
|
+
# @api private
|
|
42
|
+
def merge_operation_types(op)
|
|
43
|
+
op.rules.reduce({}) do |acc, rule|
|
|
44
|
+
types =
|
|
45
|
+
case rule
|
|
46
|
+
when Dry::Logic::Operations::Abstract
|
|
47
|
+
merge_operation_types(rule)
|
|
48
|
+
when Processor
|
|
49
|
+
rule.types
|
|
50
|
+
else
|
|
51
|
+
EMPTY_HASH.dup
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
schema_dsl.merge_types(op.class, acc, types)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
47
58
|
# @api private
|
|
48
59
|
# rubocop: disable Metrics/AbcSize
|
|
49
60
|
def define(*args, &block)
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "dry/core/equalizer"
|
|
4
|
-
|
|
5
3
|
require "dry/schema/message/or/abstract"
|
|
6
4
|
require "dry/schema/path"
|
|
7
5
|
|
|
@@ -14,23 +12,89 @@ module Dry
|
|
|
14
12
|
# @api public
|
|
15
13
|
class MultiPath < Abstract
|
|
16
14
|
# @api private
|
|
17
|
-
|
|
15
|
+
class MessageArray
|
|
16
|
+
# @api private
|
|
17
|
+
def initialize(messages)
|
|
18
|
+
@messages = messages.flatten
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def _paths
|
|
23
|
+
@messages.map(&:_path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
def to_or(root)
|
|
28
|
+
self.class.new(@messages.map { _1.to_or(root) })
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @api private
|
|
32
|
+
def to_h
|
|
33
|
+
MessageSet.new(@messages).to_h
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
MESSAGE_ARRAY_HANDLER = -> { MessageArray.new(_1) }
|
|
18
38
|
|
|
19
39
|
# @api private
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
def self.handler(message)
|
|
41
|
+
case message
|
|
42
|
+
when self
|
|
43
|
+
IDENTITY
|
|
44
|
+
when Array
|
|
45
|
+
MESSAGE_ARRAY_HANDLER
|
|
46
|
+
end
|
|
27
47
|
end
|
|
28
48
|
|
|
29
49
|
# @api public
|
|
30
50
|
def to_h
|
|
31
|
-
@to_h ||= Path[[*root, :or]].to_h(
|
|
32
|
-
|
|
33
|
-
|
|
51
|
+
@to_h ||= Path[[*root, :or]].to_h(messages.map(&:to_h))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @api private
|
|
55
|
+
def messages
|
|
56
|
+
@messages ||= _messages.flat_map { _1.to_or(root) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
def root
|
|
61
|
+
@root ||= _paths.reduce(:&)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def path
|
|
66
|
+
root
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @api private
|
|
70
|
+
def _path
|
|
71
|
+
@_path ||= Path[root]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @api private
|
|
75
|
+
def _paths
|
|
76
|
+
@paths ||= _messages.flat_map(&:_paths)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @api private
|
|
80
|
+
def to_or(root)
|
|
81
|
+
self.root == root ? messages : [self]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# @api private
|
|
87
|
+
def _messages
|
|
88
|
+
@_messages ||= [left, right].map do |message|
|
|
89
|
+
handler = self.class.handler(message)
|
|
90
|
+
|
|
91
|
+
unless handler
|
|
92
|
+
raise ArgumentError,
|
|
93
|
+
"#{message.inspect} is of unknown type #{message.class.inspect}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
handler.(message)
|
|
97
|
+
end
|
|
34
98
|
end
|
|
35
99
|
end
|
|
36
100
|
end
|
|
@@ -21,10 +21,11 @@ module Dry
|
|
|
21
21
|
|
|
22
22
|
# @api private
|
|
23
23
|
def initialize(*args, messages)
|
|
24
|
-
super(*args)
|
|
24
|
+
super(*args.map { [_1].flatten })
|
|
25
25
|
@messages = messages
|
|
26
|
-
|
|
27
|
-
@
|
|
26
|
+
message = left.first
|
|
27
|
+
@path = message.path
|
|
28
|
+
@_path = message._path
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
# Dump a message into a string
|
|
@@ -38,7 +39,7 @@ module Dry
|
|
|
38
39
|
#
|
|
39
40
|
# @api public
|
|
40
41
|
def dump
|
|
41
|
-
@dump ||=
|
|
42
|
+
@dump ||= [*left, *right].map(&:dump).join(" #{messages[:or]} ")
|
|
42
43
|
end
|
|
43
44
|
alias_method :to_s, :dump
|
|
44
45
|
|
|
@@ -55,7 +56,16 @@ module Dry
|
|
|
55
56
|
|
|
56
57
|
# @api private
|
|
57
58
|
def to_a
|
|
58
|
-
@to_a ||= [left, right]
|
|
59
|
+
@to_a ||= [*left, *right]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @api private
|
|
63
|
+
def to_or(root)
|
|
64
|
+
to_ored = [left, right].map do |msgs|
|
|
65
|
+
msgs.map { _1.to_or(root) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
self.class.new(*to_ored, messages)
|
|
59
69
|
end
|
|
60
70
|
end
|
|
61
71
|
end
|
|
@@ -17,8 +17,8 @@ module Dry
|
|
|
17
17
|
|
|
18
18
|
if paths.uniq.size == 1
|
|
19
19
|
SinglePath.new(left, right, messages)
|
|
20
|
-
elsif
|
|
21
|
-
if
|
|
20
|
+
elsif MultiPath.handler(right)
|
|
21
|
+
if MultiPath.handler(left) && paths.uniq.size > 1
|
|
22
22
|
MultiPath.new(left, right)
|
|
23
23
|
else
|
|
24
24
|
right
|
|
@@ -39,10 +39,10 @@ module Dry
|
|
|
39
39
|
super do |config|
|
|
40
40
|
config.default_locale = :en unless config.default_locale
|
|
41
41
|
|
|
42
|
-
config.root = "%<locale>s.#{config.root}"
|
|
42
|
+
config.root = -"%<locale>s.#{config.root}"
|
|
43
43
|
|
|
44
44
|
config.rule_lookup_paths = config.rule_lookup_paths.map { |path|
|
|
45
|
-
"%<locale>s.#{path}"
|
|
45
|
+
-"%<locale>s.#{path}"
|
|
46
46
|
}
|
|
47
47
|
end
|
|
48
48
|
end
|
data/lib/dry/schema/processor.rb
CHANGED
|
@@ -116,7 +116,7 @@ module Dry
|
|
|
116
116
|
->(input) { call(input) }
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
# Return string
|
|
119
|
+
# Return string representation
|
|
120
120
|
#
|
|
121
121
|
# @return [String]
|
|
122
122
|
#
|
|
@@ -188,6 +188,13 @@ module Dry
|
|
|
188
188
|
rule_applier.rules
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
+
# Return the types from the schema DSL
|
|
192
|
+
#
|
|
193
|
+
# @api private
|
|
194
|
+
def types
|
|
195
|
+
schema_dsl.types
|
|
196
|
+
end
|
|
197
|
+
|
|
191
198
|
# Check if there are filter rules
|
|
192
199
|
#
|
|
193
200
|
# @api private
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
# Combines multiple logical operations into a single type, taking into
|
|
6
|
+
# account the type of logical operation (or, and, implication) and the
|
|
7
|
+
# underlying types (schemas, nominals, etc.)
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class TypesMerger
|
|
11
|
+
attr_reader :type_registry
|
|
12
|
+
|
|
13
|
+
# @api private
|
|
14
|
+
class ValueMerger
|
|
15
|
+
attr_reader :types_merger
|
|
16
|
+
attr_reader :op_class
|
|
17
|
+
attr_reader :old
|
|
18
|
+
attr_reader :new
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
def initialize(types_merger, op_class, old, new)
|
|
22
|
+
@types_merger = types_merger
|
|
23
|
+
@op_class = op_class
|
|
24
|
+
@old = old
|
|
25
|
+
@new = new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
def call
|
|
30
|
+
handlers.fetch(op_class).call
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# @api private
|
|
36
|
+
def handlers
|
|
37
|
+
@handlers ||=
|
|
38
|
+
{
|
|
39
|
+
Dry::Logic::Operations::Or => method(:handle_or),
|
|
40
|
+
Dry::Logic::Operations::And => method(:handle_and),
|
|
41
|
+
Dry::Logic::Operations::Implication => method(:handle_implication)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @api private
|
|
46
|
+
def handle_or
|
|
47
|
+
old | new
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api private
|
|
51
|
+
def handle_ordered
|
|
52
|
+
return old if old == new
|
|
53
|
+
|
|
54
|
+
unwrapped_old, old_rule = unwrap_type(old)
|
|
55
|
+
unwrapped_new, new_rule = unwrap_type(new)
|
|
56
|
+
|
|
57
|
+
type = merge_underlying_types(unwrapped_old, unwrapped_new)
|
|
58
|
+
|
|
59
|
+
rule = [old_rule, new_rule].compact.reduce { op_class.new(_1, _2) }
|
|
60
|
+
|
|
61
|
+
type = Dry::Types::Constrained.new(type, rule: rule) if rule
|
|
62
|
+
|
|
63
|
+
type
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
alias_method :handle_and, :handle_ordered
|
|
67
|
+
alias_method :handle_implication, :handle_ordered
|
|
68
|
+
|
|
69
|
+
# @api private
|
|
70
|
+
def merge_underlying_types(unwrapped_old, unwrapped_new)
|
|
71
|
+
case [unwrapped_old, unwrapped_new]
|
|
72
|
+
in Dry::Types::Schema, Dry::Types::Schema
|
|
73
|
+
types_merger.type_registry["hash"].schema(
|
|
74
|
+
types_merger.call(
|
|
75
|
+
op_class,
|
|
76
|
+
unwrapped_old.name_key_map,
|
|
77
|
+
unwrapped_new.name_key_map
|
|
78
|
+
)
|
|
79
|
+
)
|
|
80
|
+
in [Dry::Types::AnyClass, _] | [Dry::Types::Hash, Dry::Types::Schema]
|
|
81
|
+
unwrapped_new
|
|
82
|
+
in [Dry::Types::Hash, Dry::Types::Schema] | [_, Dry::Types::AnyClass]
|
|
83
|
+
unwrapped_old
|
|
84
|
+
else
|
|
85
|
+
if unwrapped_old.primitive != unwrapped_new.primitive
|
|
86
|
+
raise ArgumentError, <<~MESSAGE
|
|
87
|
+
Can't merge types, unwrapped_old=#{unwrapped_old.inspect}, unwrapped_new=#{unwrapped_new.inspect}
|
|
88
|
+
MESSAGE
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
unwrapped_old
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @api private
|
|
96
|
+
def unwrap_type(type)
|
|
97
|
+
rules = []
|
|
98
|
+
|
|
99
|
+
while type.is_a?(Dry::Types::Decorator)
|
|
100
|
+
rules << type.rule if type.is_a?(Dry::Types::Constrained)
|
|
101
|
+
|
|
102
|
+
if type.meta[:maybe] & type.respond_to?(:right)
|
|
103
|
+
type = type.right
|
|
104
|
+
else
|
|
105
|
+
type = type.type
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
[type, rules.reduce(:&)]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def initialize(type_registry = TypeRegistry.new)
|
|
114
|
+
@type_registry = type_registry
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# @api private
|
|
118
|
+
def call(op_class, lhs, rhs)
|
|
119
|
+
lhs.merge(rhs) do |_k, old, new|
|
|
120
|
+
ValueMerger.new(self, op_class, old, new).call
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/dry/schema/version.rb
CHANGED
data/lib/dry/schema.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.10.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: 2022-
|
|
11
|
+
date: 2022-08-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -230,6 +230,7 @@ files:
|
|
|
230
230
|
- lib/dry/schema/type_container.rb
|
|
231
231
|
- lib/dry/schema/type_registry.rb
|
|
232
232
|
- lib/dry/schema/types.rb
|
|
233
|
+
- lib/dry/schema/types_merger.rb
|
|
233
234
|
- lib/dry/schema/value_coercer.rb
|
|
234
235
|
- lib/dry/schema/version.rb
|
|
235
236
|
homepage: https://dry-rb.org/gems/dry-schema
|
|
@@ -237,7 +238,7 @@ licenses:
|
|
|
237
238
|
- MIT
|
|
238
239
|
metadata:
|
|
239
240
|
allowed_push_host: https://rubygems.org
|
|
240
|
-
changelog_uri: https://github.com/dry-rb/dry-schema/blob/
|
|
241
|
+
changelog_uri: https://github.com/dry-rb/dry-schema/blob/main/CHANGELOG.md
|
|
241
242
|
source_code_uri: https://github.com/dry-rb/dry-schema
|
|
242
243
|
bug_tracker_uri: https://github.com/dry-rb/dry-schema/issues
|
|
243
244
|
post_install_message:
|