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,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dry
|
|
4
|
+
module Schema
|
|
5
|
+
# @api private
|
|
6
|
+
class PrimitiveInferrer < ::Dry::Types::PrimitiveInferrer
|
|
7
|
+
Compiler = ::Class.new(superclass::Compiler) do
|
|
8
|
+
# @api private
|
|
9
|
+
def visit_intersection(node)
|
|
10
|
+
left, right = node
|
|
11
|
+
[visit(left), visit(right)].flatten(1)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize
|
|
16
|
+
super
|
|
17
|
+
|
|
18
|
+
@compiler = Compiler.new
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
# Processes input data using objects configured within the DSL
|
|
8
|
+
# Processing is split into steps represented by `ProcessorSteps`.
|
|
9
|
+
#
|
|
10
|
+
# @see ProcessorSteps
|
|
11
|
+
# @see Params
|
|
12
|
+
# @see JSON
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class Processor
|
|
16
|
+
extend ::Dry::Initializer
|
|
17
|
+
extend ::Dry::Configurable
|
|
18
|
+
|
|
19
|
+
include ::Dry::Logic::Operators
|
|
20
|
+
|
|
21
|
+
setting :key_map_type
|
|
22
|
+
setting :type_registry_namespace, default: :strict
|
|
23
|
+
setting :filter_empty_string, default: false
|
|
24
|
+
|
|
25
|
+
option :steps, default: -> { ProcessorSteps.new }
|
|
26
|
+
|
|
27
|
+
option :schema_dsl
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# Return DSL configured via #define
|
|
31
|
+
#
|
|
32
|
+
# @return [DSL]
|
|
33
|
+
# @api private
|
|
34
|
+
attr_reader :definition
|
|
35
|
+
|
|
36
|
+
# Define a schema for your processor class
|
|
37
|
+
#
|
|
38
|
+
# @see Schema#define
|
|
39
|
+
# @see Schema#Params
|
|
40
|
+
# @see Schema#JSON
|
|
41
|
+
#
|
|
42
|
+
# @return [Class]
|
|
43
|
+
#
|
|
44
|
+
# @api public
|
|
45
|
+
def define(&)
|
|
46
|
+
@definition ||= DSL.new(
|
|
47
|
+
processor_type: self, parent: superclass.definition, **config.to_h, &
|
|
48
|
+
)
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Build a new processor object
|
|
53
|
+
#
|
|
54
|
+
# @return [Processor]
|
|
55
|
+
#
|
|
56
|
+
# @api public
|
|
57
|
+
def new(options = nil)
|
|
58
|
+
if options || block_given?
|
|
59
|
+
processor = super(**(options || EMPTY_HASH))
|
|
60
|
+
yield(processor) if block_given?
|
|
61
|
+
processor
|
|
62
|
+
elsif definition
|
|
63
|
+
definition.call
|
|
64
|
+
else
|
|
65
|
+
raise ::ArgumentError, "Cannot create a schema without a definition"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Apply processing steps to the provided input
|
|
71
|
+
#
|
|
72
|
+
# @param [Hash] input
|
|
73
|
+
#
|
|
74
|
+
# @return [Result]
|
|
75
|
+
#
|
|
76
|
+
# @api public
|
|
77
|
+
def call(input)
|
|
78
|
+
Result.new(input.dup, message_compiler: message_compiler) do |result|
|
|
79
|
+
steps.call(result)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
alias_method :[], :call
|
|
83
|
+
|
|
84
|
+
# @api public
|
|
85
|
+
def xor(_other)
|
|
86
|
+
raise ::NotImplementedError, "composing schemas using `xor` operator is not supported yet"
|
|
87
|
+
end
|
|
88
|
+
alias_method :^, :xor
|
|
89
|
+
|
|
90
|
+
# Merge with another schema
|
|
91
|
+
#
|
|
92
|
+
# @param [Processor] other
|
|
93
|
+
#
|
|
94
|
+
# @return [Processor, Params, JSON]
|
|
95
|
+
#
|
|
96
|
+
# @api public
|
|
97
|
+
def merge(other)
|
|
98
|
+
schema_dsl.merge(other.schema_dsl).()
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Return a proc that acts like a schema object
|
|
102
|
+
#
|
|
103
|
+
# @return [Proc]
|
|
104
|
+
#
|
|
105
|
+
# @api public
|
|
106
|
+
def to_proc
|
|
107
|
+
->(input) { call(input) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Return string representation
|
|
111
|
+
#
|
|
112
|
+
# @return [String]
|
|
113
|
+
#
|
|
114
|
+
# @api public
|
|
115
|
+
def inspect
|
|
116
|
+
<<~STR.strip
|
|
117
|
+
#<#{self.class.name} keys=#{key_map.map(&:dump)} rules=#{rules.transform_values(&:to_s)}>
|
|
118
|
+
STR
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Return the key map
|
|
122
|
+
#
|
|
123
|
+
# @return [KeyMap]
|
|
124
|
+
#
|
|
125
|
+
# @api public
|
|
126
|
+
def key_map
|
|
127
|
+
steps.key_map
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Return the type schema
|
|
131
|
+
#
|
|
132
|
+
# @return [Dry::Types::Lax]
|
|
133
|
+
#
|
|
134
|
+
# @api private
|
|
135
|
+
def type_schema
|
|
136
|
+
steps.type_schema
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Return type schema used when composing subschemas
|
|
140
|
+
#
|
|
141
|
+
# @return [Dry::Types::Schema]
|
|
142
|
+
#
|
|
143
|
+
# @api private
|
|
144
|
+
def strict_type_schema
|
|
145
|
+
schema_dsl.strict_type_schema
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Return the rule applier
|
|
149
|
+
#
|
|
150
|
+
# @api private
|
|
151
|
+
def rule_applier
|
|
152
|
+
steps.rule_applier
|
|
153
|
+
end
|
|
154
|
+
alias_method :to_rule, :rule_applier
|
|
155
|
+
|
|
156
|
+
# Return the rules config
|
|
157
|
+
#
|
|
158
|
+
# @return [Dry::Types::Config]
|
|
159
|
+
#
|
|
160
|
+
# @api private
|
|
161
|
+
def config
|
|
162
|
+
@config ||= rule_applier.config
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Return AST representation of the rules
|
|
166
|
+
#
|
|
167
|
+
# @api private
|
|
168
|
+
def to_ast(*)
|
|
169
|
+
rule_applier.to_ast
|
|
170
|
+
end
|
|
171
|
+
alias_method :ast, :to_ast
|
|
172
|
+
|
|
173
|
+
# Return the message compiler
|
|
174
|
+
#
|
|
175
|
+
# @return [MessageCompiler]
|
|
176
|
+
#
|
|
177
|
+
# @api private
|
|
178
|
+
def message_compiler
|
|
179
|
+
rule_applier.message_compiler
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Return the rules from rule applier
|
|
183
|
+
#
|
|
184
|
+
# @return [MessageCompiler]
|
|
185
|
+
#
|
|
186
|
+
# @api private
|
|
187
|
+
def rules
|
|
188
|
+
rule_applier.rules
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Return the types from the schema DSL
|
|
192
|
+
#
|
|
193
|
+
# @api private
|
|
194
|
+
def types
|
|
195
|
+
schema_dsl.types
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Check if there are filter rules
|
|
199
|
+
#
|
|
200
|
+
# @api private
|
|
201
|
+
def filter_rules?
|
|
202
|
+
@filter_rules_predicate ||= schema_dsl.filter_rules?
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Return filter schema
|
|
206
|
+
#
|
|
207
|
+
# @api private
|
|
208
|
+
def filter_schema
|
|
209
|
+
@filter_schema ||= schema_dsl.filter_schema
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/schema/constants"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# Steps for the Dry::Schema::Processor
|
|
9
|
+
#
|
|
10
|
+
# There are 4 main steps:
|
|
11
|
+
#
|
|
12
|
+
# 1. `key_coercer` - Prepare input hash using a key map
|
|
13
|
+
# 2. `filter_schema` - Apply pre-coercion filtering rules
|
|
14
|
+
# (optional step, used only when `filter` was used)
|
|
15
|
+
# 3. `value_coercer` - Apply value coercions based on type specifications
|
|
16
|
+
# 4. `rule_applier` - Apply rules
|
|
17
|
+
#
|
|
18
|
+
# @see Processor
|
|
19
|
+
#
|
|
20
|
+
# @api public
|
|
21
|
+
class ProcessorSteps
|
|
22
|
+
extend ::Dry::Initializer
|
|
23
|
+
|
|
24
|
+
option :steps, default: -> { EMPTY_HASH.dup }
|
|
25
|
+
option :before_steps, default: -> { EMPTY_HASH.dup }
|
|
26
|
+
option :after_steps, default: -> { EMPTY_HASH.dup }
|
|
27
|
+
|
|
28
|
+
# Executes steps and callbacks in order
|
|
29
|
+
#
|
|
30
|
+
# @param [Result] result
|
|
31
|
+
#
|
|
32
|
+
# @return [Result]
|
|
33
|
+
#
|
|
34
|
+
# @api public
|
|
35
|
+
def call(result)
|
|
36
|
+
STEPS_IN_ORDER.each do |name|
|
|
37
|
+
before_steps[name]&.each { |step| step&.(result) }
|
|
38
|
+
steps[name]&.(result)
|
|
39
|
+
after_steps[name]&.each { |step| step&.(result) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
result
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @api private
|
|
46
|
+
def rule_applier
|
|
47
|
+
@rule_applier ||= steps[:rule_applier].executor
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api private
|
|
51
|
+
def key_map
|
|
52
|
+
@key_map ||= self[:key_coercer].executor.key_map
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @api private
|
|
56
|
+
def type_schema
|
|
57
|
+
@type_schema ||= steps[:value_coercer].executor.type_schema
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns step by name
|
|
61
|
+
#
|
|
62
|
+
# @param [Symbol] name The step name
|
|
63
|
+
#
|
|
64
|
+
# @api public
|
|
65
|
+
def [](name)
|
|
66
|
+
steps[name]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Sets step by name
|
|
70
|
+
#
|
|
71
|
+
# @param [Symbol] name The step name
|
|
72
|
+
#
|
|
73
|
+
# @api public
|
|
74
|
+
def []=(name, value)
|
|
75
|
+
steps[name] = Step.new(type: :core, name: name, executor: value)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Add passed block before mentioned step
|
|
79
|
+
#
|
|
80
|
+
# @param [Symbol] name The step name
|
|
81
|
+
#
|
|
82
|
+
# @return [ProcessorSteps]
|
|
83
|
+
#
|
|
84
|
+
# @api public
|
|
85
|
+
def after(name, &block)
|
|
86
|
+
after_steps[name] ||= EMPTY_ARRAY.dup
|
|
87
|
+
after_steps[name] << Step.new(type: :after, name: name, executor: block)
|
|
88
|
+
after_steps[name].sort_by!(&:path)
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Add passed block before mentioned step
|
|
93
|
+
#
|
|
94
|
+
# @param [Symbol] name The step name
|
|
95
|
+
#
|
|
96
|
+
# @return [ProcessorSteps]
|
|
97
|
+
#
|
|
98
|
+
# @api public
|
|
99
|
+
def before(name, &block)
|
|
100
|
+
before_steps[name] ||= EMPTY_ARRAY.dup
|
|
101
|
+
before_steps[name] << Step.new(type: :before, name: name, executor: block)
|
|
102
|
+
before_steps[name].sort_by!(&:path)
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Stacks callback steps and returns new ProcessorSteps instance
|
|
107
|
+
#
|
|
108
|
+
# @param [ProcessorSteps] other
|
|
109
|
+
#
|
|
110
|
+
# @return [ProcessorSteps]
|
|
111
|
+
#
|
|
112
|
+
# @api public
|
|
113
|
+
def merge(other)
|
|
114
|
+
ProcessorSteps.new(
|
|
115
|
+
before_steps: merge_callbacks(before_steps, other.before_steps),
|
|
116
|
+
after_steps: merge_callbacks(after_steps, other.after_steps)
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @api private
|
|
121
|
+
def merge_callbacks(left, right)
|
|
122
|
+
left.merge(right) do |_key, oldval, newval|
|
|
123
|
+
(oldval + newval).sort_by(&:path)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @api private
|
|
128
|
+
def import_callbacks(path, other)
|
|
129
|
+
other.before_steps.each do |name, steps|
|
|
130
|
+
before_steps[name] ||= []
|
|
131
|
+
before_steps[name].concat(steps.map { |step| step.scoped(path) }).sort_by!(&:path)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
other.after_steps.each do |name, steps|
|
|
135
|
+
after_steps[name] ||= []
|
|
136
|
+
after_steps[name].concat(steps.map { |step| step.scoped(path) }).sort_by!(&:path)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/core/equalizer"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# Processing result
|
|
9
|
+
#
|
|
10
|
+
# @see Processor#call
|
|
11
|
+
#
|
|
12
|
+
# @api public
|
|
13
|
+
class Result
|
|
14
|
+
include ::Dry::Equalizer(:output, :errors)
|
|
15
|
+
|
|
16
|
+
extend ::Dry::Initializer[undefined: false]
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
param :output, reader: false
|
|
20
|
+
|
|
21
|
+
# A list of failure ASTs produced by rule result objects
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
24
|
+
option :result_ast, default: -> { EMPTY_ARRAY.dup }
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
option :message_compiler
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
option :path, optional: true, reader: false
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
def self.new(*, **)
|
|
34
|
+
result = super
|
|
35
|
+
yield(result) if block_given?
|
|
36
|
+
result.freeze
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Return a new result scoped to a specific path
|
|
40
|
+
#
|
|
41
|
+
# @param path [Symbol, Array, Path]
|
|
42
|
+
#
|
|
43
|
+
# @return [Result]
|
|
44
|
+
#
|
|
45
|
+
# @api private
|
|
46
|
+
def at(at_path, &)
|
|
47
|
+
new(@output, path: Path.new([*path, *Path[at_path]]), &)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @api private
|
|
51
|
+
def new(output, **opts, &)
|
|
52
|
+
self.class.new(
|
|
53
|
+
output,
|
|
54
|
+
message_compiler: message_compiler,
|
|
55
|
+
result_ast: result_ast,
|
|
56
|
+
**opts,
|
|
57
|
+
&
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @api private
|
|
62
|
+
def update(hash)
|
|
63
|
+
output.update(hash)
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
def path
|
|
69
|
+
@path || Path::EMPTY
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Dump result to a hash returning processed and validated data
|
|
73
|
+
#
|
|
74
|
+
# @return [Hash]
|
|
75
|
+
#
|
|
76
|
+
# @api private
|
|
77
|
+
def output
|
|
78
|
+
path.equal?(Path::EMPTY) ? @output : @output.dig(*path)
|
|
79
|
+
end
|
|
80
|
+
alias_method :to_h, :output
|
|
81
|
+
|
|
82
|
+
# @api private
|
|
83
|
+
def replace(value)
|
|
84
|
+
if value.is_a?(output.class)
|
|
85
|
+
output.replace(value)
|
|
86
|
+
elsif path.equal?(Path::EMPTY)
|
|
87
|
+
@output = value
|
|
88
|
+
else
|
|
89
|
+
value_holder = path.keys.length > 1 ? @output.dig(*path.to_a[0..-2]) : @output
|
|
90
|
+
|
|
91
|
+
value_holder[path.last] = value
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
self
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @api private
|
|
98
|
+
def concat(other)
|
|
99
|
+
result_ast.concat(other.map(&:to_ast))
|
|
100
|
+
self
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Read value from the output hash
|
|
104
|
+
#
|
|
105
|
+
# @param [Symbol] name
|
|
106
|
+
#
|
|
107
|
+
# @return [Object]
|
|
108
|
+
#
|
|
109
|
+
# @api public
|
|
110
|
+
def [](name)
|
|
111
|
+
output[name]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Check if a given key is present in the output
|
|
115
|
+
#
|
|
116
|
+
# @param [Symbol] name
|
|
117
|
+
#
|
|
118
|
+
# @return [Boolean]
|
|
119
|
+
#
|
|
120
|
+
# @api public
|
|
121
|
+
def key?(name)
|
|
122
|
+
output.key?(name)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Check if there's an error for the provided spec
|
|
126
|
+
#
|
|
127
|
+
# @param [Symbol, Hash<Symbol=>Symbol>] spec
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean]
|
|
130
|
+
#
|
|
131
|
+
# @api public
|
|
132
|
+
def error?(spec)
|
|
133
|
+
message_set.any? { |msg| Path[msg.path].include?(Path[spec]) }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Check if the result is successful
|
|
137
|
+
#
|
|
138
|
+
# @return [Boolean]
|
|
139
|
+
#
|
|
140
|
+
# @api public
|
|
141
|
+
def success?
|
|
142
|
+
result_ast.empty?
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Check if the result is not successful
|
|
146
|
+
#
|
|
147
|
+
# @return [Boolean]
|
|
148
|
+
#
|
|
149
|
+
# @api public
|
|
150
|
+
def failure?
|
|
151
|
+
!success?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Get human-readable error representation
|
|
155
|
+
#
|
|
156
|
+
# @see #message_set
|
|
157
|
+
#
|
|
158
|
+
# @return [MessageSet]
|
|
159
|
+
#
|
|
160
|
+
# @api public
|
|
161
|
+
def errors(options = EMPTY_HASH)
|
|
162
|
+
message_set(options)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Return the message set
|
|
166
|
+
#
|
|
167
|
+
# @param [Hash] options
|
|
168
|
+
# @option options [Symbol] :locale Alternative locale (default is :en)
|
|
169
|
+
# @option options [Boolean] :hints Whether to include hint messages or not
|
|
170
|
+
# @option options [Boolean] :full Whether to generate messages that include key names
|
|
171
|
+
#
|
|
172
|
+
# @return [MessageSet]
|
|
173
|
+
#
|
|
174
|
+
# @api public
|
|
175
|
+
def message_set(options = EMPTY_HASH)
|
|
176
|
+
message_compiler.with(options).(result_ast)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Return a string representation of the result
|
|
180
|
+
#
|
|
181
|
+
# @return [String]
|
|
182
|
+
#
|
|
183
|
+
# @api public
|
|
184
|
+
def inspect
|
|
185
|
+
"#<#{self.class}#{to_h.inspect} errors=#{errors.to_h.inspect} path=#{path.keys.inspect}>"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Pattern matching support
|
|
189
|
+
#
|
|
190
|
+
# @api private
|
|
191
|
+
def deconstruct_keys(_)
|
|
192
|
+
output
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Add a new error AST node
|
|
196
|
+
#
|
|
197
|
+
# @api private
|
|
198
|
+
def add_error(node)
|
|
199
|
+
result_ast << node
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/initializer"
|
|
4
|
+
require "dry/schema/constants"
|
|
5
|
+
|
|
6
|
+
module Dry
|
|
7
|
+
module Schema
|
|
8
|
+
# Applies rules defined within the DSL
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class RuleApplier
|
|
12
|
+
extend ::Dry::Initializer
|
|
13
|
+
|
|
14
|
+
# @api private
|
|
15
|
+
param :rules
|
|
16
|
+
|
|
17
|
+
# @api private
|
|
18
|
+
option :config, default: -> { Schema.config.dup }
|
|
19
|
+
|
|
20
|
+
# @api private
|
|
21
|
+
option :message_compiler, default: -> { MessageCompiler.new(Messages.setup(config.messages)) }
|
|
22
|
+
|
|
23
|
+
# @api private
|
|
24
|
+
def call(input)
|
|
25
|
+
results = EMPTY_ARRAY.dup
|
|
26
|
+
|
|
27
|
+
rules.each do |name, rule|
|
|
28
|
+
next if input.error?(name)
|
|
29
|
+
|
|
30
|
+
result = rule.(input.to_h)
|
|
31
|
+
results << result if result.failure?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
input.concat(results)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
def to_ast
|
|
39
|
+
if config.messages.namespace
|
|
40
|
+
[:namespace, [config.messages.namespace, [:set, rules.values.map(&:to_ast)]]]
|
|
41
|
+
else
|
|
42
|
+
[:set, rules.values.map(&:to_ast)]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/schema/constants"
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Schema
|
|
7
|
+
# @api private
|
|
8
|
+
class Step
|
|
9
|
+
# @api private
|
|
10
|
+
attr_reader :name
|
|
11
|
+
|
|
12
|
+
# @api private
|
|
13
|
+
attr_reader :type
|
|
14
|
+
|
|
15
|
+
# @api private
|
|
16
|
+
attr_reader :executor
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
attr_reader :path
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def initialize(type:, name:, executor:, path: Path::EMPTY)
|
|
23
|
+
@type = type
|
|
24
|
+
@name = name
|
|
25
|
+
@executor = executor
|
|
26
|
+
@path = path
|
|
27
|
+
validate_name(name)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @api private
|
|
31
|
+
def call(result)
|
|
32
|
+
scoped_result = path.equal?(Path::EMPTY) ? result : result.at(path)
|
|
33
|
+
|
|
34
|
+
output = executor.(scoped_result)
|
|
35
|
+
scoped_result.replace(output) if output.is_a?(Hash)
|
|
36
|
+
output
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api private
|
|
40
|
+
def scoped(parent_path)
|
|
41
|
+
self.class.new(
|
|
42
|
+
type: type,
|
|
43
|
+
name: name,
|
|
44
|
+
executor: executor,
|
|
45
|
+
path: Path.new([*parent_path, *path])
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# @api private
|
|
52
|
+
def validate_name(name)
|
|
53
|
+
return if STEPS_IN_ORDER.include?(name)
|
|
54
|
+
|
|
55
|
+
raise ArgumentError, "Undefined step name #{name}. Available names: #{STEPS_IN_ORDER}"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|