mega-sharp-sys 0.0.1
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 +7 -0
- data/dry-schema-1.16.0/CHANGELOG.md +901 -0
- data/dry-schema-1.16.0/LICENSE +20 -0
- data/dry-schema-1.16.0/README.md +17 -0
- data/dry-schema-1.16.0/config/errors.yml +127 -0
- data/dry-schema-1.16.0/dry-schema.gemspec +50 -0
- data/dry-schema-1.16.0/lib/dry/schema/compiler.rb +51 -0
- data/dry-schema-1.16.0/lib/dry/schema/config.rb +83 -0
- data/dry-schema-1.16.0/lib/dry/schema/constants.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/dsl.rb +517 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/compiler_methods.rb +19 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_compiler_methods.rb +89 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/message_set_methods.rb +44 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints/result_methods.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/hints.rb +65 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/info/schema_compiler.rb +123 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/info.rb +27 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema/schema_compiler.rb +314 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/json_schema.rb +29 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/monads.rb +32 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions/struct.rb +62 -0
- data/dry-schema-1.16.0/lib/dry/schema/extensions.rb +21 -0
- data/dry-schema-1.16.0/lib/dry/schema/json.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/key.rb +184 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_coercer.rb +41 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_map.rb +136 -0
- data/dry-schema-1.16.0/lib/dry/schema/key_validator.rb +117 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/array.rb +54 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/core.rb +55 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/dsl.rb +282 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/each.rb +31 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/filled.rb +58 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/hash.rb +36 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/key.rb +50 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/maybe.rb +38 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/optional.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/required.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/schema.rb +103 -0
- data/dry-schema-1.16.0/lib/dry/schema/macros/value.rb +125 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/abstract.rb +28 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/multi_path.rb +100 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or/single_path.rb +72 -0
- data/dry-schema-1.16.0/lib/dry/schema/message/or.rb +30 -0
- data/dry-schema-1.16.0/lib/dry/schema/message.rb +116 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_compiler/visitor_opts.rb +56 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_compiler.rb +234 -0
- data/dry-schema-1.16.0/lib/dry/schema/message_set.rb +144 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/abstract.rb +202 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/i18n.rb +139 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/namespaced.rb +93 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/template.rb +43 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages/yaml.rb +209 -0
- data/dry-schema-1.16.0/lib/dry/schema/messages.rb +32 -0
- data/dry-schema-1.16.0/lib/dry/schema/namespaced_rule.rb +38 -0
- data/dry-schema-1.16.0/lib/dry/schema/params.rb +17 -0
- data/dry-schema-1.16.0/lib/dry/schema/path.rb +107 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate.rb +102 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate_inferrer.rb +25 -0
- data/dry-schema-1.16.0/lib/dry/schema/predicate_registry.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/primitive_inferrer.rb +22 -0
- data/dry-schema-1.16.0/lib/dry/schema/processor.rb +213 -0
- data/dry-schema-1.16.0/lib/dry/schema/processor_steps.rb +141 -0
- data/dry-schema-1.16.0/lib/dry/schema/result.rb +203 -0
- data/dry-schema-1.16.0/lib/dry/schema/rule_applier.rb +47 -0
- data/dry-schema-1.16.0/lib/dry/schema/step.rb +59 -0
- data/dry-schema-1.16.0/lib/dry/schema/trace.rb +114 -0
- data/dry-schema-1.16.0/lib/dry/schema/type_container.rb +27 -0
- data/dry-schema-1.16.0/lib/dry/schema/type_registry.rb +44 -0
- data/dry-schema-1.16.0/lib/dry/schema/types.rb +14 -0
- data/dry-schema-1.16.0/lib/dry/schema/types_merger.rb +139 -0
- data/dry-schema-1.16.0/lib/dry/schema/value_coercer.rb +36 -0
- data/dry-schema-1.16.0/lib/dry/schema/version.rb +7 -0
- data/dry-schema-1.16.0/lib/dry/schema.rb +114 -0
- data/dry-schema-1.16.0/lib/dry-schema.rb +3 -0
- data/mega-sharp-sys.gemspec +11 -0
- metadata +114 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/schema/constants"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# The schema definition DSL class
|
|
9
|
+
#
|
|
10
|
+
# The DSL is exposed by:
|
|
11
|
+
# - `Schema.define`
|
|
12
|
+
# - `Schema.Params`
|
|
13
|
+
# - `Schema.JSON`
|
|
14
|
+
# - `Schema::Params.define` - use with sub-classes
|
|
15
|
+
# - `Schema::JSON.define` - use with sub-classes
|
|
16
|
+
#
|
|
17
|
+
# @example class-based definition
|
|
18
|
+
# class UserSchema < Dry::Schema::Params
|
|
19
|
+
# define do
|
|
20
|
+
# required(:name).filled
|
|
21
|
+
# required(:age).filled(:integer, gt: 18)
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# user_schema = UserSchema.new
|
|
26
|
+
# user_schema.(name: 'Jame', age: 21)
|
|
27
|
+
#
|
|
28
|
+
# @example instance-based definition shortcut
|
|
29
|
+
# UserSchema = Dry::Schema.Params do
|
|
30
|
+
# required(:name).filled
|
|
31
|
+
# required(:age).filled(:integer, gt: 18)
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# UserSchema.(name: 'Jame', age: 21)
|
|
35
|
+
#
|
|
36
|
+
# @api public
|
|
37
|
+
class DSL
|
|
38
|
+
Types = Schema::Types
|
|
39
|
+
|
|
40
|
+
extend ::Dry::Initializer
|
|
41
|
+
|
|
42
|
+
# @return [Compiler] The type of the processor (Params, JSON, or a custom sub-class)
|
|
43
|
+
option :processor_type, default: -> { Processor }
|
|
44
|
+
|
|
45
|
+
# @return [Array] An array with macros defined within the DSL
|
|
46
|
+
option :macros, default: -> { EMPTY_ARRAY.dup }
|
|
47
|
+
|
|
48
|
+
# @return [Compiler] A key=>type map defined within the DSL
|
|
49
|
+
option :types, default: -> { EMPTY_HASH.dup }
|
|
50
|
+
|
|
51
|
+
# @return [Array] Optional parent DSL objects, that will be used to merge keys and rules
|
|
52
|
+
option :parent, Types::Coercible::Array, default: -> { EMPTY_ARRAY.dup }, as: :parents
|
|
53
|
+
|
|
54
|
+
# @return [Config] Configuration object exposed via `#configure` method
|
|
55
|
+
option :config, optional: true, default: proc { default_config }
|
|
56
|
+
|
|
57
|
+
# @return [ProcessorSteps] Steps for the processor
|
|
58
|
+
option :steps, default: proc { ProcessorSteps.new }
|
|
59
|
+
|
|
60
|
+
# @return [Path, Array] Path under which the schema is defined
|
|
61
|
+
option :path, -> *args { Path[*args] if args.any? }, default: proc { EMPTY_ARRAY }
|
|
62
|
+
|
|
63
|
+
# Build a new DSL object and evaluate provided block
|
|
64
|
+
#
|
|
65
|
+
# @param [Hash] options
|
|
66
|
+
# @option options [Class] :processor The processor type
|
|
67
|
+
# (`Params`, `JSON` or a custom sub-class)
|
|
68
|
+
# @option options [Compiler] :compiler An instance of a rule compiler
|
|
69
|
+
# (must be compatible with `Schema::Compiler`) (optional)
|
|
70
|
+
# @option options [Array[DSL]] :parent One or more instances of the parent DSL (optional)
|
|
71
|
+
# @option options [Config] :config A configuration object (optional)
|
|
72
|
+
#
|
|
73
|
+
# @see Schema.define
|
|
74
|
+
# @see Schema.Params
|
|
75
|
+
# @see Schema.JSON
|
|
76
|
+
# @see Processor.define
|
|
77
|
+
#
|
|
78
|
+
# @return [DSL]
|
|
79
|
+
#
|
|
80
|
+
# @api public
|
|
81
|
+
def self.new(**options, &)
|
|
82
|
+
dsl = super
|
|
83
|
+
dsl.instance_eval(&) if block_given?
|
|
84
|
+
dsl.instance_variable_set("@compiler", options[:compiler]) if options[:compiler]
|
|
85
|
+
dsl
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Provide customized configuration for your schema
|
|
89
|
+
#
|
|
90
|
+
# @example
|
|
91
|
+
# Dry::Schema.define do
|
|
92
|
+
# configure do |config|
|
|
93
|
+
# config.messages.backend = :i18n
|
|
94
|
+
# end
|
|
95
|
+
# end
|
|
96
|
+
#
|
|
97
|
+
# @see Config
|
|
98
|
+
#
|
|
99
|
+
# @return [DSL]
|
|
100
|
+
#
|
|
101
|
+
# @api public
|
|
102
|
+
def configure(&)
|
|
103
|
+
config.configure(&)
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @api private
|
|
108
|
+
def compiler
|
|
109
|
+
@compiler ||= Compiler.new(predicates)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @api private
|
|
113
|
+
def predicates
|
|
114
|
+
@predicates ||= config.predicates
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Return a macro with the provided name
|
|
118
|
+
#
|
|
119
|
+
# @param [Symbol] name
|
|
120
|
+
#
|
|
121
|
+
# @return [Macros::Core]
|
|
122
|
+
#
|
|
123
|
+
# @api public
|
|
124
|
+
def [](name)
|
|
125
|
+
macros.detect { |macro| macro.name.equal?(name) }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Define a required key
|
|
129
|
+
#
|
|
130
|
+
# @example
|
|
131
|
+
# required(:name).filled
|
|
132
|
+
#
|
|
133
|
+
# required(:age).value(:integer)
|
|
134
|
+
#
|
|
135
|
+
# required(:user_limit).value(:integer, gt?: 0)
|
|
136
|
+
#
|
|
137
|
+
# required(:tags).filled { array? | str? }
|
|
138
|
+
#
|
|
139
|
+
# @param [Symbol] name The key name
|
|
140
|
+
#
|
|
141
|
+
# @return [Macros::Required]
|
|
142
|
+
#
|
|
143
|
+
# @api public
|
|
144
|
+
def required(name, &)
|
|
145
|
+
key(name, macro: Macros::Required, &)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Define an optional key
|
|
149
|
+
#
|
|
150
|
+
# This works exactly the same as `required` except that if a key is not present
|
|
151
|
+
# rules will not be applied
|
|
152
|
+
#
|
|
153
|
+
# @see DSL#required
|
|
154
|
+
#
|
|
155
|
+
# @param [Symbol] name The key name
|
|
156
|
+
#
|
|
157
|
+
# @return [Macros::Optional]
|
|
158
|
+
#
|
|
159
|
+
# @api public
|
|
160
|
+
def optional(name, &)
|
|
161
|
+
key(name, macro: Macros::Optional, &)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# A generic method for defining keys
|
|
165
|
+
#
|
|
166
|
+
# @param [Symbol] name The key name
|
|
167
|
+
# @param [Class] macro The macro sub-class (ie `Macros::Required` or
|
|
168
|
+
# any other `Macros::Key` subclass)
|
|
169
|
+
#
|
|
170
|
+
# @return [Macros::Key]
|
|
171
|
+
#
|
|
172
|
+
# @api public
|
|
173
|
+
def key(name, macro:, &)
|
|
174
|
+
raise ArgumentError, "Key +#{name}+ is not a symbol" unless name.is_a?(::Symbol)
|
|
175
|
+
|
|
176
|
+
set_type(name, Types::Any.meta(default: true))
|
|
177
|
+
|
|
178
|
+
macro = macro.new(
|
|
179
|
+
name: name,
|
|
180
|
+
compiler: compiler,
|
|
181
|
+
schema_dsl: self,
|
|
182
|
+
filter_schema_dsl: filter_schema_dsl
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
macro.value(&) if block_given?
|
|
186
|
+
macros << macro
|
|
187
|
+
macro
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Build a processor based on DSL's definitions
|
|
191
|
+
#
|
|
192
|
+
# @return [Processor, Params, JSON]
|
|
193
|
+
#
|
|
194
|
+
# @api private
|
|
195
|
+
def call
|
|
196
|
+
all_steps = parents.map(&:steps) + [steps]
|
|
197
|
+
|
|
198
|
+
result_steps = all_steps.inject { |result, steps| result.merge(steps) }
|
|
199
|
+
|
|
200
|
+
result_steps[:key_validator] = key_validator if config.validate_keys
|
|
201
|
+
result_steps[:key_coercer] = key_coercer
|
|
202
|
+
result_steps[:value_coercer] = value_coercer
|
|
203
|
+
result_steps[:rule_applier] = rule_applier
|
|
204
|
+
result_steps[:filter_schema] = filter_schema.rule_applier if filter_rules?
|
|
205
|
+
|
|
206
|
+
processor_type.new(schema_dsl: self, steps: result_steps)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Merge with another dsl
|
|
210
|
+
#
|
|
211
|
+
# @return [DSL]
|
|
212
|
+
#
|
|
213
|
+
# @api private
|
|
214
|
+
def merge(other)
|
|
215
|
+
new(
|
|
216
|
+
parent: parents + other.parents,
|
|
217
|
+
macros: macros + other.macros,
|
|
218
|
+
types: types.merge(other.types),
|
|
219
|
+
steps: steps.merge(other.steps)
|
|
220
|
+
)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Cast this DSL into a rule object
|
|
224
|
+
#
|
|
225
|
+
# @return [RuleApplier]
|
|
226
|
+
def to_rule
|
|
227
|
+
call.to_rule
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# A shortcut for defining an array type with a member
|
|
231
|
+
#
|
|
232
|
+
# @example
|
|
233
|
+
# required(:tags).filled(array[:string])
|
|
234
|
+
#
|
|
235
|
+
# @return [Dry::Types::Array::Member]
|
|
236
|
+
#
|
|
237
|
+
# @api public
|
|
238
|
+
def array
|
|
239
|
+
-> member_type { type_registry["array"].of(resolve_type(member_type)) }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Method allows steps injection to the processor
|
|
243
|
+
#
|
|
244
|
+
# @example
|
|
245
|
+
# before(:rule_applier) do |input|
|
|
246
|
+
# input.compact
|
|
247
|
+
# end
|
|
248
|
+
#
|
|
249
|
+
# @return [DSL]
|
|
250
|
+
#
|
|
251
|
+
# @api public
|
|
252
|
+
def before(key, &)
|
|
253
|
+
steps.before(key, &)
|
|
254
|
+
self
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Method allows steps injection to the processor
|
|
258
|
+
#
|
|
259
|
+
# @example
|
|
260
|
+
# after(:rule_applier) do |input|
|
|
261
|
+
# input.compact
|
|
262
|
+
# end
|
|
263
|
+
#
|
|
264
|
+
# @return [DSL]
|
|
265
|
+
#
|
|
266
|
+
# @api public
|
|
267
|
+
def after(key, &)
|
|
268
|
+
steps.after(key, &)
|
|
269
|
+
self
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# The parent (last from parents) which is used for copying non mergeable configuration
|
|
273
|
+
#
|
|
274
|
+
# @return DSL
|
|
275
|
+
#
|
|
276
|
+
# @api public
|
|
277
|
+
def parent
|
|
278
|
+
@parent ||= parents.last
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Return type schema used by the value coercer
|
|
282
|
+
#
|
|
283
|
+
# @return [Dry::Types::Lax]
|
|
284
|
+
#
|
|
285
|
+
# @api private
|
|
286
|
+
def type_schema
|
|
287
|
+
strict_type_schema.lax
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Return type schema used when composing subschemas
|
|
291
|
+
#
|
|
292
|
+
# @return [Dry::Types::Schema]
|
|
293
|
+
#
|
|
294
|
+
# @api private
|
|
295
|
+
def strict_type_schema
|
|
296
|
+
type_registry["hash"].schema(types)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Return a new DSL instance using the same processor type
|
|
300
|
+
#
|
|
301
|
+
# @return [Dry::Types::Safe]
|
|
302
|
+
#
|
|
303
|
+
# @api private
|
|
304
|
+
def new(klass: self.class, **options, &)
|
|
305
|
+
klass.new(**options, processor_type: processor_type, config: config, &)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Set a type for the given key name
|
|
309
|
+
#
|
|
310
|
+
# @param [Symbol] name The key name
|
|
311
|
+
# @param [Symbol, Array<Symbol>, Dry::Types::Type] spec The type spec or a type object
|
|
312
|
+
#
|
|
313
|
+
# @return [Dry::Types::Safe]
|
|
314
|
+
#
|
|
315
|
+
# @api private
|
|
316
|
+
def set_type(name, spec)
|
|
317
|
+
type = resolve_type(spec)
|
|
318
|
+
meta = {required: false, maybe: type.optional?}
|
|
319
|
+
|
|
320
|
+
@types[name] = type.meta(meta)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Check if a custom type was set under provided key name
|
|
324
|
+
#
|
|
325
|
+
# @return [Bool]
|
|
326
|
+
#
|
|
327
|
+
# @api private
|
|
328
|
+
def custom_type?(name)
|
|
329
|
+
!types[name].meta[:default].equal?(true)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Resolve type object from the provided spec
|
|
333
|
+
#
|
|
334
|
+
# @param [Symbol, Array<Symbol>, Dry::Types::Type] spec
|
|
335
|
+
#
|
|
336
|
+
# @return [Dry::Types::Type]
|
|
337
|
+
#
|
|
338
|
+
# @api private
|
|
339
|
+
def resolve_type(spec)
|
|
340
|
+
case spec
|
|
341
|
+
when ::Dry::Types::Type then spec
|
|
342
|
+
when ::Array then spec.map { |s| resolve_type(s) }.reduce(:|)
|
|
343
|
+
else
|
|
344
|
+
type_registry[spec]
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# @api private
|
|
349
|
+
def filter_schema
|
|
350
|
+
filter_schema_dsl.call
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Build an input schema DSL used by `filter` API
|
|
354
|
+
#
|
|
355
|
+
# @see Macros::Value#filter
|
|
356
|
+
#
|
|
357
|
+
# @api private
|
|
358
|
+
def filter_schema_dsl
|
|
359
|
+
@filter_schema_dsl ||= new(parent: parent_filter_schemas)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Check if any filter rules were defined
|
|
363
|
+
#
|
|
364
|
+
# @api private
|
|
365
|
+
def filter_rules?
|
|
366
|
+
if instance_variable_defined?("@filter_schema_dsl") && !filter_schema_dsl.macros.empty?
|
|
367
|
+
return true
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
parents.any?(&:filter_rules?)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# This DSL's type map merged with any parent type maps
|
|
374
|
+
#
|
|
375
|
+
# @api private
|
|
376
|
+
def types
|
|
377
|
+
[*parents.map(&:types), @types].reduce(:merge)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
# @api private
|
|
381
|
+
def merge_types(op_class, lhs, rhs)
|
|
382
|
+
types_merger.(op_class, lhs, rhs)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
protected
|
|
386
|
+
|
|
387
|
+
# Build a rule applier
|
|
388
|
+
#
|
|
389
|
+
# @return [RuleApplier]
|
|
390
|
+
#
|
|
391
|
+
# @api protected
|
|
392
|
+
def rule_applier
|
|
393
|
+
RuleApplier.new(rules, config: config.finalize!)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Build rules from defined macros
|
|
397
|
+
#
|
|
398
|
+
# @see #rule_applier
|
|
399
|
+
#
|
|
400
|
+
# @api protected
|
|
401
|
+
def rules
|
|
402
|
+
parent_rules.merge(macros.to_h { [_1.name, _1.to_rule] }.compact)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Build a key map from defined types
|
|
406
|
+
#
|
|
407
|
+
# @api protected
|
|
408
|
+
def key_map(types = self.types)
|
|
409
|
+
keys = types.map { |key, type| key_spec(key, type) }
|
|
410
|
+
km = KeyMap.new(keys)
|
|
411
|
+
|
|
412
|
+
if key_map_type
|
|
413
|
+
km.public_send(key_map_type)
|
|
414
|
+
else
|
|
415
|
+
km
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
private
|
|
420
|
+
|
|
421
|
+
# @api private
|
|
422
|
+
def parent_filter_schemas
|
|
423
|
+
parents.select(&:filter_rules?).map(&:filter_schema)
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Build a key validator
|
|
427
|
+
#
|
|
428
|
+
# @return [KeyValidator]
|
|
429
|
+
#
|
|
430
|
+
# @api private
|
|
431
|
+
def key_validator
|
|
432
|
+
KeyValidator.new(key_map: key_map)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
# Build a key coercer
|
|
436
|
+
#
|
|
437
|
+
# @return [KeyCoercer]
|
|
438
|
+
#
|
|
439
|
+
# @api private
|
|
440
|
+
def key_coercer
|
|
441
|
+
KeyCoercer.symbolized(key_map)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Build a value coercer
|
|
445
|
+
#
|
|
446
|
+
# @return [ValueCoercer]
|
|
447
|
+
#
|
|
448
|
+
# @api private
|
|
449
|
+
def value_coercer
|
|
450
|
+
ValueCoercer.new(type_schema)
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
# Return type registry configured by the processor type
|
|
454
|
+
#
|
|
455
|
+
# @api private
|
|
456
|
+
def type_registry
|
|
457
|
+
@type_registry ||= TypeRegistry.new(
|
|
458
|
+
config.types,
|
|
459
|
+
processor_type.config.type_registry_namespace
|
|
460
|
+
)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# Return key map type configured by the processor type
|
|
464
|
+
#
|
|
465
|
+
# @api private
|
|
466
|
+
def key_map_type
|
|
467
|
+
processor_type.config.key_map_type
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# Build a key spec needed by the key map
|
|
471
|
+
#
|
|
472
|
+
# TODO: we need a key-map compiler using Types AST
|
|
473
|
+
#
|
|
474
|
+
# @api private
|
|
475
|
+
def key_spec(name, type)
|
|
476
|
+
if type.respond_to?(:keys)
|
|
477
|
+
{name => key_map(type.name_key_map)}
|
|
478
|
+
elsif type.respond_to?(:member)
|
|
479
|
+
kv = key_spec(name, type.member)
|
|
480
|
+
kv.equal?(name) ? name : kv.flatten(1)
|
|
481
|
+
elsif type.meta[:maybe] && type.respond_to?(:right)
|
|
482
|
+
key_spec(name, type.right)
|
|
483
|
+
elsif type.respond_to?(:type)
|
|
484
|
+
key_spec(name, type.type)
|
|
485
|
+
else
|
|
486
|
+
name
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# @api private
|
|
491
|
+
def parent_rules
|
|
492
|
+
parents.reduce({}) { |rules, parent| rules.merge(parent.rules) }
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# @api private
|
|
496
|
+
def parent_key_map
|
|
497
|
+
parents.reduce([]) { |key_map, parent| parent.key_map + key_map }
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# @api private
|
|
501
|
+
def default_config
|
|
502
|
+
parents.each_cons(2) do |left, right|
|
|
503
|
+
unless left.config == right.config
|
|
504
|
+
raise ::ArgumentError,
|
|
505
|
+
"Parent configs differ, left=#{left.inspect}, right=#{right.inspect}"
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
(parent || Schema).config.dup
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def types_merger
|
|
513
|
+
@types_merger ||= TypesMerger.new(type_registry)
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Tweaks AND visitor to enable :hints
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module CompilerMethods
|
|
11
|
+
# @api private
|
|
12
|
+
def visit_and(node)
|
|
13
|
+
super.with(hints: true)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Adds support for processing [:hint, ...] nodes produced by dry-logic
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
module MessageCompilerMethods
|
|
11
|
+
HINT_TYPE_EXCLUSION = %i[
|
|
12
|
+
key? nil? bool? str? int? float? decimal?
|
|
13
|
+
date? date_time? time? hash? array?
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
HINT_OTHER_EXCLUSION = %i[format? filled?].freeze
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
attr_reader :hints
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def initialize(*, **)
|
|
23
|
+
super
|
|
24
|
+
@hints = @options.fetch(:hints, true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @api private
|
|
28
|
+
def hints?
|
|
29
|
+
hints.equal?(true)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
def filter(messages, opts)
|
|
34
|
+
Array(messages).flatten.reject { |msg| exclude?(msg, opts) }.uniq
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
# rubocop: disable Metrics/AbcSize
|
|
39
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
40
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
41
|
+
def exclude?(messages, opts)
|
|
42
|
+
Array(messages).all? do |msg|
|
|
43
|
+
hints = opts.hints.reject { |h|
|
|
44
|
+
msg.eql?(h) || h.predicate.eql?(:filled?)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
key_failure = opts.key_failure?(msg.path)
|
|
48
|
+
predicate = msg.predicate
|
|
49
|
+
|
|
50
|
+
(HINT_TYPE_EXCLUSION.include?(predicate) && !key_failure) ||
|
|
51
|
+
(msg.predicate == :filled? && key_failure) ||
|
|
52
|
+
(!key_failure && HINT_TYPE_EXCLUSION.include?(predicate) &&
|
|
53
|
+
!hints.empty? && hints.any? { |hint| hint.path == msg.path }) ||
|
|
54
|
+
HINT_OTHER_EXCLUSION.include?(predicate)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
58
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
59
|
+
# rubocop: enable Metrics/AbcSize
|
|
60
|
+
|
|
61
|
+
# @api private
|
|
62
|
+
def message_type(options)
|
|
63
|
+
options[:message_type].equal?(:hint) ? Hint : Message
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @api private
|
|
67
|
+
def visit_hint(node, opts)
|
|
68
|
+
if hints?
|
|
69
|
+
filter(visit(node, opts.(message_type: :hint)), opts)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @api private
|
|
74
|
+
def visit_predicate(node, opts)
|
|
75
|
+
message = super
|
|
76
|
+
opts.current_messages << message
|
|
77
|
+
message
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
def visit_each(_node, _opts)
|
|
82
|
+
# TODO: we can still generate a hint for elements here!
|
|
83
|
+
[]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Hint extensions for MessageSet
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module MessageSetMethods
|
|
11
|
+
# Filtered message hints from all messages
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Message::Hint>]
|
|
14
|
+
attr_reader :hints
|
|
15
|
+
|
|
16
|
+
# Configuration option to enable/disable showing errors
|
|
17
|
+
#
|
|
18
|
+
# @return [Boolean]
|
|
19
|
+
attr_reader :failures
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def initialize(messages, options = EMPTY_HASH)
|
|
23
|
+
super
|
|
24
|
+
@hints = messages.select(&:hint?)
|
|
25
|
+
@failures = options.fetch(:failures, true)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Dump message set to a hash with either all messages or just hints
|
|
29
|
+
#
|
|
30
|
+
# @see MessageSet#to_h
|
|
31
|
+
# @see ResultMethods#hints
|
|
32
|
+
#
|
|
33
|
+
# @return [Hash<Symbol=>Array<String>>]
|
|
34
|
+
#
|
|
35
|
+
# @api public
|
|
36
|
+
def to_h
|
|
37
|
+
@to_h ||= failures ? messages_map : messages_map(hints)
|
|
38
|
+
end
|
|
39
|
+
alias_method :to_hash, :to_h
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
module Extensions
|
|
6
|
+
module Hints
|
|
7
|
+
# Get errors exclusively without hints
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
module ResultMethods
|
|
11
|
+
# Return error messages exclusively
|
|
12
|
+
#
|
|
13
|
+
# @see Result#errors
|
|
14
|
+
#
|
|
15
|
+
# @return [MessageSet]
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def errors(options = EMPTY_HASH)
|
|
19
|
+
message_set(options.merge(hints: false))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get all messages including hints
|
|
23
|
+
#
|
|
24
|
+
# @see #message_set
|
|
25
|
+
#
|
|
26
|
+
# @return [MessageSet]
|
|
27
|
+
#
|
|
28
|
+
# @api public
|
|
29
|
+
def messages(options = EMPTY_HASH)
|
|
30
|
+
message_set(options)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Get hints exclusively without errors
|
|
34
|
+
#
|
|
35
|
+
# @see #message_set
|
|
36
|
+
#
|
|
37
|
+
# @return [MessageSet]
|
|
38
|
+
#
|
|
39
|
+
# @api public
|
|
40
|
+
def hints(options = EMPTY_HASH)
|
|
41
|
+
message_set(options.merge(failures: false))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|