dry-schema 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE +20 -0
  4. data/README.md +21 -0
  5. data/config/errors.yml +91 -0
  6. data/lib/dry-schema.rb +1 -0
  7. data/lib/dry/schema.rb +51 -0
  8. data/lib/dry/schema/compiler.rb +31 -0
  9. data/lib/dry/schema/config.rb +52 -0
  10. data/lib/dry/schema/constants.rb +13 -0
  11. data/lib/dry/schema/dsl.rb +382 -0
  12. data/lib/dry/schema/extensions.rb +3 -0
  13. data/lib/dry/schema/extensions/monads.rb +18 -0
  14. data/lib/dry/schema/json.rb +16 -0
  15. data/lib/dry/schema/key.rb +166 -0
  16. data/lib/dry/schema/key_coercer.rb +37 -0
  17. data/lib/dry/schema/key_map.rb +133 -0
  18. data/lib/dry/schema/macros.rb +6 -0
  19. data/lib/dry/schema/macros/core.rb +51 -0
  20. data/lib/dry/schema/macros/dsl.rb +74 -0
  21. data/lib/dry/schema/macros/each.rb +18 -0
  22. data/lib/dry/schema/macros/filled.rb +24 -0
  23. data/lib/dry/schema/macros/hash.rb +46 -0
  24. data/lib/dry/schema/macros/key.rb +137 -0
  25. data/lib/dry/schema/macros/maybe.rb +37 -0
  26. data/lib/dry/schema/macros/optional.rb +17 -0
  27. data/lib/dry/schema/macros/required.rb +17 -0
  28. data/lib/dry/schema/macros/value.rb +41 -0
  29. data/lib/dry/schema/message.rb +103 -0
  30. data/lib/dry/schema/message_compiler.rb +193 -0
  31. data/lib/dry/schema/message_compiler/visitor_opts.rb +30 -0
  32. data/lib/dry/schema/message_set.rb +123 -0
  33. data/lib/dry/schema/messages.rb +42 -0
  34. data/lib/dry/schema/messages/abstract.rb +143 -0
  35. data/lib/dry/schema/messages/i18n.rb +60 -0
  36. data/lib/dry/schema/messages/namespaced.rb +53 -0
  37. data/lib/dry/schema/messages/yaml.rb +82 -0
  38. data/lib/dry/schema/params.rb +16 -0
  39. data/lib/dry/schema/predicate.rb +80 -0
  40. data/lib/dry/schema/predicate_inferrer.rb +49 -0
  41. data/lib/dry/schema/predicate_registry.rb +38 -0
  42. data/lib/dry/schema/processor.rb +151 -0
  43. data/lib/dry/schema/result.rb +164 -0
  44. data/lib/dry/schema/rule_applier.rb +45 -0
  45. data/lib/dry/schema/trace.rb +103 -0
  46. data/lib/dry/schema/type_registry.rb +42 -0
  47. data/lib/dry/schema/types.rb +12 -0
  48. data/lib/dry/schema/value_coercer.rb +27 -0
  49. data/lib/dry/schema/version.rb +5 -0
  50. metadata +255 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b804e7069cd7d8764f82ce208e38768fa9975e6d1460e1e44917d5c0eaeee911
4
+ data.tar.gz: 6814f96c49db2634895c20faf7b331feccbdcdcbc83ff89292f655acd6c0adfc
5
+ SHA512:
6
+ metadata.gz: 80d4cc2626025ef517e48bba83026c1bba7ef5797fa25e2543589ed503ec9d7142e799ed9713d4452b7a1f1b2d2988b4c6aeb3172230ec6fd4987d58c8621775
7
+ data.tar.gz: f35d2b94570ec416b6fcb0457a4e685f37ff3a265a011a4016af611307e2d8d1b8b03dc9e4b59a63b5f2cf55aacf8ceabac0a5d689e73f14e4aaa5cef96e1fa4
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0 2019-01-30
2
+
3
+ Initial release.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Dryrb Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ [gem]: https://rubygems.org/gems/dry-schema
2
+ [travis]: https://travis-ci.org/dry-rb/dry-schema
3
+ [codeclimate]: https://codeclimate.com/github/dry-rb/dry-schema
4
+ [coveralls]: https://coveralls.io/r/dry-rb/dry-schema
5
+ [inchpages]: http://inch-ci.org/github/dry-rb/dry-schema
6
+
7
+ # dry-schema [![Join the chat at https://gitter.im/dry-rb/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dry-rb/chat)
8
+
9
+ [![Gem Version](https://badge.fury.io/rb/dry-schema.svg)][gem]
10
+ [![Build Status](https://travis-ci.org/dry-rb/dry-schema.svg?branch=master)][travis]
11
+ [![Code Climate](https://codeclimate.com/github/dry-rb/dry-schema/badges/gpa.svg)][codeclimate]
12
+ [![Test Coverage](https://codeclimate.com/github/dry-rb/dry-schema/badges/coverage.svg)][codeclimate]
13
+ [![Inline docs](http://inch-ci.org/github/dry-rb/dry-schema.svg?branch=master)][inchpages]
14
+
15
+ <!-- ## Links
16
+
17
+ * [Documentation](http://dry-rb.org/gems/dry-schema) -->
18
+
19
+ ## License
20
+
21
+ See `LICENSE` file.
data/config/errors.yml ADDED
@@ -0,0 +1,91 @@
1
+ en:
2
+ errors:
3
+ or: "or"
4
+ array?: "must be an array"
5
+
6
+ empty?: "must be empty"
7
+
8
+ excludes?: "must not include %{value}"
9
+
10
+ excluded_from?:
11
+ arg:
12
+ default: "must not be one of: %{list}"
13
+ range: "must not be one of: %{list_left} - %{list_right}"
14
+ exclusion?: "must not be one of: %{list}"
15
+
16
+ eql?: "must be equal to %{left}"
17
+
18
+ not_eql?: "must not be equal to %{left}"
19
+
20
+ filled?: "must be filled"
21
+
22
+ format?: "is in invalid format"
23
+
24
+ number?: "must be a number"
25
+
26
+ odd?: "must be odd"
27
+
28
+ even?: "must be even"
29
+
30
+ gt?: "must be greater than %{num}"
31
+
32
+ gteq?: "must be greater than or equal to %{num}"
33
+
34
+ hash?: "must be a hash"
35
+
36
+ included_in?:
37
+ arg:
38
+ default: "must be one of: %{list}"
39
+ range: "must be one of: %{list_left} - %{list_right}"
40
+ inclusion?: "must be one of: %{list}"
41
+
42
+ includes?: "must include %{value}"
43
+
44
+ bool?: "must be boolean"
45
+
46
+ true?: "must be true"
47
+
48
+ false?: "must be false"
49
+
50
+ int?: "must be an integer"
51
+
52
+ float?: "must be a float"
53
+
54
+ decimal?: "must be a decimal"
55
+
56
+ date?: "must be a date"
57
+
58
+ date_time?: "must be a date time"
59
+
60
+ time?: "must be a time"
61
+
62
+ key?: "is missing"
63
+
64
+ attr?: "is missing"
65
+
66
+ lt?: "must be less than %{num}"
67
+
68
+ lteq?: "must be less than or equal to %{num}"
69
+
70
+ max_size?: "size cannot be greater than %{num}"
71
+
72
+ min_size?: "size cannot be less than %{num}"
73
+
74
+ nil?: "cannot be defined"
75
+
76
+ str?: "must be a string"
77
+
78
+ type?: "must be %{type}"
79
+
80
+ size?:
81
+ arg:
82
+ default: "size must be %{size}"
83
+ range: "size must be within %{size_left} - %{size_right}"
84
+
85
+ value:
86
+ string:
87
+ arg:
88
+ default: "length must be %{size}"
89
+ range: "length must be within %{size_left} - %{size_right}"
90
+ not:
91
+ empty?: "cannot be empty"
data/lib/dry-schema.rb ADDED
@@ -0,0 +1 @@
1
+ require 'dry/schema'
data/lib/dry/schema.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'dry/core/extensions'
2
+
3
+ require 'dry/schema/constants'
4
+ require 'dry/schema/dsl'
5
+ require 'dry/schema/params'
6
+ require 'dry/schema/json'
7
+
8
+ module Dry
9
+ module Schema
10
+ extend Dry::Core::Extensions
11
+
12
+ # Define a schema
13
+ #
14
+ # @return [Processor]
15
+ #
16
+ # @api public
17
+ def self.define(options = EMPTY_HASH, &block)
18
+ DSL.new(options, &block).call
19
+ end
20
+
21
+ # Define a param schema
22
+ #
23
+ # @return [Params]
24
+ #
25
+ # @api public
26
+ def self.Params(**options, &block)
27
+ define(**options, processor_type: Params, &block)
28
+ end
29
+ singleton_class.send(:alias_method, :Form, :Params)
30
+
31
+ # Define a JSON schema
32
+ #
33
+ # @return [JSON]
34
+ #
35
+ # @api public
36
+ def self.JSON(**options, &block)
37
+ define(**options, processor_type: JSON, &block)
38
+ end
39
+
40
+ # Return configured paths to message files
41
+ #
42
+ # @return [Array<String>]
43
+ #
44
+ # @api public
45
+ def self.messages_paths
46
+ Messages::Abstract.config.paths
47
+ end
48
+ end
49
+ end
50
+
51
+ require 'dry/schema/extensions'
@@ -0,0 +1,31 @@
1
+ require 'dry/logic/rule_compiler'
2
+ require 'dry/schema/predicate_registry'
3
+
4
+ module Dry
5
+ module Schema
6
+ # Extended rule compiler used internally by the DSL
7
+ #
8
+ # @api private
9
+ class Compiler < Logic::RuleCompiler
10
+ # Builds a default compiler instance with custom predicate registry
11
+ #
12
+ # @return [Compiler]
13
+ #
14
+ # @api private
15
+ def self.new(predicates = PredicateRegistry.new)
16
+ super
17
+ end
18
+
19
+ # Return true if a given predicate is supported by this compiler
20
+ #
21
+ # @param [Symbol] predicate
22
+ #
23
+ # @return [Boolean]
24
+ #
25
+ # @api private
26
+ def supports?(predicate)
27
+ predicates.key?(predicate)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'delegate'
2
+ require 'dry/configurable'
3
+
4
+ require 'dry/schema/predicate_registry'
5
+
6
+ module Dry
7
+ module Schema
8
+ # Schema definition configuration class
9
+ #
10
+ # @see DSL#configure
11
+ #
12
+ # @api public
13
+ class Config < SimpleDelegator
14
+ extend Dry::Configurable
15
+
16
+ setting :predicates, Schema::PredicateRegistry.new
17
+ setting :messages, :yaml
18
+ setting :messages_file
19
+ setting :namespace
20
+ setting :rules, {}
21
+
22
+ # Build a new config object with defaults filled in
23
+ #
24
+ # @api private
25
+ def self.new
26
+ super(struct.new(*settings.map { |key| config.public_send(key) }))
27
+ end
28
+
29
+ # Build a struct with defined settings
30
+ #
31
+ # @return [Struct]
32
+ #
33
+ # @api private
34
+ def self.struct
35
+ ::Struct.new(*settings)
36
+ end
37
+
38
+ # Expose configurable object to the provided block
39
+ #
40
+ # This method is used by `DSL#configure`
41
+ #
42
+ # @return [Config]
43
+ #
44
+ # @api private
45
+ def configure(&block)
46
+ yield(__getobj__)
47
+ values.freeze
48
+ freeze
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ require 'dry/core/constants'
2
+
3
+ module Dry
4
+ module Schema
5
+ include Core::Constants
6
+
7
+ InvalidSchemaError = Class.new(StandardError)
8
+ MissingMessageError = Class.new(StandardError)
9
+
10
+ QUESTION_MARK = '?'.freeze
11
+ DOT = '.'.freeze
12
+ end
13
+ end
@@ -0,0 +1,382 @@
1
+ require 'dry/initializer'
2
+
3
+ require 'dry/schema/constants'
4
+ require 'dry/schema/config'
5
+ require 'dry/schema/compiler'
6
+ require 'dry/schema/types'
7
+ require 'dry/schema/macros'
8
+
9
+ require 'dry/schema/processor'
10
+ require 'dry/schema/key_map'
11
+ require 'dry/schema/key_coercer'
12
+ require 'dry/schema/value_coercer'
13
+ require 'dry/schema/rule_applier'
14
+
15
+ module Dry
16
+ module Schema
17
+ # The schema definition DSL class
18
+ #
19
+ # The DSL is exposed by:
20
+ # - `Schema.define`
21
+ # - `Schema.Params`
22
+ # - `Schema.JSON`
23
+ # - `Schema::Params.define` - use with sub-classes
24
+ # - `Schema::JSON.define` - use with sub-classes
25
+ #
26
+ # @example class-based definition
27
+ # class UserSchema < Dry::Schema::Params
28
+ # define do
29
+ # required(:name).filled
30
+ # required(:age).filled(:integer, gt: 18)
31
+ # end
32
+ # end
33
+ #
34
+ # user_schema = UserSchema.new
35
+ # user_schema.(name: 'Jame', age: 21)
36
+ #
37
+ # @example instance-based definition shortcut
38
+ # UserSchema = Dry::Schema.Params do
39
+ # required(:name).filled
40
+ # required(:age).filled(:integer, gt: 18)
41
+ # end
42
+ #
43
+ # UserSchema.(name: 'Jame', age: 21)
44
+ #
45
+ # @api public
46
+ class DSL
47
+ Types = Schema::Types
48
+
49
+ extend Dry::Initializer
50
+
51
+ include ::Dry::Equalizer(:options)
52
+
53
+ # @!attribute [r] compiler
54
+ # @return [Compiler] The rule compiler object
55
+ option :compiler, default: -> { Compiler.new }
56
+
57
+ # @!attribute [r] processor_type
58
+ # @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
59
+ option :processor_type, default: -> { Processor }
60
+
61
+ # @!attribute [r] macros
62
+ # @return [Array] An array with macros defined within the DSL
63
+ option :macros, default: -> { EMPTY_ARRAY.dup }
64
+
65
+ # @!attribute [r] types
66
+ # @return [Compiler] A key=>type map defined within the DSL
67
+ option :types, default: -> { EMPTY_HASH.dup }
68
+
69
+ # @!attribute [r] parent
70
+ # @return [DSL] An optional parent DSL object that will be used to merge keys and rules
71
+ option :parent, optional: true
72
+
73
+ # @!attribute [r] config
74
+ # @return [Config] Configuration object exposed via `#configure` method
75
+ option :config, optional: true, default: -> { Config.new }
76
+
77
+ # Build a new DSL object and evaluate provided block
78
+ #
79
+ # @param [Hash] options
80
+ # @option options [Class] :processor The processor type (`Params`, `JSON` or a custom sub-class)
81
+ # @option options [Compiler] :compiler An instance of a rule compiler (must be compatible with `Schema::Compiler`) (optional)
82
+ # @option options [DSL] :parent An instance of the parent DSL (optional)
83
+ # @option options [Config] :config A configuration object (optional)
84
+ #
85
+ # @see Schema.define
86
+ # @see Schema.Params
87
+ # @see Schema.JSON
88
+ # @see Processor.define
89
+ #
90
+ # @return [DSL]
91
+ #
92
+ # @api public
93
+ def self.new(options = EMPTY_HASH, &block)
94
+ dsl = super
95
+ dsl.instance_eval(&block) if block
96
+ dsl
97
+ end
98
+
99
+ # Provide customized configuration for your schema
100
+ #
101
+ # @example
102
+ # Dry::Schema.define do
103
+ # configure do |config|
104
+ # config.messages = :i18n
105
+ # end
106
+ # end
107
+ #
108
+ # @see Config
109
+ #
110
+ # @return [DSL]
111
+ #
112
+ # @api public
113
+ def configure(&block)
114
+ config.configure(&block)
115
+ self
116
+ end
117
+
118
+ # Define a required key
119
+ #
120
+ # @example
121
+ # required(:name).filled
122
+ #
123
+ # required(:age).value(:integer)
124
+ #
125
+ # required(:user_limit).value(:integer, gt?: 0)
126
+ #
127
+ # required(:tags).filled { array? | str? }
128
+ #
129
+ # @param [Symbol] name The key name
130
+ #
131
+ # @return [Macros::Required]
132
+ #
133
+ # @api public
134
+ def required(name, &block)
135
+ key(name, macro: Macros::Required, &block)
136
+ end
137
+
138
+ # Define an optional key
139
+ #
140
+ # This works exactly the same as `required` except that if a key is not present
141
+ # rules will not be applied
142
+ #
143
+ # @see DSL#required
144
+ #
145
+ # @param [Symbol] name The key name
146
+ #
147
+ # @return [Macros::Optional]
148
+ #
149
+ # @api public
150
+ def optional(name, &block)
151
+ key(name, macro: Macros::Optional, &block)
152
+ end
153
+
154
+ # A generic method for defining keys
155
+ #
156
+ # @param [Symbol] name The key name
157
+ # @param [Class] macro The macro sub-class (ie `Macros::Required` or any other `Macros::Key` subclass)
158
+ #
159
+ # @return [Macros::Key]
160
+ #
161
+ # @api public
162
+ def key(name, macro:, &block)
163
+ set_type(name, Types::Any)
164
+
165
+ macro = macro.new(
166
+ name: name,
167
+ compiler: compiler,
168
+ schema_dsl: self,
169
+ filter_schema: filter_schema
170
+ )
171
+
172
+ macro.value(&block) if block
173
+ macros << macro
174
+ macro
175
+ end
176
+
177
+ # Build a processor based on DSL's definitions
178
+ #
179
+ # @return [Processor]
180
+ #
181
+ # @api private
182
+ def call
183
+ steps = [key_coercer]
184
+ steps << filter_schema.rule_applier if filter_rules?
185
+ steps << value_coercer << rule_applier
186
+
187
+ processor_type.new { |processor| steps.each { |step| processor << step } }
188
+ end
189
+
190
+ # Cast this DSL into a rule object
191
+ #
192
+ # @return [RuleApplier]
193
+ def to_rule
194
+ call.to_rule
195
+ end
196
+
197
+ # A shortcut for defining an array type with a member
198
+ #
199
+ # @example
200
+ # required(:tags).filled(array[:string])
201
+ #
202
+ # @return [Dry::Types::Array::Member]
203
+ #
204
+ # @api public
205
+ def array
206
+ -> member_type { type_registry["array"].of(resolve_type(member_type)) }
207
+ end
208
+
209
+ # Return type schema used by the value coercer
210
+ #
211
+ # @return [Dry::Types::Safe]
212
+ #
213
+ # @api private
214
+ def type_schema
215
+ type_registry["hash"].schema(types.merge(parent_types)).safe
216
+ end
217
+
218
+ # Return a new DSL instance using the same processor type
219
+ #
220
+ # @return [Dry::Types::Safe]
221
+ #
222
+ # @api private
223
+ def new(&block)
224
+ self.class.new(processor_type: processor_type, &block)
225
+ end
226
+
227
+ # Set a type for the given key name
228
+ #
229
+ # @param [Symbol] name The key name
230
+ # @param [Symbol, Array<Symbol>, Dry::Types::Type] spec The type spec or a type object
231
+ #
232
+ # @return [Dry::Types::Safe]
233
+ #
234
+ # @api private
235
+ def set_type(name, spec)
236
+ type = resolve_type(spec)
237
+ meta = { omittable: true, maybe: maybe?(type) }
238
+
239
+ types[name] = type.meta(meta)
240
+ end
241
+
242
+ # Check if the given type is a maybe sum
243
+ #
244
+ # @api private
245
+ def maybe?(type)
246
+ type.is_a?(Dry::Types::Sum) && type.left.primitive.equal?(NilClass)
247
+ end
248
+
249
+ protected
250
+
251
+ # Build a rule applier
252
+ #
253
+ # @return [RuleApplier]
254
+ #
255
+ # @api protected
256
+ def rule_applier
257
+ RuleApplier.new(rules, config: config)
258
+ end
259
+
260
+ # Build rules from defined macros
261
+ #
262
+ # @see #rule_applier
263
+ #
264
+ # @api protected
265
+ def rules
266
+ macros.map { |m| [m.name, m.to_rule] }.to_h.merge(parent_rules)
267
+ end
268
+
269
+ # Build a key map from defined types
270
+ #
271
+ # @api protected
272
+ def key_map(types = self.types)
273
+ keys = types.keys.each_with_object([]) { |key_name, arr|
274
+ arr << key_spec(key_name, types[key_name])
275
+ }
276
+ km = KeyMap.new(keys)
277
+
278
+ if key_map_type
279
+ km.public_send(key_map_type)
280
+ else
281
+ km
282
+ end
283
+ end
284
+
285
+ private
286
+
287
+ # Check if any filter rules were defined
288
+ #
289
+ # @api private
290
+ def filter_rules?
291
+ instance_variable_defined?('@__filter_schema__') && !filter_schema.macros.empty?
292
+ end
293
+
294
+ # Build an input schema DSL used by `filter` API
295
+ #
296
+ # @see Macros::Value#filter
297
+ #
298
+ # @api private
299
+ def filter_schema
300
+ @__filter_schema__ ||= new
301
+ end
302
+
303
+ # Build a key coercer
304
+ #
305
+ # @return [KeyCoercer]
306
+ #
307
+ # @api private
308
+ def key_coercer
309
+ KeyCoercer.symbolized(key_map + parent_key_map)
310
+ end
311
+
312
+ # Build a value coercer
313
+ #
314
+ # @return [ValueCoercer]
315
+ #
316
+ # @api private
317
+ def value_coercer
318
+ ValueCoercer.new(type_schema)
319
+ end
320
+
321
+ # Return type registry configured by the processor type
322
+ #
323
+ # @api private
324
+ def type_registry
325
+ processor_type.config.type_registry
326
+ end
327
+
328
+ # Return key map type configured by the processor type
329
+ #
330
+ # @api private
331
+ def key_map_type
332
+ processor_type.config.key_map_type
333
+ end
334
+
335
+ # Build a key spec needed by the key map
336
+ #
337
+ # @api private
338
+ def key_spec(name, type)
339
+ if type.respond_to?(:member_types)
340
+ { name => key_map(type.member_types) }
341
+ elsif type.respond_to?(:member)
342
+ kv = key_spec(name, type.member)
343
+ kv.equal?(name) ? name : kv.flatten(1)
344
+ else
345
+ name
346
+ end
347
+ end
348
+
349
+ # Resolve type object from the provided spec
350
+ #
351
+ # @param [Symbol, Array<Symbol>, Dry::Types::Type]
352
+ #
353
+ # @return [Dry::Types::Type]
354
+ #
355
+ # @api private
356
+ def resolve_type(spec)
357
+ case spec
358
+ when ::Dry::Types::Type then spec
359
+ when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
360
+ else
361
+ type_registry[spec]
362
+ end
363
+ end
364
+
365
+ # @api private
366
+ def parent_rules
367
+ parent&.rules || EMPTY_HASH
368
+ end
369
+
370
+ # @api private
371
+ def parent_types
372
+ # TODO: this is awful, it'd be nice if we had `Dry::Types::Hash::Schema#merge`
373
+ parent&.type_schema&.member_types || EMPTY_HASH
374
+ end
375
+
376
+ # @api private
377
+ def parent_key_map
378
+ parent&.key_map || EMPTY_ARRAY
379
+ end
380
+ end
381
+ end
382
+ end