dry-schema 1.4.1 → 1.5.2

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