dry-schema 1.9.3 → 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 +29 -0
- data/LICENSE +1 -1
- data/lib/dry/schema/dsl.rb +19 -1
- 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 +15 -11
- data/lib/dry/schema/message/or/single_path.rb +15 -5
- data/lib/dry/schema/message_set.rb +1 -1
- data/lib/dry/schema/processor.rb +7 -0
- 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 +3 -2
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,34 @@
|
|
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
|
+
|
3
32
|
## 1.9.3 2022-06-23
|
4
33
|
|
5
34
|
|
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
|
@@ -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
|
@@ -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)
|
@@ -34,17 +34,16 @@ module Dry
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
def self.handler(message)
|
39
|
-
handlers.find { |k,| message.is_a?(k) }&.last
|
40
|
-
end
|
37
|
+
MESSAGE_ARRAY_HANDLER = -> { MessageArray.new(_1) }
|
41
38
|
|
42
39
|
# @api private
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
40
|
+
def self.handler(message)
|
41
|
+
case message
|
42
|
+
when self
|
43
|
+
IDENTITY
|
44
|
+
when Array
|
45
|
+
MESSAGE_ARRAY_HANDLER
|
46
|
+
end
|
48
47
|
end
|
49
48
|
|
50
49
|
# @api public
|
@@ -59,7 +58,7 @@ module Dry
|
|
59
58
|
|
60
59
|
# @api private
|
61
60
|
def root
|
62
|
-
@root ||=
|
61
|
+
@root ||= _paths.reduce(:&)
|
63
62
|
end
|
64
63
|
|
65
64
|
# @api private
|
@@ -67,9 +66,14 @@ module Dry
|
|
67
66
|
root
|
68
67
|
end
|
69
68
|
|
69
|
+
# @api private
|
70
|
+
def _path
|
71
|
+
@_path ||= Path[root]
|
72
|
+
end
|
73
|
+
|
70
74
|
# @api private
|
71
75
|
def _paths
|
72
|
-
@paths ||=
|
76
|
+
@paths ||= _messages.flat_map(&:_paths)
|
73
77
|
end
|
74
78
|
|
75
79
|
# @api private
|
@@ -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
|
data/lib/dry/schema/processor.rb
CHANGED
@@ -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
|