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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c27dabeb33b1644739f90d7bbac22dfe85ae79d742da8376cad64bdfff46c514
4
- data.tar.gz: 5b4df5b8215933c70fd3c04fd3a8cd74c0ff7bdfe4919110f95753348ce28726
3
+ metadata.gz: 0e0f30051a5014afc7cf0dc8f66f23a20f05e83dd3b2a970491d85d84e5be988
4
+ data.tar.gz: 771b790fd369f36abf86367a95afaac387825df4b041bdc325c02f7f41cd3487
5
5
  SHA512:
6
- metadata.gz: 9111228ae652344f54044d93fd7516d67cdefa0789587b786d58291d40f9c61c009207c5b3ef68d7302e8e889a125e51fbd4bfa3a5ee6b8b2629a2e06cf7a538
7
- data.tar.gz: bc1701018a9c35fb618c92a50fb4d68f24957f3d22431e343457dba65645d6476a5c7378620fe934b8520cfd20d62a2d2de1ac5c04af1f0130dfba5de86cb2f9
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
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2021 dry-rb team
3
+ Copyright (c) 2015-2022 dry-rb team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
@@ -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::Safe]
291
+ # @return [Dry::Types::Lax]
290
292
  #
291
293
  # @api private
292
294
  def type_schema
293
- our_schema = type_registry["hash"].schema(types).lax
294
- schemas = [*parents.map(&:type_schema), our_schema]
295
- schemas.inject { |result, schema| result.schema(schema.to_a) }
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: false, maybe: type.optional?}
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(*args, set_type: false) do |*predicates, type_spec:, type_rule:|
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
- append_macro(Macros::Value) do |macro|
63
- macro.call(...)
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
- append_macro(Macros::Filled) do |macro|
81
- macro.call(...)
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(*args, nullable: false, set_type: true)
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(*args, set_type: false) do |*predicates, type_spec:, type_rule:|
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
- super(*predicates, type_spec: type_spec, type_rule: type_rule, **opts)
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(*args, nullable: true) do |*predicates, type_spec:, type_rule:|
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
- schemas = op.rules.select { |rule| rule.is_a?(Processor) }
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.type_schema)
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.type_schema
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.type_schema)
33
+ build_array_type(current_type, schema.strict_type_schema)
34
34
  else
35
- schema.type_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
- attr_reader :root
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 initialize(...)
21
- super
22
- flat_left = left.flatten
23
- flat_right = right.flatten
24
- @root = [*flat_left, *flat_right].map(&:_path).reduce(:&)
25
- @left = flat_left.map { _1.to_or(root) }
26
- @right = flat_right.map { _1.to_or(root) }
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
- [MessageSet.new(left).to_h, MessageSet.new(right).to_h]
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
- @path = left.path
27
- @_path = left._path
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 ||= "#{left.dump} #{messages[:or]} #{right.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 right.is_a?(Array)
21
- if left.is_a?(Array) && paths.uniq.size > 1
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
@@ -105,7 +105,7 @@ module Dry
105
105
 
106
106
  # @api private
107
107
  def messages_map(messages = self.messages)
108
- combine_message_hashes(messages.map(&:to_h))
108
+ combine_message_hashes(messages.map(&:to_h)).freeze
109
109
  end
110
110
 
111
111
  # @api private
@@ -138,13 +138,22 @@ module Dry
138
138
 
139
139
  # Return the type schema
140
140
  #
141
- # @return [Dry::Types::Safe]
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Schema
5
- VERSION = "1.9.2"
5
+ VERSION = "1.10.2"
6
6
  end
7
7
  end
data/lib/dry/schema.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/core/extensions"
4
+ require "dry/configurable"
5
+ require "dry/logic"
4
6
 
5
7
  require "dry/schema/config"
6
8
  require "dry/schema/constants"
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.9.2
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-05-28 00:00:00.000000000 Z
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.2.32
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
- ...