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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4447e05345b9fee9c6fa8014f1650d2f366428450f1a8b5de29da67794db47f
4
- data.tar.gz: 5dd78346a5a1f0e8df66f81ccb409e5870b409bb24547960954680b550186125
3
+ metadata.gz: 8aa8d227152a712ff8580d62686ad5a4b83b4439dc8d58f42651b869e203697c
4
+ data.tar.gz: e22028010d0ac11567a8a615725496f77db8a2acd107350363b20174da01a20c
5
5
  SHA512:
6
- metadata.gz: 1545a765733867780eef3bbfdcb4a5bf2fa9a4e7a5c7c47542a720e19f0b43a0ceef35e59a1c8238a4d4c2a38c7c01690d70df3677bc8a46f1e47d0d9eef3996
7
- data.tar.gz: a9c514202ba40aa68a503fa06a11f814cdaef34f196ad14664d23b13d713d364d328cfb73acbd889d55687e7e157e65b1e433df512c1ea2a18ff81bd5c7cb119
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...master](https://github.com/dry-rb/dry-schema/compare/v1.5.3...master)
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
@@ -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
data/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  [![CI Status](https://github.com/dry-rb/dry-schema/workflows/ci/badge.svg)][actions]
12
12
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/961f5c776f1d49218b2cede3745e059c)][codacy]
13
13
  [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/961f5c776f1d49218b2cede3745e059c)][codacy]
14
- [![Inline docs](http://inch-ci.org/github/dry-rb/dry-schema.svg?branch=master)][inchpages]
14
+ [![Inline docs](http://inch-ci.org/github/dry-rb/dry-schema.svg?branch=main)][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/master/CHANGELOG.md"
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
 
@@ -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?: {mininum: IDENTITY},
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(*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)
@@ -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
@@ -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
@@ -116,7 +116,7 @@ module Dry
116
116
  ->(input) { call(input) }
117
117
  end
118
118
 
119
- # Return string represntation
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Schema
5
- VERSION = "1.9.1"
5
+ VERSION = "1.10.1"
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.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-02-17 00:00:00.000000000 Z
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/master/CHANGELOG.md
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: