dry-schema 1.9.3 → 1.10.3

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: 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