dry-schema 1.9.2 → 1.10.2
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/LICENSE +1 -1
- data/lib/dry/schema/dsl.rb +31 -6
- data/lib/dry/schema/extensions/json_schema/schema_compiler.rb +12 -0
- 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 +20 -9
- data/lib/dry/schema/macros/value.rb +2 -2
- 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/processor.rb +17 -1
- data/lib/dry/schema/types_merger.rb +139 -0
- data/lib/dry/schema/version.rb +1 -1
- data/lib/dry/schema.rb +2 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e0f30051a5014afc7cf0dc8f66f23a20f05e83dd3b2a970491d85d84e5be988
|
4
|
+
data.tar.gz: 771b790fd369f36abf86367a95afaac387825df4b041bdc325c02f7f41cd3487
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12759b86883bd94e85480124539c44a3280491935a86addacd02c799696cce5d5e1cec54238b17d89c05cbe704e2394ac4749c9349e9f8e1bbe6ed92f344af73
|
7
|
+
data.tar.gz: 57b2a77a57ef8587a3a520eeecc7cc3dd5d766a92ba5096ed2306c0e466ee0c9580e7550c3ef475e88eb61a58ce7c92de3860442e29455f5590ccfa0bd7b5eda
|
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.10.2 2022-08-23
|
4
|
+
|
5
|
+
|
6
|
+
### Fixed
|
7
|
+
|
8
|
+
- Fix value coercion for composed schemas (via #421) (@robhanlon22)
|
9
|
+
|
10
|
+
|
11
|
+
[Compare v1.10.1...v1.10.2](https://github.com/dry-rb/dry-schema/compare/v1.10.1...v1.10.2)
|
12
|
+
|
13
|
+
## 1.10.1 2022-08-22
|
14
|
+
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
- Reverted zeitwerk-related changes that were included in 1.10.0 by an accident (@solnic)
|
19
|
+
|
20
|
+
[Compare v1.10.0...v1.10.1](https://github.com/dry-rb/dry-schema/compare/v1.10.0...v1.10.1)
|
21
|
+
|
22
|
+
## 1.10.0 2022-08-16
|
23
|
+
|
24
|
+
|
25
|
+
### Added
|
26
|
+
|
27
|
+
- Allow nested `filled` and `value` macro usage (via #412) (@robhanlon22)
|
28
|
+
- Support for more complex scenarios when composing schemas (via #420) (@robhanlon22)
|
29
|
+
|
30
|
+
### Fixed
|
31
|
+
|
32
|
+
- Fix `or` messages for complex schemas (via #413) (@robhanlon22)
|
33
|
+
- Using `filled` with a constrained constructor type works as expected (via #416) (@robhanlon22)
|
34
|
+
- Fix types and key maps for composed schemas (via #415) (@robhanlon22)
|
35
|
+
|
36
|
+
### Changed
|
37
|
+
|
38
|
+
- Freeze message hash (fixes #417 via #418) (@solnic)
|
39
|
+
|
40
|
+
[Compare v1.9.3...v1.10.0](https://github.com/dry-rb/dry-schema/compare/v1.9.3...v1.10.0)
|
41
|
+
|
42
|
+
## 1.9.3 2022-06-23
|
43
|
+
|
44
|
+
|
45
|
+
### Added
|
46
|
+
|
47
|
+
- Support `anyOf` composition in JSON schema output (@robhanlon22)
|
48
|
+
|
49
|
+
### Fixed
|
50
|
+
|
51
|
+
- Allow composition of multiple ors (issue #307 fixed via #409) (@robhanlon22)
|
52
|
+
|
53
|
+
|
54
|
+
[Compare v1.9.2...v1.9.3](https://github.com/dry-rb/dry-schema/compare/v1.9.2...v1.9.3)
|
55
|
+
|
3
56
|
## 1.9.2 2022-05-28
|
4
57
|
|
5
58
|
|
data/LICENSE
CHANGED
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
|
@@ -286,13 +288,20 @@ module Dry
|
|
286
288
|
|
287
289
|
# Return type schema used by the value coercer
|
288
290
|
#
|
289
|
-
# @return [Dry::Types::
|
291
|
+
# @return [Dry::Types::Lax]
|
290
292
|
#
|
291
293
|
# @api private
|
292
294
|
def type_schema
|
293
|
-
|
294
|
-
|
295
|
-
|
295
|
+
strict_type_schema.lax
|
296
|
+
end
|
297
|
+
|
298
|
+
# Return type schema used when composing subschemas
|
299
|
+
#
|
300
|
+
# @return [Dry::Types::Schema]
|
301
|
+
#
|
302
|
+
# @api private
|
303
|
+
def strict_type_schema
|
304
|
+
type_registry["hash"].schema(types)
|
296
305
|
end
|
297
306
|
|
298
307
|
# Return a new DSL instance using the same processor type
|
@@ -314,9 +323,9 @@ module Dry
|
|
314
323
|
# @api private
|
315
324
|
def set_type(name, spec)
|
316
325
|
type = resolve_type(spec)
|
317
|
-
meta = {required:
|
326
|
+
meta = {required: true, maybe: type.optional?}
|
318
327
|
|
319
|
-
types[name] = type.meta(meta)
|
328
|
+
@types[name] = type.meta(meta)
|
320
329
|
end
|
321
330
|
|
322
331
|
# Check if a custom type was set under provided key name
|
@@ -369,6 +378,18 @@ module Dry
|
|
369
378
|
parents.any?(&:filter_rules?)
|
370
379
|
end
|
371
380
|
|
381
|
+
# This DSL's type map merged with any parent type maps
|
382
|
+
#
|
383
|
+
# @api private
|
384
|
+
def types
|
385
|
+
[*parents.map(&:types), @types].reduce(:merge)
|
386
|
+
end
|
387
|
+
|
388
|
+
# @api private
|
389
|
+
def merge_types(op_class, lhs, rhs)
|
390
|
+
types_merger.(op_class, lhs, rhs)
|
391
|
+
end
|
392
|
+
|
372
393
|
protected
|
373
394
|
|
374
395
|
# Build a rule applier
|
@@ -495,6 +516,10 @@ module Dry
|
|
495
516
|
|
496
517
|
(parent || Schema).config.dup
|
497
518
|
end
|
519
|
+
|
520
|
+
def types_merger
|
521
|
+
@types_merger ||= TypesMerger.new(type_registry)
|
522
|
+
end
|
498
523
|
end
|
499
524
|
end
|
500
525
|
end
|
@@ -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)
|
@@ -51,11 +62,11 @@ module Dry
|
|
51
62
|
schema = definition.call
|
52
63
|
type_schema =
|
53
64
|
if array_type?(parent_type)
|
54
|
-
build_array_type(parent_type, definition.
|
65
|
+
build_array_type(parent_type, definition.strict_type_schema)
|
55
66
|
elsif redefined_schema?(args)
|
56
67
|
parent_type.schema(definition.types)
|
57
68
|
else
|
58
|
-
definition.
|
69
|
+
definition.strict_type_schema
|
59
70
|
end
|
60
71
|
final_type = optional? ? type_schema.optional : type_schema
|
61
72
|
|
@@ -30,9 +30,9 @@ module Dry
|
|
30
30
|
|
31
31
|
updated_type =
|
32
32
|
if array_type?(current_type)
|
33
|
-
build_array_type(current_type, schema.
|
33
|
+
build_array_type(current_type, schema.strict_type_schema)
|
34
34
|
else
|
35
|
-
schema.
|
35
|
+
schema.strict_type_schema
|
36
36
|
end
|
37
37
|
|
38
38
|
import_steps(schema)
|
@@ -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
|
data/lib/dry/schema/processor.rb
CHANGED
@@ -138,13 +138,22 @@ module Dry
|
|
138
138
|
|
139
139
|
# Return the type schema
|
140
140
|
#
|
141
|
-
# @return [Dry::Types::
|
141
|
+
# @return [Dry::Types::Lax]
|
142
142
|
#
|
143
143
|
# @api private
|
144
144
|
def type_schema
|
145
145
|
steps.type_schema
|
146
146
|
end
|
147
147
|
|
148
|
+
# Return type schema used when composing subschemas
|
149
|
+
#
|
150
|
+
# @return [Dry::Types::Schema]
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
def strict_type_schema
|
154
|
+
schema_dsl.strict_type_schema
|
155
|
+
end
|
156
|
+
|
148
157
|
# Return the rule applier
|
149
158
|
#
|
150
159
|
# @api private
|
@@ -188,6 +197,13 @@ module Dry
|
|
188
197
|
rule_applier.rules
|
189
198
|
end
|
190
199
|
|
200
|
+
# Return the types from the schema DSL
|
201
|
+
#
|
202
|
+
# @api private
|
203
|
+
def types
|
204
|
+
schema_dsl.types
|
205
|
+
end
|
206
|
+
|
191
207
|
# Check if there are filter rules
|
192
208
|
#
|
193
209
|
# @api private
|
@@ -0,0 +1,139 @@
|
|
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
|
+
if op_class <= Dry::Logic::Operations::Or
|
31
|
+
merge_or
|
32
|
+
elsif op_class <= Dry::Logic::Operations::And
|
33
|
+
merge_and
|
34
|
+
elsif op_class <= Dry::Logic::Operations::Implication
|
35
|
+
merge_implication
|
36
|
+
else
|
37
|
+
raise ArgumentError, <<~MESSAGE
|
38
|
+
Can't merge operations, op_class=#{op_class}
|
39
|
+
MESSAGE
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# @api private
|
46
|
+
def merge_or
|
47
|
+
old | new
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api private
|
51
|
+
def merge_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_unwrapped_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 :merge_and, :merge_ordered
|
67
|
+
alias_method :merge_implication, :merge_ordered
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def merge_unwrapped_types(unwrapped_old, unwrapped_new)
|
71
|
+
case [unwrapped_old, unwrapped_new]
|
72
|
+
in Dry::Types::Schema, Dry::Types::Schema
|
73
|
+
merge_schemas(unwrapped_old, unwrapped_new)
|
74
|
+
in [Dry::Types::AnyClass, _] | [Dry::Types::Hash, Dry::Types::Schema]
|
75
|
+
unwrapped_new
|
76
|
+
in [Dry::Types::Schema, Dry::Types::Hash] | [_, Dry::Types::AnyClass]
|
77
|
+
unwrapped_old
|
78
|
+
else
|
79
|
+
merge_equivalent_types(unwrapped_old, unwrapped_new)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @api private
|
84
|
+
def merge_schemas(unwrapped_old, unwrapped_new)
|
85
|
+
types_merger.type_registry["hash"].schema(
|
86
|
+
types_merger.call(
|
87
|
+
op_class,
|
88
|
+
unwrapped_old.name_key_map,
|
89
|
+
unwrapped_new.name_key_map
|
90
|
+
)
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# @api private
|
95
|
+
def merge_equivalent_types(unwrapped_old, unwrapped_new)
|
96
|
+
if unwrapped_old.primitive <= unwrapped_new.primitive
|
97
|
+
unwrapped_new
|
98
|
+
elsif unwrapped_new.primitive <= unwrapped_old.primitive
|
99
|
+
unwrapped_old
|
100
|
+
else
|
101
|
+
raise ArgumentError, <<~MESSAGE
|
102
|
+
Can't merge types, unwrapped_old=#{unwrapped_old.inspect}, unwrapped_new=#{unwrapped_new.inspect}
|
103
|
+
MESSAGE
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# @api private
|
108
|
+
def unwrap_type(type)
|
109
|
+
rules = []
|
110
|
+
|
111
|
+
loop do
|
112
|
+
rules << type.rule if type.respond_to?(:rule)
|
113
|
+
|
114
|
+
if type.optional?
|
115
|
+
type = type.left.primitive?(nil) ? type.right : type.left
|
116
|
+
elsif type.is_a?(Dry::Types::Decorator)
|
117
|
+
type = type.type
|
118
|
+
else
|
119
|
+
break
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
[type, rules.reduce(:&)]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def initialize(type_registry = TypeRegistry.new)
|
128
|
+
@type_registry = type_registry
|
129
|
+
end
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
def call(op_class, lhs, rhs)
|
133
|
+
lhs.merge(rhs) do |_k, old, new|
|
134
|
+
ValueMerger.new(self, op_class, old, new).call
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
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.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-23 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
|
@@ -240,7 +241,7 @@ metadata:
|
|
240
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
|
-
post_install_message:
|
244
|
+
post_install_message:
|
244
245
|
rdoc_options: []
|
245
246
|
require_paths:
|
246
247
|
- lib
|
@@ -255,9 +256,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
255
256
|
- !ruby/object:Gem::Version
|
256
257
|
version: '0'
|
257
258
|
requirements: []
|
258
|
-
rubygems_version: 3.
|
259
|
-
signing_key:
|
259
|
+
rubygems_version: 3.1.6
|
260
|
+
signing_key:
|
260
261
|
specification_version: 4
|
261
262
|
summary: Coercion and validation for data structures
|
262
263
|
test_files: []
|
263
|
-
...
|