dry-schema 0.1.0

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