dry-schema 1.9.3 → 1.10.3

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: c2dead9a036c0d9322c68b50938417ebe12a4b62ed2f99a8ae6d4f49f894dabd
4
- data.tar.gz: ac3b386e3266e8498aa62549cddfbaece9414281beee5710c1930b062eaacde8
3
+ metadata.gz: 52498945533408be846a7c22b278471bb1e45fec87b9b3a994eee33034009fff
4
+ data.tar.gz: 1b5f542e5c07fb57a67cd7fa2062bf17a51bcc6acf2e9ba4955468f42a9c4b08
5
5
  SHA512:
6
- metadata.gz: '0831055ac93da2898c55b6194703e87bda1c27040ac23965c5f261c2e59387888f679b9dcd967a4a0665cef220b7c97db823670067267b34b40ed09a3c315b81'
7
- data.tar.gz: d82ee10469742e83ee412ebe6c06593a86c44c1e0f01fd29067700d9a37ecc461dc3e4ad6e2a09bfe4694d1e95b6c28132810bf243d27338257f66e0c50cc98e
6
+ metadata.gz: d189171c08a31c3011ff9747890eb3286f019edd2a5ec6f8bb28c9dcfd1b623886d52cdd88715bac608c1adf58d0ffd139c68a228799d207d1353821f16f5dd9
7
+ data.tar.gz: 105ac239e814c3ce1b40641137c5b9bf01dbd3c077d3891606773e4825f92c90a37d1248f6b352f75b77bafc80b52b2a0c1d4b719ec3a4a5fde09c186f6dd4f5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  <!--- DO NOT EDIT THIS FILE - IT'S AUTOMATICALLY GENERATED VIA DEVTOOLS --->
2
2
 
3
+ ## 1.10.3 2019-10-10
4
+
5
+
6
+ ### Fixed
7
+
8
+ - Addressed regressions causing issues with handling sum types (see #419 and #423 fixed via #425) (@robhanlon22)
9
+
10
+
11
+ [Compare v1.10.2...v1.10.3](https://github.com/dry-rb/dry-schema/compare/v1.10.2...v1.10.3)
12
+
13
+ ## 1.10.2 2022-08-23
14
+
15
+
16
+ ### Fixed
17
+
18
+ - Fix value coercion for composed schemas (via #421) (@robhanlon22)
19
+
20
+
21
+ [Compare v1.10.1...v1.10.2](https://github.com/dry-rb/dry-schema/compare/v1.10.1...v1.10.2)
22
+
23
+ ## 1.10.1 2022-08-22
24
+
25
+
26
+ ### Changed
27
+
28
+ - Reverted zeitwerk-related changes that were included in 1.10.0 by an accident (@solnic)
29
+
30
+ [Compare v1.10.0...v1.10.1](https://github.com/dry-rb/dry-schema/compare/v1.10.0...v1.10.1)
31
+
32
+ ## 1.10.0 2022-08-16
33
+
34
+
35
+ ### Added
36
+
37
+ - Allow nested `filled` and `value` macro usage (via #412) (@robhanlon22)
38
+ - Support for more complex scenarios when composing schemas (via #420) (@robhanlon22)
39
+
40
+ ### Fixed
41
+
42
+ - Fix `or` messages for complex schemas (via #413) (@robhanlon22)
43
+ - Using `filled` with a constrained constructor type works as expected (via #416) (@robhanlon22)
44
+ - Fix types and key maps for composed schemas (via #415) (@robhanlon22)
45
+
46
+ ### Changed
47
+
48
+ - Freeze message hash (fixes #417 via #418) (@solnic)
49
+
50
+ [Compare v1.9.3...v1.10.0](https://github.com/dry-rb/dry-schema/compare/v1.9.3...v1.10.0)
51
+
3
52
  ## 1.9.3 2022-06-23
4
53
 
5
54
 
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/dry-schema.gemspec CHANGED
@@ -36,10 +36,10 @@ Gem::Specification.new do |spec|
36
36
  # to update dependencies edit project.yml
37
37
  spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
38
38
  spec.add_runtime_dependency "dry-configurable", "~> 0.13", ">= 0.13.0"
39
- spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
39
+ spec.add_runtime_dependency "dry-core", "~> 0.9", ">= 0.9"
40
40
  spec.add_runtime_dependency "dry-initializer", "~> 3.0"
41
- spec.add_runtime_dependency "dry-logic", "~> 1.0"
42
- spec.add_runtime_dependency "dry-types", "~> 1.5"
41
+ spec.add_runtime_dependency "dry-logic", "~> 1.3"
42
+ spec.add_runtime_dependency "dry-types", "~> 1.6"
43
43
 
44
44
  spec.add_development_dependency "bundler"
45
45
  spec.add_development_dependency "rake"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/logic/rule_compiler"
4
3
  require "dry/schema/namespaced_rule"
5
4
  require "dry/schema/predicate_registry"
6
5
 
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/configurable"
5
-
6
3
  require "dry/schema/constants"
7
4
  require "dry/schema/predicate_registry"
8
5
  require "dry/schema/type_container"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "pathname"
4
- require "dry/core/constants"
5
4
 
6
5
  module Dry
7
6
  # Common constants used across the library
@@ -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
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/cache"
4
- require "dry/core/equalizer"
5
-
6
3
  module Dry
7
4
  module Schema
8
5
  # Coerces keys in a hash using provided coercer function
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/core/cache"
5
3
  require "dry/schema/constants"
6
4
  require "dry/schema/key"
7
5
 
@@ -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)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/logic/operators"
4
-
5
3
  require "dry/schema/macros/core"
6
4
  require "dry/schema/predicate_inferrer"
7
5
  require "dry/schema/primitive_inferrer"
@@ -58,12 +56,13 @@ module Dry
58
56
  # @return [Macros::Core]
59
57
  #
60
58
  # @api public
61
- def value(...)
62
- append_macro(Macros::Value) do |macro|
63
- macro.call(...)
59
+ def value(*args, **opts, &block)
60
+ extract_type_spec(args) do |*predicates, type_spec:, type_rule:|
61
+ append_macro(Macros::Value) do |macro|
62
+ macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
63
+ end
64
64
  end
65
65
  end
66
- ruby2_keywords :value if respond_to?(:ruby2_keywords, true)
67
66
 
68
67
  # Prepends `:filled?` predicate
69
68
  #
@@ -76,12 +75,31 @@ module Dry
76
75
  # @return [Macros::Core]
77
76
  #
78
77
  # @api public
79
- def filled(...)
80
- append_macro(Macros::Filled) do |macro|
81
- macro.call(...)
78
+ def filled(*args, **opts, &block)
79
+ extract_type_spec(args) do |*predicates, type_spec:, type_rule:|
80
+ append_macro(Macros::Filled) do |macro|
81
+ macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
82
+ end
83
+ end
84
+ end
85
+
86
+ # Set type specification and predicates for a maybe value
87
+ #
88
+ # @example
89
+ # required(:name).maybe(:string)
90
+ #
91
+ # @see Macros::Key#value
92
+ #
93
+ # @return [Macros::Key]
94
+ #
95
+ # @api public
96
+ def maybe(*args, **opts, &block)
97
+ extract_type_spec(args, nullable: true) do |*predicates, type_spec:, type_rule:|
98
+ append_macro(Macros::Maybe) do |macro|
99
+ macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
100
+ end
82
101
  end
83
102
  end
84
- ruby2_keywords :filled if respond_to?(:ruby2_keywords, true)
85
103
 
86
104
  # Specify a nested hash without enforced `hash?` type-check
87
105
  #
@@ -201,7 +219,7 @@ module Dry
201
219
 
202
220
  # @api private
203
221
  # rubocop: disable Metrics/PerceivedComplexity
204
- def extract_type_spec(*args, nullable: false, set_type: true)
222
+ def extract_type_spec(args, nullable: false, set_type: true)
205
223
  type_spec = args[0] unless schema_or_predicate?(args[0])
206
224
 
207
225
  predicates = Array(type_spec ? args[1..] : args)
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/type"
4
3
  require "dry/schema/macros/dsl"
5
4
 
6
5
  module Dry
@@ -11,13 +10,15 @@ module Dry
11
10
  # @api private
12
11
  class Each < DSL
13
12
  # @api private
14
- def value(*args, **opts)
15
- extract_type_spec(*args, set_type: false) do |*predicates, type_spec:, type_rule:|
13
+ def value(*args, **opts, &block)
14
+ extract_type_spec(args, set_type: false) do |*predicates, type_spec:, type_rule:|
16
15
  if type_spec && !type_spec.is_a?(Dry::Types::Type)
17
16
  type(schema_dsl.array[type_spec])
18
17
  end
19
18
 
20
- super(*predicates, type_spec: type_spec, type_rule: type_rule, **opts)
19
+ append_macro(Macros::Value) do |macro|
20
+ macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
21
+ end
21
22
  end
22
23
  end
23
24
 
@@ -13,12 +13,15 @@ module Dry
13
13
  def call(*predicates, **opts, &block)
14
14
  ensure_valid_predicates(predicates)
15
15
 
16
- if opts[:type_spec] && !filter_empty_string?
17
- value(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts, &block)
18
- elsif opts[:type_rule]
19
- value(:filled?).value(*predicates, **opts, &block)
20
- else
21
- value(:filled?, *predicates, **opts, &block)
16
+ append_macro(Macros::Value) do |macro|
17
+ if opts[:type_spec] && !filter_empty_string?
18
+ macro.call(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts,
19
+ &block)
20
+ elsif opts[:type_rule]
21
+ macro.call(:filled?).value(*predicates, **opts, &block)
22
+ else
23
+ macro.call(:filled?, *predicates, **opts, &block)
24
+ end
22
25
  end
23
26
  end
24
27
 
@@ -32,67 +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
- # Set type specification and predicates for a maybe value
79
- #
80
- # @example
81
- # required(:name).maybe(:string)
82
- #
83
- # @see Macros::Key#value
84
- #
85
- # @return [Macros::Key]
86
- #
87
- # @api public
88
- def maybe(*args, **opts, &block)
89
- extract_type_spec(*args, nullable: true) do |*predicates, type_spec:, type_rule:|
90
- append_macro(Macros::Maybe) do |macro|
91
- macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
92
- end
93
- end
94
- end
95
-
96
35
  # Coerce macro to a rule
97
36
  #
98
37
  # @return [Dry::Logic::Rule]
@@ -19,7 +19,9 @@ module Dry
19
19
  raise ::Dry::Schema::InvalidSchemaError, "Using maybe with nil? predicate is redundant"
20
20
  end
21
21
 
22
- value(*args, **opts, &block)
22
+ append_macro(Macros::Value) do |macro|
23
+ macro.call(*args, **opts, &block)
24
+ end
23
25
 
24
26
  self
25
27
  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)
@@ -34,17 +34,16 @@ module Dry
34
34
  end
35
35
  end
36
36
 
37
- # @api private
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
- private_class_method def self.handlers
44
- @handlers ||= {
45
- self => -> { _1 },
46
- Array => -> { MessageArray.new(_1) }
47
- }.freeze
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 ||= _messages.flat_map(&:_paths).reduce(:&)
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 ||= [Path[root]]
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
- @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
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/initializer"
4
- require "dry/core/equalizer"
5
4
 
6
5
  require "dry/schema/path"
7
6
  require "dry/schema/message/or"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
-
5
3
  module Dry
6
4
  module Schema
7
5
  # A set of messages used to generate errors
@@ -105,7 +103,7 @@ module Dry
105
103
 
106
104
  # @api private
107
105
  def messages_map(messages = self.messages)
108
- combine_message_hashes(messages.map(&:to_h))
106
+ combine_message_hashes(messages.map(&:to_h)).freeze
109
107
  end
110
108
 
111
109
  # @api private
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "set"
4
4
  require "concurrent/map"
5
- require "dry/core/equalizer"
6
- require "dry/configurable"
7
5
 
8
6
  require "dry/schema/constants"
9
7
  require "dry/schema/messages/template"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/initializer"
4
- require "dry/core/equalizer"
5
4
 
6
5
  require "dry/schema/constants"
7
6
 
@@ -3,7 +3,6 @@
3
3
  require "yaml"
4
4
  require "pathname"
5
5
 
6
- require "dry/core/equalizer"
7
6
  require "dry/schema/constants"
8
7
  require "dry/schema/messages/abstract"
9
8
 
@@ -169,11 +168,9 @@ module Dry
169
168
  text = input.gsub("%", "#")
170
169
 
171
170
  # rubocop:disable Security/Eval
172
- # rubocop:disable Style/DocumentDynamicEvalDefinition
173
171
  evaluator = eval(<<~RUBY, EMPTY_CONTEXT, __FILE__, __LINE__ + 1)
174
172
  -> (#{tokens.map { |token| "#{token}:" }.join(", ")}) { "#{text}" }
175
173
  RUBY
176
- # rubocop:enable Style/DocumentDynamicEvalDefinition
177
174
  # rubocop:enable Security/Eval
178
175
 
179
176
  {
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
- require "dry/logic/operators"
5
-
6
3
  module Dry
7
4
  module Schema
8
5
  # Predicate objects used within the DSL
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/predicate_inferrer"
4
-
5
3
  module Dry
6
4
  module Schema
7
5
  # @api private
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/logic/predicates"
4
- require "dry/types/predicate_registry"
5
-
6
3
  module Dry
7
4
  module Schema
8
5
  # A registry with predicate objects from `Dry::Logic::Predicates`
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/types/primitive_inferrer"
4
-
5
3
  module Dry
6
4
  module Schema
7
5
  # @api private
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/configurable"
4
3
  require "dry/initializer"
5
- require "dry/logic/operators"
6
4
 
7
5
  require "dry/schema/type_registry"
8
6
  require "dry/schema/type_container"
@@ -138,13 +136,22 @@ module Dry
138
136
 
139
137
  # Return the type schema
140
138
  #
141
- # @return [Dry::Types::Safe]
139
+ # @return [Dry::Types::Lax]
142
140
  #
143
141
  # @api private
144
142
  def type_schema
145
143
  steps.type_schema
146
144
  end
147
145
 
146
+ # Return type schema used when composing subschemas
147
+ #
148
+ # @return [Dry::Types::Schema]
149
+ #
150
+ # @api private
151
+ def strict_type_schema
152
+ schema_dsl.strict_type_schema
153
+ end
154
+
148
155
  # Return the rule applier
149
156
  #
150
157
  # @api private
@@ -188,6 +195,13 @@ module Dry
188
195
  rule_applier.rules
189
196
  end
190
197
 
198
+ # Return the types from the schema DSL
199
+ #
200
+ # @api private
201
+ def types
202
+ schema_dsl.types
203
+ end
204
+
191
205
  # Check if there are filter rules
192
206
  #
193
207
  # @api private
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/initializer"
4
- require "dry/core/equalizer"
5
4
 
6
5
  require "dry/schema/path"
7
6
 
@@ -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
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/equalizer"
4
3
  require "dry/initializer"
5
4
 
6
5
  module Dry
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Dry
4
4
  module Schema
5
- VERSION = "1.9.3"
5
+ VERSION = "1.10.3"
6
6
  end
7
7
  end
data/lib/dry/schema.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/core/extensions"
3
+ require "dry/core"
4
+ require "dry/configurable"
5
+ require "dry/logic"
6
+ require "dry/types"
4
7
 
5
8
  require "dry/schema/config"
6
9
  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.3
4
+ version: 1.10.3
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-06-23 00:00:00.000000000 Z
11
+ date: 2022-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -50,20 +50,20 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0.5'
53
+ version: '0.9'
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: '0.5'
56
+ version: '0.9'
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '0.5'
63
+ version: '0.9'
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: '0.5'
66
+ version: '0.9'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: dry-initializer
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -84,28 +84,28 @@ dependencies:
84
84
  requirements:
85
85
  - - "~>"
86
86
  - !ruby/object:Gem::Version
87
- version: '1.0'
87
+ version: '1.3'
88
88
  type: :runtime
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  requirements:
92
92
  - - "~>"
93
93
  - !ruby/object:Gem::Version
94
- version: '1.0'
94
+ version: '1.3'
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: dry-types
97
97
  requirement: !ruby/object:Gem::Requirement
98
98
  requirements:
99
99
  - - "~>"
100
100
  - !ruby/object:Gem::Version
101
- version: '1.5'
101
+ version: '1.6'
102
102
  type: :runtime
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - "~>"
107
107
  - !ruby/object:Gem::Version
108
- version: '1.5'
108
+ version: '1.6'
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: bundler
111
111
  requirement: !ruby/object:Gem::Requirement
@@ -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