dry-schema 1.4.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +210 -73
  3. data/LICENSE +1 -1
  4. data/README.md +4 -6
  5. data/config/errors.yml +4 -0
  6. data/dry-schema.gemspec +46 -0
  7. data/lib/dry-schema.rb +1 -1
  8. data/lib/dry/schema.rb +20 -7
  9. data/lib/dry/schema/compiler.rb +5 -5
  10. data/lib/dry/schema/config.rb +15 -6
  11. data/lib/dry/schema/constants.rb +16 -7
  12. data/lib/dry/schema/dsl.rb +89 -31
  13. data/lib/dry/schema/extensions.rb +10 -2
  14. data/lib/dry/schema/extensions/hints.rb +15 -8
  15. data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +2 -2
  16. data/lib/dry/schema/extensions/hints/message_set_methods.rb +0 -47
  17. data/lib/dry/schema/extensions/info.rb +27 -0
  18. data/lib/dry/schema/extensions/info/schema_compiler.rb +105 -0
  19. data/lib/dry/schema/extensions/monads.rb +1 -1
  20. data/lib/dry/schema/extensions/struct.rb +32 -0
  21. data/lib/dry/schema/json.rb +1 -1
  22. data/lib/dry/schema/key.rb +20 -5
  23. data/lib/dry/schema/key_coercer.rb +4 -4
  24. data/lib/dry/schema/key_map.rb +9 -4
  25. data/lib/dry/schema/key_validator.rb +67 -0
  26. data/lib/dry/schema/macros.rb +8 -8
  27. data/lib/dry/schema/macros/array.rb +17 -4
  28. data/lib/dry/schema/macros/core.rb +11 -6
  29. data/lib/dry/schema/macros/dsl.rb +44 -23
  30. data/lib/dry/schema/macros/each.rb +4 -4
  31. data/lib/dry/schema/macros/filled.rb +5 -5
  32. data/lib/dry/schema/macros/hash.rb +21 -3
  33. data/lib/dry/schema/macros/key.rb +10 -9
  34. data/lib/dry/schema/macros/maybe.rb +4 -5
  35. data/lib/dry/schema/macros/optional.rb +1 -1
  36. data/lib/dry/schema/macros/required.rb +1 -1
  37. data/lib/dry/schema/macros/schema.rb +23 -2
  38. data/lib/dry/schema/macros/value.rb +34 -7
  39. data/lib/dry/schema/message.rb +35 -9
  40. data/lib/dry/schema/message/or.rb +18 -39
  41. data/lib/dry/schema/message/or/abstract.rb +28 -0
  42. data/lib/dry/schema/message/or/multi_path.rb +37 -0
  43. data/lib/dry/schema/message/or/single_path.rb +64 -0
  44. data/lib/dry/schema/message_compiler.rb +58 -22
  45. data/lib/dry/schema/message_compiler/visitor_opts.rb +2 -2
  46. data/lib/dry/schema/message_set.rb +26 -37
  47. data/lib/dry/schema/messages.rb +6 -6
  48. data/lib/dry/schema/messages/abstract.rb +54 -62
  49. data/lib/dry/schema/messages/i18n.rb +36 -10
  50. data/lib/dry/schema/messages/namespaced.rb +12 -2
  51. data/lib/dry/schema/messages/template.rb +19 -44
  52. data/lib/dry/schema/messages/yaml.rb +61 -14
  53. data/lib/dry/schema/params.rb +1 -1
  54. data/lib/dry/schema/path.rb +44 -5
  55. data/lib/dry/schema/predicate.rb +4 -2
  56. data/lib/dry/schema/predicate_inferrer.rb +4 -184
  57. data/lib/dry/schema/predicate_registry.rb +2 -2
  58. data/lib/dry/schema/primitive_inferrer.rb +16 -0
  59. data/lib/dry/schema/processor.rb +50 -29
  60. data/lib/dry/schema/processor_steps.rb +50 -27
  61. data/lib/dry/schema/result.rb +53 -6
  62. data/lib/dry/schema/rule_applier.rb +7 -7
  63. data/lib/dry/schema/step.rb +79 -0
  64. data/lib/dry/schema/trace.rb +5 -4
  65. data/lib/dry/schema/type_container.rb +3 -3
  66. data/lib/dry/schema/type_registry.rb +2 -2
  67. data/lib/dry/schema/types.rb +1 -1
  68. data/lib/dry/schema/value_coercer.rb +2 -2
  69. data/lib/dry/schema/version.rb +1 -1
  70. metadata +21 -7
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/array'
4
- require 'dry/schema/macros/each'
5
- require 'dry/schema/macros/filled'
6
- require 'dry/schema/macros/schema'
7
- require 'dry/schema/macros/hash'
8
- require 'dry/schema/macros/maybe'
9
- require 'dry/schema/macros/optional'
10
- require 'dry/schema/macros/required'
3
+ require "dry/schema/macros/array"
4
+ require "dry/schema/macros/each"
5
+ require "dry/schema/macros/filled"
6
+ require "dry/schema/macros/schema"
7
+ require "dry/schema/macros/hash"
8
+ require "dry/schema/macros/maybe"
9
+ require "dry/schema/macros/optional"
10
+ require "dry/schema/macros/required"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/dsl'
3
+ require "dry/schema/macros/dsl"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -13,16 +13,29 @@ module Dry
13
13
  def value(*args, **opts, &block)
14
14
  type(:array)
15
15
 
16
- extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
16
+ extract_type_spec(*args, set_type: false) do |*predicates, type_spec:, type_rule:|
17
17
  type(schema_dsl.array[type_spec]) if type_spec
18
18
 
19
19
  is_hash_block = type_spec.equal?(:hash)
20
20
 
21
21
  if predicates.any? || opts.any? || !is_hash_block
22
- super(*predicates, type_spec: type_spec, **opts, &(is_hash_block ? nil : block))
22
+ super(
23
+ *predicates, type_spec: type_spec, type_rule: type_rule, **opts,
24
+ &(is_hash_block ? nil : block)
25
+ )
23
26
  end
24
27
 
25
- hash(&block) if is_hash_block
28
+ is_op = args.size.equal?(2) && args[1].is_a?(Logic::Operations::Abstract)
29
+
30
+ if is_hash_block && !is_op
31
+ hash(&block)
32
+ elsif is_op
33
+ hash = Value.new(schema_dsl: schema_dsl.new, name: name).hash(args[1])
34
+
35
+ trace.captures.concat(hash.trace.captures)
36
+
37
+ type(schema_dsl.types[name].of(hash.schema_dsl.types[name]))
38
+ end
26
39
  end
27
40
 
28
41
  self
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/initializer'
3
+ require "dry/initializer"
4
4
 
5
- require 'dry/schema/constants'
6
- require 'dry/schema/compiler'
7
- require 'dry/schema/trace'
5
+ require "dry/schema/constants"
6
+ require "dry/schema/compiler"
7
+ require "dry/schema/trace"
8
8
 
9
9
  module Dry
10
10
  module Schema
@@ -28,8 +28,13 @@ module Dry
28
28
  option :schema_dsl, optional: true
29
29
 
30
30
  # @api private
31
- def new(options = EMPTY_HASH)
32
- self.class.new({ name: name, compiler: compiler, schema_dsl: schema_dsl }.merge(options))
31
+ def new(**options)
32
+ self.class.new(name: name, compiler: compiler, schema_dsl: schema_dsl, **options)
33
+ end
34
+
35
+ # @api private
36
+ def path
37
+ schema_dsl.path
33
38
  end
34
39
 
35
40
  # @api private
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/logic/operators'
4
- require 'dry/types/predicate_inferrer'
5
- require 'dry/types/primitive_inferrer'
3
+ require "dry/logic/operators"
6
4
 
7
- require 'dry/schema/macros/core'
5
+ require "dry/schema/macros/core"
6
+ require "dry/schema/predicate_inferrer"
7
+ require "dry/schema/primitive_inferrer"
8
8
 
9
9
  module Dry
10
10
  module Schema
@@ -28,13 +28,13 @@ module Dry
28
28
  # PredicateInferrer is used to infer predicate type-check from a type spec
29
29
  # @return [PredicateInferrer]
30
30
  # @api private
31
- option :predicate_inferrer, default: proc { ::Dry::Types::PredicateInferrer.new(compiler.predicates) }
31
+ option :predicate_inferrer, default: proc { PredicateInferrer.new(compiler.predicates) }
32
32
 
33
33
  # @!attribute [r] primitive_inferrer
34
34
  # PrimitiveInferrer used to get a list of primitive classes from configured type
35
35
  # @return [PrimitiveInferrer]
36
36
  # @api private
37
- option :primitive_inferrer, default: proc { ::Dry::Types::PrimitiveInferrer.new }
37
+ option :primitive_inferrer, default: proc { PrimitiveInferrer.new }
38
38
 
39
39
  # @overload value(*predicates, **predicate_opts)
40
40
  # Set predicates without and with arguments
@@ -57,11 +57,12 @@ module Dry
57
57
  # @return [Macros::Core]
58
58
  #
59
59
  # @api public
60
- def value(*predicates, **opts, &block)
60
+ def value(*predicates, &block)
61
61
  append_macro(Macros::Value) do |macro|
62
- macro.call(*predicates, **opts, &block)
62
+ macro.call(*predicates, &block)
63
63
  end
64
64
  end
65
+ ruby2_keywords :value if respond_to?(:ruby2_keywords, true)
65
66
 
66
67
  # Prepends `:filled?` predicate
67
68
  #
@@ -74,11 +75,12 @@ module Dry
74
75
  # @return [Macros::Core]
75
76
  #
76
77
  # @api public
77
- def filled(*args, **opts, &block)
78
+ def filled(*args, &block)
78
79
  append_macro(Macros::Filled) do |macro|
79
- macro.call(*args, **opts, &block)
80
+ macro.call(*args, &block)
80
81
  end
81
82
  end
83
+ ruby2_keywords :filled if respond_to?(:ruby2_keywords, true)
82
84
 
83
85
  # Specify a nested hash without enforced `hash?` type-check
84
86
  #
@@ -99,6 +101,7 @@ module Dry
99
101
  macro.call(*args, &block)
100
102
  end
101
103
  end
104
+ ruby2_keywords :schema if respond_to?(:ruby2_keywords, true)
102
105
 
103
106
  # Specify a nested hash with enforced `hash?` type-check
104
107
  #
@@ -113,6 +116,7 @@ module Dry
113
116
  macro.call(*args, &block)
114
117
  end
115
118
  end
119
+ ruby2_keywords :hash if respond_to?(:ruby2_keywords, true)
116
120
 
117
121
  # Specify predicates that should be applied to each element of an array
118
122
  #
@@ -136,6 +140,7 @@ module Dry
136
140
  macro.value(*args, &block)
137
141
  end
138
142
  end
143
+ ruby2_keywords :each if respond_to?(:ruby2_keywords, true)
139
144
 
140
145
  # Like `each` but sets `array?` type-check
141
146
  #
@@ -155,6 +160,7 @@ module Dry
155
160
  macro.value(*args, &block)
156
161
  end
157
162
  end
163
+ ruby2_keywords :array if respond_to?(:ruby2_keywords, true)
158
164
 
159
165
  # Set type spec
160
166
  #
@@ -171,6 +177,11 @@ module Dry
171
177
  self
172
178
  end
173
179
 
180
+ # @api private
181
+ def custom_type?
182
+ schema_dsl.custom_type?(name)
183
+ end
184
+
174
185
  private
175
186
 
176
187
  # @api private
@@ -189,29 +200,32 @@ module Dry
189
200
 
190
201
  # @api private
191
202
  def extract_type_spec(*args, nullable: false, set_type: true)
192
- type_spec = args[0]
193
-
194
- is_type_spec = type_spec.is_a?(Dry::Schema::Processor) ||
195
- type_spec.is_a?(Symbol) &&
196
- type_spec.to_s.end_with?(QUESTION_MARK)
197
-
198
- type_spec = nil if is_type_spec
203
+ type_spec = args[0] unless schema_or_predicate?(args[0])
199
204
 
200
205
  predicates = Array(type_spec ? args[1..-1] : args)
206
+ type_rule = nil
201
207
 
202
208
  if type_spec
203
209
  resolved_type = resolve_type(type_spec, nullable)
204
210
 
205
- type(resolved_type) if set_type
206
-
207
- type_predicates = predicate_inferrer[resolved_type]
211
+ if type_spec.is_a?(::Array)
212
+ type_rule = type_spec.map { |ts| new(chain: false).value(ts) }.reduce(:|)
213
+ else
214
+ type_predicates = predicate_inferrer[resolved_type]
208
215
 
209
- predicates.replace(type_predicates + predicates) unless type_predicates.empty?
216
+ predicates.replace(type_predicates + predicates) unless type_predicates.empty?
210
217
 
211
- return self if predicates.empty?
218
+ return self if predicates.empty?
219
+ end
212
220
  end
213
221
 
214
- yield(*predicates, type_spec: type_spec)
222
+ type(resolved_type) if set_type && resolved_type
223
+
224
+ if type_rule
225
+ yield(*predicates, type_spec: nil, type_rule: type_rule)
226
+ else
227
+ yield(*predicates, type_spec: type_spec, type_rule: nil)
228
+ end
215
229
  end
216
230
 
217
231
  # @api private
@@ -224,6 +238,13 @@ module Dry
224
238
  schema_dsl.resolve_type([:nil, resolved])
225
239
  end
226
240
  end
241
+
242
+ # @api private
243
+ def schema_or_predicate?(arg)
244
+ arg.is_a?(Dry::Schema::Processor) ||
245
+ arg.is_a?(Symbol) &&
246
+ arg.to_s.end_with?(QUESTION_MARK)
247
+ end
227
248
  end
228
249
  end
229
250
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/types/type'
4
- require 'dry/schema/macros/dsl'
3
+ require "dry/types/type"
4
+ require "dry/schema/macros/dsl"
5
5
 
6
6
  module Dry
7
7
  module Schema
@@ -12,12 +12,12 @@ module Dry
12
12
  class Each < DSL
13
13
  # @api private
14
14
  def value(*args, **opts)
15
- extract_type_spec(*args, set_type: false) do |*predicates, type_spec:|
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, **opts)
20
+ super(*predicates, type_spec: type_spec, type_rule: type_rule, **opts)
21
21
  end
22
22
  end
23
23
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/value'
3
+ require "dry/schema/macros/value"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -15,23 +15,23 @@ module Dry
15
15
 
16
16
  if opts[:type_spec] && !filter_empty_string?
17
17
  value(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts, &block)
18
+ elsif opts[:type_rule]
19
+ value(:filled?).value(*predicates, **opts, &block)
18
20
  else
19
21
  value(:filled?, *predicates, **opts, &block)
20
22
  end
21
23
  end
22
24
 
23
25
  # @api private
24
- # rubocop:disable Style/GuardClause
25
26
  def ensure_valid_predicates(predicates)
26
27
  if predicates.include?(:empty?)
27
- raise ::Dry::Schema::InvalidSchemaError, 'Using filled with empty? predicate is invalid'
28
+ raise ::Dry::Schema::InvalidSchemaError, "Using filled with empty? predicate is invalid"
28
29
  end
29
30
 
30
31
  if predicates.include?(:filled?)
31
- raise ::Dry::Schema::InvalidSchemaError, 'Using filled with filled? is redundant'
32
+ raise ::Dry::Schema::InvalidSchemaError, "Using filled with filled? is redundant"
32
33
  end
33
34
  end
34
- # rubocop:enable Style/GuardClause
35
35
 
36
36
  # @api private
37
37
  def filter_empty_string?
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/schema'
3
+ require "dry/schema/macros/schema"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -11,8 +11,26 @@ module Dry
11
11
  class Hash < Schema
12
12
  # @api private
13
13
  def call(*args, &block)
14
- trace << hash?
15
- super(*args, &block)
14
+ if args.size >= 1 && args[0].respond_to?(:keys)
15
+ hash_type = args[0]
16
+ type_predicates = predicate_inferrer[hash_type]
17
+ all_predicats = type_predicates + args.drop(1)
18
+
19
+ super(*all_predicats) do
20
+ hash_type.each do |key|
21
+ if key.required?
22
+ required(key.name).value(key.type)
23
+ else
24
+ optional(key.name).value(key.type)
25
+ end
26
+ instance_exec(&block) if block
27
+ end
28
+ end
29
+ else
30
+ trace << hash?
31
+
32
+ super(*args, &block)
33
+ end
16
34
  end
17
35
  end
18
36
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/processor'
4
- require 'dry/schema/macros/dsl'
5
- require 'dry/schema/constants'
3
+ require "dry/schema/processor"
4
+ require "dry/schema/macros/dsl"
5
+ require "dry/schema/constants"
6
6
 
7
7
  module Dry
8
8
  module Schema
@@ -30,6 +30,7 @@ module Dry
30
30
  (filter_schema_dsl[name] || filter_schema_dsl.optional(name)).value(*args, &block)
31
31
  self
32
32
  end
33
+ ruby2_keywords(:filter) if respond_to?(:ruby2_keywords, true)
33
34
 
34
35
  # @overload value(type_spec, *predicates, **predicate_opts)
35
36
  # Set type specification and predicates
@@ -53,8 +54,8 @@ module Dry
53
54
  #
54
55
  # @api public
55
56
  def value(*args, **opts, &block)
56
- extract_type_spec(*args) do |*predicates, type_spec:|
57
- super(*predicates, type_spec: type_spec, **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)
58
59
  end
59
60
  end
60
61
 
@@ -69,8 +70,8 @@ module Dry
69
70
  #
70
71
  # @api public
71
72
  def filled(*args, **opts, &block)
72
- extract_type_spec(*args) do |*predicates, type_spec:|
73
- super(*predicates, type_spec: type_spec, **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)
74
75
  end
75
76
  end
76
77
 
@@ -85,9 +86,9 @@ module Dry
85
86
  #
86
87
  # @api public
87
88
  def maybe(*args, **opts, &block)
88
- extract_type_spec(*args, nullable: true) do |*predicates, type_spec:|
89
+ extract_type_spec(*args, nullable: true) do |*predicates, type_spec:, type_rule:|
89
90
  append_macro(Macros::Maybe) do |macro|
90
- macro.call(*predicates, **opts, &block)
91
+ macro.call(*predicates, type_spec: type_spec, type_rule: type_rule, **opts, &block)
91
92
  end
92
93
  end
93
94
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/dsl'
3
+ require "dry/schema/macros/dsl"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -12,11 +12,11 @@ module Dry
12
12
  # @api private
13
13
  def call(*args, **opts, &block)
14
14
  if args.include?(:empty?)
15
- raise ::Dry::Schema::InvalidSchemaError, 'Using maybe with empty? predicate is invalid'
15
+ raise ::Dry::Schema::InvalidSchemaError, "Using maybe with empty? predicate is invalid"
16
16
  end
17
17
 
18
18
  if args.include?(:nil?)
19
- raise ::Dry::Schema::InvalidSchemaError, 'Using maybe with nil? predicate is redundant'
19
+ raise ::Dry::Schema::InvalidSchemaError, "Using maybe with nil? predicate is redundant"
20
20
  end
21
21
 
22
22
  value(*args, **opts, &block)
@@ -30,8 +30,7 @@ module Dry
30
30
  [
31
31
  [:not, [:predicate, [:nil?, [[:input, Undefined]]]]],
32
32
  trace.to_rule.to_ast
33
- ]
34
- ]
33
+ ]]
35
34
  end
36
35
  end
37
36
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/key'
3
+ require "dry/schema/macros/key"
4
4
 
5
5
  module Dry
6
6
  module Schema
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema/macros/key'
3
+ require "dry/schema/macros/key"
4
4
 
5
5
  module Dry
6
6
  module Schema