dry-schema 1.3.2 → 1.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +178 -108
- data/LICENSE +1 -1
- data/README.md +2 -2
- data/lib/dry/schema.rb +1 -1
- data/lib/dry/schema/constants.rb +3 -2
- data/lib/dry/schema/dsl.rb +65 -20
- data/lib/dry/schema/extensions/hints/message_compiler_methods.rb +1 -1
- data/lib/dry/schema/key.rb +4 -4
- data/lib/dry/schema/macros/core.rb +2 -2
- data/lib/dry/schema/macros/dsl.rb +31 -8
- data/lib/dry/schema/macros/filled.rb +0 -7
- data/lib/dry/schema/macros/key.rb +2 -2
- data/lib/dry/schema/macros/schema.rb +2 -7
- data/lib/dry/schema/macros/value.rb +21 -2
- data/lib/dry/schema/message_compiler.rb +6 -5
- data/lib/dry/schema/messages/abstract.rb +43 -19
- data/lib/dry/schema/messages/i18n.rb +34 -1
- data/lib/dry/schema/messages/namespaced.rb +6 -1
- data/lib/dry/schema/messages/template.rb +1 -1
- data/lib/dry/schema/messages/yaml.rb +12 -0
- data/lib/dry/schema/namespaced_rule.rb +2 -1
- data/lib/dry/schema/predicate_registry.rb +2 -23
- data/lib/dry/schema/processor.rb +10 -27
- data/lib/dry/schema/processor_steps.rb +116 -0
- data/lib/dry/schema/result.rb +1 -1
- data/lib/dry/schema/type_registry.rb +2 -2
- data/lib/dry/schema/version.rb +1 -1
- metadata +5 -5
- data/lib/dry/schema/primitive_inferrer.rb +0 -98
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-schema
|
2
|
-
[
|
2
|
+
[actions]: https://github.com/dry-rb/dry-schema/actions
|
3
3
|
[codeclimate]: https://codeclimate.com/github/dry-rb/dry-schema
|
4
4
|
[chat]: https://dry-rb.zulipchat.com
|
5
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-schema
|
@@ -7,7 +7,7 @@
|
|
7
7
|
# dry-schema [][chat]
|
8
8
|
|
9
9
|
[][gem]
|
10
|
-
[][actions]
|
11
11
|
[][codeclimate]
|
12
12
|
[][codeclimate]
|
13
13
|
[][inchpages]
|
data/lib/dry/schema.rb
CHANGED
data/lib/dry/schema/constants.rb
CHANGED
@@ -26,10 +26,11 @@ module Dry
|
|
26
26
|
# An error raised when a localized message cannot be found
|
27
27
|
MissingMessageError = Class.new(StandardError) do
|
28
28
|
# @api private
|
29
|
-
def initialize(path)
|
29
|
+
def initialize(path, paths = [])
|
30
30
|
*rest, rule = path
|
31
31
|
super(<<~STR)
|
32
|
-
Message template for #{rule.inspect} under #{rest.join(DOT).inspect} was not found
|
32
|
+
Message template for #{rule.inspect} under #{rest.join(DOT).inspect} was not found. Searched in:
|
33
|
+
#{paths.map { |string| "\"#{string}\"" }.join("\n")}
|
33
34
|
STR
|
34
35
|
end
|
35
36
|
end
|
data/lib/dry/schema/dsl.rb
CHANGED
@@ -9,6 +9,7 @@ require 'dry/schema/types'
|
|
9
9
|
require 'dry/schema/macros'
|
10
10
|
|
11
11
|
require 'dry/schema/processor'
|
12
|
+
require 'dry/schema/processor_steps'
|
12
13
|
require 'dry/schema/key_map'
|
13
14
|
require 'dry/schema/key_coercer'
|
14
15
|
require 'dry/schema/value_coercer'
|
@@ -50,8 +51,6 @@ module Dry
|
|
50
51
|
|
51
52
|
extend Dry::Initializer
|
52
53
|
|
53
|
-
include ::Dry::Equalizer(:options)
|
54
|
-
|
55
54
|
# @return [Compiler] The rule compiler object
|
56
55
|
option :compiler, default: -> { Compiler.new }
|
57
56
|
|
@@ -64,18 +63,21 @@ module Dry
|
|
64
63
|
# @return [Compiler] A key=>type map defined within the DSL
|
65
64
|
option :types, default: -> { EMPTY_HASH.dup }
|
66
65
|
|
67
|
-
# @return [
|
68
|
-
option :parent,
|
66
|
+
# @return [Array] Optional parent DSL objects, that will be used to merge keys and rules
|
67
|
+
option :parent, Types::Coercible::Array, default: -> { EMPTY_ARRAY.dup }, as: :parents
|
69
68
|
|
70
69
|
# @return [Config] Configuration object exposed via `#configure` method
|
71
70
|
option :config, optional: true, default: proc { parent ? parent.config.dup : Config.new }
|
72
71
|
|
72
|
+
# @return [ProcessorSteps] Steps for the processor
|
73
|
+
option :steps, default: proc { parent ? parent.steps.dup : ProcessorSteps.new }
|
74
|
+
|
73
75
|
# Build a new DSL object and evaluate provided block
|
74
76
|
#
|
75
77
|
# @param [Hash] options
|
76
78
|
# @option options [Class] :processor The processor type (`Params`, `JSON` or a custom sub-class)
|
77
79
|
# @option options [Compiler] :compiler An instance of a rule compiler (must be compatible with `Schema::Compiler`) (optional)
|
78
|
-
# @option options [DSL] :parent
|
80
|
+
# @option options [Array[DSL]] :parent One or more instances of the parent DSL (optional)
|
79
81
|
# @option options [Config] :config A configuration object (optional)
|
80
82
|
#
|
81
83
|
# @see Schema.define
|
@@ -189,9 +191,10 @@ module Dry
|
|
189
191
|
#
|
190
192
|
# @api private
|
191
193
|
def call
|
192
|
-
steps =
|
193
|
-
steps
|
194
|
-
steps
|
194
|
+
steps[:key_coercer] = key_coercer
|
195
|
+
steps[:value_coercer] = value_coercer
|
196
|
+
steps[:rule_applier] = rule_applier
|
197
|
+
steps[:filter_schema] = filter_schema.rule_applier if filter_rules?
|
195
198
|
|
196
199
|
processor_type.new(schema_dsl: self, steps: steps)
|
197
200
|
end
|
@@ -215,14 +218,54 @@ module Dry
|
|
215
218
|
-> member_type { type_registry['array'].of(resolve_type(member_type)) }
|
216
219
|
end
|
217
220
|
|
221
|
+
# Method allows steps injection to the processor
|
222
|
+
#
|
223
|
+
# @example
|
224
|
+
# before(:rule_applier) do |input|
|
225
|
+
# input.compact
|
226
|
+
# end
|
227
|
+
#
|
228
|
+
# @return [DSL]
|
229
|
+
#
|
230
|
+
# @api public
|
231
|
+
def before(key, &block)
|
232
|
+
steps.before(key, &block)
|
233
|
+
self
|
234
|
+
end
|
235
|
+
|
236
|
+
# Method allows steps injection to the processor
|
237
|
+
#
|
238
|
+
# @example
|
239
|
+
# after(:rule_applier) do |input|
|
240
|
+
# input.compact
|
241
|
+
# end
|
242
|
+
#
|
243
|
+
# @return [DSL]
|
244
|
+
#
|
245
|
+
# @api public
|
246
|
+
def after(key, &block)
|
247
|
+
steps.after(key, &block)
|
248
|
+
self
|
249
|
+
end
|
250
|
+
|
251
|
+
# The parent (last from parents) which is used for copying non mergeable configuration
|
252
|
+
#
|
253
|
+
# @return DSL
|
254
|
+
#
|
255
|
+
# @api public
|
256
|
+
def parent
|
257
|
+
@parent ||= parents.last
|
258
|
+
end
|
259
|
+
|
218
260
|
# Return type schema used by the value coercer
|
219
261
|
#
|
220
262
|
# @return [Dry::Types::Safe]
|
221
263
|
#
|
222
264
|
# @api private
|
223
265
|
def type_schema
|
224
|
-
|
225
|
-
|
266
|
+
our_schema = type_registry['hash'].schema(types).lax
|
267
|
+
schemas = [*parents.map(&:type_schema), our_schema]
|
268
|
+
schemas.inject { |result, schema| result.schema(schema.to_a) }
|
226
269
|
end
|
227
270
|
|
228
271
|
# Return a new DSL instance using the same processor type
|
@@ -230,8 +273,8 @@ module Dry
|
|
230
273
|
# @return [Dry::Types::Safe]
|
231
274
|
#
|
232
275
|
# @api private
|
233
|
-
def new(options
|
234
|
-
self.class.new(options
|
276
|
+
def new(**options, &block)
|
277
|
+
self.class.new(**options, processor_type: processor_type, config: config, &block)
|
235
278
|
end
|
236
279
|
|
237
280
|
# Set a type for the given key name
|
@@ -276,14 +319,18 @@ module Dry
|
|
276
319
|
#
|
277
320
|
# @api private
|
278
321
|
def filter_schema_dsl
|
279
|
-
@filter_schema_dsl ||= new(parent:
|
322
|
+
@filter_schema_dsl ||= new(parent: parent_filter_schemas)
|
280
323
|
end
|
281
324
|
|
282
325
|
# Check if any filter rules were defined
|
283
326
|
#
|
284
327
|
# @api private
|
285
328
|
def filter_rules?
|
286
|
-
|
329
|
+
if instance_variable_defined?('@filter_schema_dsl') && !filter_schema_dsl.macros.empty?
|
330
|
+
return true
|
331
|
+
end
|
332
|
+
|
333
|
+
parents.any?(&:filter_rules?)
|
287
334
|
end
|
288
335
|
|
289
336
|
protected
|
@@ -323,10 +370,8 @@ module Dry
|
|
323
370
|
private
|
324
371
|
|
325
372
|
# @api private
|
326
|
-
def
|
327
|
-
|
328
|
-
|
329
|
-
parent.filter_schema if parent.filter_rules?
|
373
|
+
def parent_filter_schemas
|
374
|
+
parents.select(&:filter_rules?).map(&:filter_schema)
|
330
375
|
end
|
331
376
|
|
332
377
|
# Build a key coercer
|
@@ -380,12 +425,12 @@ module Dry
|
|
380
425
|
|
381
426
|
# @api private
|
382
427
|
def parent_rules
|
383
|
-
parent
|
428
|
+
parents.reduce({}) { |rules, parent| rules.merge(parent.rules) }
|
384
429
|
end
|
385
430
|
|
386
431
|
# @api private
|
387
432
|
def parent_key_map
|
388
|
-
parent
|
433
|
+
parents.reduce([]) { |key_map, parent| parent.key_map + key_map }
|
389
434
|
end
|
390
435
|
end
|
391
436
|
end
|
data/lib/dry/schema/key.rb
CHANGED
@@ -27,8 +27,8 @@ module Dry
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# @api private
|
30
|
-
def self.new(*args)
|
31
|
-
fetch_or_store(
|
30
|
+
def self.new(*args, **kwargs)
|
31
|
+
fetch_or_store(args, kwargs) { super }
|
32
32
|
end
|
33
33
|
|
34
34
|
# @api private
|
@@ -65,8 +65,8 @@ module Dry
|
|
65
65
|
end
|
66
66
|
|
67
67
|
# @api private
|
68
|
-
def new(new_opts
|
69
|
-
self.class.new(id,
|
68
|
+
def new(**new_opts)
|
69
|
+
self.class.new(id, name: name, coercer: coercer, **new_opts)
|
70
70
|
end
|
71
71
|
|
72
72
|
# @api private
|
@@ -28,8 +28,8 @@ module Dry
|
|
28
28
|
option :schema_dsl, optional: true
|
29
29
|
|
30
30
|
# @api private
|
31
|
-
def new(options
|
32
|
-
self.class.new(
|
31
|
+
def new(**options)
|
32
|
+
self.class.new(name: name, compiler: compiler, schema_dsl: schema_dsl, **options)
|
33
33
|
end
|
34
34
|
|
35
35
|
# @api private
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'dry/logic/operators'
|
4
|
+
require 'dry/types/predicate_inferrer'
|
5
|
+
require 'dry/types/primitive_inferrer'
|
4
6
|
|
5
7
|
require 'dry/schema/macros/core'
|
6
8
|
|
@@ -26,7 +28,13 @@ module Dry
|
|
26
28
|
# PredicateInferrer is used to infer predicate type-check from a type spec
|
27
29
|
# @return [PredicateInferrer]
|
28
30
|
# @api private
|
29
|
-
option :predicate_inferrer, default: proc { PredicateInferrer.new(compiler.predicates) }
|
31
|
+
option :predicate_inferrer, default: proc { ::Dry::Types::PredicateInferrer.new(compiler.predicates) }
|
32
|
+
|
33
|
+
# @!attribute [r] primitive_inferrer
|
34
|
+
# PrimitiveInferrer used to get a list of primitive classes from configured type
|
35
|
+
# @return [PrimitiveInferrer]
|
36
|
+
# @api private
|
37
|
+
option :primitive_inferrer, default: proc { ::Dry::Types::PrimitiveInferrer.new }
|
30
38
|
|
31
39
|
# @overload value(*predicates, **predicate_opts)
|
32
40
|
# Set predicates without and with arguments
|
@@ -49,11 +57,12 @@ module Dry
|
|
49
57
|
# @return [Macros::Core]
|
50
58
|
#
|
51
59
|
# @api public
|
52
|
-
def value(*predicates,
|
60
|
+
def value(*predicates, &block)
|
53
61
|
append_macro(Macros::Value) do |macro|
|
54
|
-
macro.call(*predicates,
|
62
|
+
macro.call(*predicates, &block)
|
55
63
|
end
|
56
64
|
end
|
65
|
+
ruby2_keywords :value if respond_to?(:ruby2_keywords, true)
|
57
66
|
|
58
67
|
# Prepends `:filled?` predicate
|
59
68
|
#
|
@@ -66,11 +75,12 @@ module Dry
|
|
66
75
|
# @return [Macros::Core]
|
67
76
|
#
|
68
77
|
# @api public
|
69
|
-
def filled(*args,
|
78
|
+
def filled(*args, &block)
|
70
79
|
append_macro(Macros::Filled) do |macro|
|
71
|
-
macro.call(*args,
|
80
|
+
macro.call(*args, &block)
|
72
81
|
end
|
73
82
|
end
|
83
|
+
ruby2_keywords :filled if respond_to?(:ruby2_keywords, true)
|
74
84
|
|
75
85
|
# Specify a nested hash without enforced `hash?` type-check
|
76
86
|
#
|
@@ -91,6 +101,7 @@ module Dry
|
|
91
101
|
macro.call(*args, &block)
|
92
102
|
end
|
93
103
|
end
|
104
|
+
ruby2_keywords :schema if respond_to?(:ruby2_keywords, true)
|
94
105
|
|
95
106
|
# Specify a nested hash with enforced `hash?` type-check
|
96
107
|
#
|
@@ -105,6 +116,7 @@ module Dry
|
|
105
116
|
macro.call(*args, &block)
|
106
117
|
end
|
107
118
|
end
|
119
|
+
ruby2_keywords :hash if respond_to?(:ruby2_keywords, true)
|
108
120
|
|
109
121
|
# Specify predicates that should be applied to each element of an array
|
110
122
|
#
|
@@ -128,6 +140,7 @@ module Dry
|
|
128
140
|
macro.value(*args, &block)
|
129
141
|
end
|
130
142
|
end
|
143
|
+
ruby2_keywords :each if respond_to?(:ruby2_keywords, true)
|
131
144
|
|
132
145
|
# Like `each` but sets `array?` type-check
|
133
146
|
#
|
@@ -147,6 +160,7 @@ module Dry
|
|
147
160
|
macro.value(*args, &block)
|
148
161
|
end
|
149
162
|
end
|
163
|
+
ruby2_keywords :array if respond_to?(:ruby2_keywords, true)
|
150
164
|
|
151
165
|
# Set type spec
|
152
166
|
#
|
@@ -192,9 +206,7 @@ module Dry
|
|
192
206
|
predicates = Array(type_spec ? args[1..-1] : args)
|
193
207
|
|
194
208
|
if type_spec
|
195
|
-
resolved_type =
|
196
|
-
nullable && !type_spec.is_a?(::Array) ? [:nil, type_spec] : type_spec
|
197
|
-
)
|
209
|
+
resolved_type = resolve_type(type_spec, nullable)
|
198
210
|
|
199
211
|
type(resolved_type) if set_type
|
200
212
|
|
@@ -207,6 +219,17 @@ module Dry
|
|
207
219
|
|
208
220
|
yield(*predicates, type_spec: type_spec)
|
209
221
|
end
|
222
|
+
|
223
|
+
# @api private
|
224
|
+
def resolve_type(type_spec, nullable)
|
225
|
+
resolved = schema_dsl.resolve_type(type_spec)
|
226
|
+
|
227
|
+
if type_spec.is_a?(::Array) || !nullable || resolved.optional?
|
228
|
+
resolved
|
229
|
+
else
|
230
|
+
schema_dsl.resolve_type([:nil, resolved])
|
231
|
+
end
|
232
|
+
end
|
210
233
|
end
|
211
234
|
end
|
212
235
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/schema/primitive_inferrer'
|
4
3
|
require 'dry/schema/macros/value'
|
5
4
|
|
6
5
|
module Dry
|
@@ -10,12 +9,6 @@ module Dry
|
|
10
9
|
#
|
11
10
|
# @api private
|
12
11
|
class Filled < Value
|
13
|
-
# @!attribute [r] primitive_inferrer
|
14
|
-
# PrimitiveInferrer used to get a list of primitive classes from configured type
|
15
|
-
# @return [PrimitiveInferrer]
|
16
|
-
# @api private
|
17
|
-
option :primitive_inferrer, default: proc { PrimitiveInferrer.new }
|
18
|
-
|
19
12
|
# @api private
|
20
13
|
def call(*predicates, **opts, &block)
|
21
14
|
ensure_valid_predicates(predicates)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/schema/predicate_inferrer'
|
4
3
|
require 'dry/schema/processor'
|
5
4
|
require 'dry/schema/macros/dsl'
|
6
5
|
require 'dry/schema/constants'
|
@@ -31,6 +30,7 @@ module Dry
|
|
31
30
|
(filter_schema_dsl[name] || filter_schema_dsl.optional(name)).value(*args, &block)
|
32
31
|
self
|
33
32
|
end
|
33
|
+
ruby2_keywords(:filter) if respond_to?(:ruby2_keywords, true)
|
34
34
|
|
35
35
|
# @overload value(type_spec, *predicates, **predicate_opts)
|
36
36
|
# Set type specification and predicates
|
@@ -88,7 +88,7 @@ module Dry
|
|
88
88
|
def maybe(*args, **opts, &block)
|
89
89
|
extract_type_spec(*args, nullable: true) do |*predicates, type_spec:|
|
90
90
|
append_macro(Macros::Maybe) do |macro|
|
91
|
-
macro.call(*predicates, **opts, &block)
|
91
|
+
macro.call(*predicates, type_spec: type_spec, **opts, &block)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
@@ -28,8 +28,8 @@ module Dry
|
|
28
28
|
definition = schema_dsl.new(&block)
|
29
29
|
schema = definition.call
|
30
30
|
type_schema =
|
31
|
-
if
|
32
|
-
parent_type
|
31
|
+
if array_type?(parent_type)
|
32
|
+
build_array_type(parent_type, definition.type_schema)
|
33
33
|
elsif redefined_schema?(args)
|
34
34
|
parent_type.schema(definition.types)
|
35
35
|
else
|
@@ -56,11 +56,6 @@ module Dry
|
|
56
56
|
parent_type.optional?
|
57
57
|
end
|
58
58
|
|
59
|
-
# @api private
|
60
|
-
def array?
|
61
|
-
parent_type.respond_to?(:of)
|
62
|
-
end
|
63
|
-
|
64
59
|
# @api private
|
65
60
|
def schema?
|
66
61
|
parent_type.respond_to?(:schema)
|
@@ -17,8 +17,8 @@ module Dry
|
|
17
17
|
current_type = schema_dsl.types[name]
|
18
18
|
|
19
19
|
updated_type =
|
20
|
-
if
|
21
|
-
current_type
|
20
|
+
if array_type?(current_type)
|
21
|
+
build_array_type(current_type, schema.type_schema)
|
22
22
|
else
|
23
23
|
schema.type_schema
|
24
24
|
end
|
@@ -39,6 +39,25 @@ module Dry
|
|
39
39
|
self
|
40
40
|
end
|
41
41
|
|
42
|
+
# @api private
|
43
|
+
def array_type?(type)
|
44
|
+
primitive_inferrer[type].eql?([::Array])
|
45
|
+
end
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def build_array_type(array_type, member)
|
49
|
+
if array_type.respond_to?(:of)
|
50
|
+
array_type.of(member)
|
51
|
+
else
|
52
|
+
raise ArgumentError, <<~ERROR.split("\n").join(' ')
|
53
|
+
Cannot define schema for a nominal array type.
|
54
|
+
Array types must be instances of Dry::Types::Array,
|
55
|
+
usually constructed with Types::Constructor(Array) { ... } or
|
56
|
+
Dry::Types['array'].constructor { ... }
|
57
|
+
ERROR
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
42
61
|
# @api private
|
43
62
|
def respond_to_missing?(meth, include_private = false)
|
44
63
|
super || meth.to_s.end_with?(QUESTION_MARK)
|