dry-schema 1.9.2 → 1.10.2

Sign up to get free protection for your applications and to get access to all the features.
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
- ...