dry-validation 0.13.3 → 1.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +506 -95
- data/LICENSE +1 -1
- data/README.md +16 -12
- data/config/errors.yml +3 -88
- data/dry-validation.gemspec +37 -24
- data/lib/dry/validation/config.rb +24 -0
- data/lib/dry/validation/constants.rb +43 -0
- data/lib/dry/validation/contract/class_interface.rb +230 -0
- data/lib/dry/validation/contract.rb +171 -0
- data/lib/dry/validation/evaluator.rb +224 -0
- data/lib/dry/validation/extensions/hints.rb +67 -0
- data/lib/dry/validation/extensions/monads.rb +24 -8
- data/lib/dry/validation/extensions/predicates_as_macros.rb +75 -0
- data/lib/dry/validation/failures.rb +70 -0
- data/lib/dry/validation/function.rb +44 -0
- data/lib/dry/validation/macro.rb +38 -0
- data/lib/dry/validation/macros.rb +104 -0
- data/lib/dry/validation/message.rb +80 -80
- data/lib/dry/validation/message_set.rb +80 -105
- data/lib/dry/validation/messages/resolver.rb +131 -0
- data/lib/dry/validation/result.rb +183 -41
- data/lib/dry/validation/rule.rb +135 -0
- data/lib/dry/validation/schema_ext.rb +19 -0
- data/lib/dry/validation/values.rb +104 -0
- data/lib/dry/validation/version.rb +3 -1
- data/lib/dry/validation.rb +45 -28
- data/lib/dry-validation.rb +3 -1
- metadata +46 -344
- data/.codeclimate.yml +0 -17
- data/.gitignore +0 -9
- data/.rspec +0 -3
- data/.travis.yml +0 -29
- data/CONTRIBUTING.md +0 -31
- data/Gemfile +0 -25
- data/Rakefile +0 -22
- data/benchmarks/benchmark_form_invalid.rb +0 -64
- data/benchmarks/benchmark_form_valid.rb +0 -64
- data/benchmarks/benchmark_schema_invalid_huge.rb +0 -52
- data/benchmarks/profile_schema_call_invalid.rb +0 -20
- data/benchmarks/profile_schema_call_valid.rb +0 -20
- data/benchmarks/profile_schema_definition.rb +0 -14
- data/benchmarks/profile_schema_huge_invalid.rb +0 -30
- data/benchmarks/profile_schema_messages_invalid.rb +0 -20
- data/benchmarks/suite.rb +0 -5
- data/examples/basic.rb +0 -15
- data/examples/each.rb +0 -14
- data/examples/json.rb +0 -12
- data/examples/multiple.rb +0 -27
- data/examples/nested.rb +0 -22
- data/examples/params.rb +0 -11
- data/lib/dry/validation/compat/form.rb +0 -67
- data/lib/dry/validation/deprecations.rb +0 -24
- data/lib/dry/validation/executor.rb +0 -91
- data/lib/dry/validation/extensions/struct.rb +0 -32
- data/lib/dry/validation/extensions.rb +0 -7
- data/lib/dry/validation/input_processor_compiler/json.rb +0 -45
- data/lib/dry/validation/input_processor_compiler/params.rb +0 -49
- data/lib/dry/validation/input_processor_compiler/sanitizer.rb +0 -47
- data/lib/dry/validation/input_processor_compiler.rb +0 -137
- data/lib/dry/validation/message_compiler/visitor_opts.rb +0 -37
- data/lib/dry/validation/message_compiler.rb +0 -188
- data/lib/dry/validation/messages/abstract.rb +0 -119
- data/lib/dry/validation/messages/i18n.rb +0 -47
- data/lib/dry/validation/messages/namespaced.rb +0 -39
- data/lib/dry/validation/messages/yaml.rb +0 -61
- data/lib/dry/validation/messages.rb +0 -14
- data/lib/dry/validation/predicate_registry.rb +0 -115
- data/lib/dry/validation/predicates.rb +0 -19
- data/lib/dry/validation/schema/check.rb +0 -37
- data/lib/dry/validation/schema/class_interface.rb +0 -190
- data/lib/dry/validation/schema/deprecated.rb +0 -30
- data/lib/dry/validation/schema/dsl.rb +0 -118
- data/lib/dry/validation/schema/form.rb +0 -9
- data/lib/dry/validation/schema/json.rb +0 -21
- data/lib/dry/validation/schema/key.rb +0 -71
- data/lib/dry/validation/schema/params.rb +0 -22
- data/lib/dry/validation/schema/rule.rb +0 -202
- data/lib/dry/validation/schema/value.rb +0 -211
- data/lib/dry/validation/schema.rb +0 -126
- data/lib/dry/validation/schema_compiler.rb +0 -81
- data/lib/dry/validation/template.rb +0 -66
- data/lib/dry/validation/type_specs.rb +0 -70
- data/log/.gitkeep +0 -0
- data/spec/extensions/monads/result_spec.rb +0 -40
- data/spec/extensions/struct/schema_spec.rb +0 -32
- data/spec/fixtures/locales/en.yml +0 -8
- data/spec/fixtures/locales/pl.yml +0 -22
- data/spec/integration/custom_error_messages_spec.rb +0 -54
- data/spec/integration/custom_predicates_spec.rb +0 -228
- data/spec/integration/hints_spec.rb +0 -170
- data/spec/integration/injecting_rules_spec.rb +0 -30
- data/spec/integration/json/defining_base_schema_spec.rb +0 -41
- data/spec/integration/localized_error_messages_spec.rb +0 -72
- data/spec/integration/message_compiler_spec.rb +0 -405
- data/spec/integration/messages/i18n_spec.rb +0 -104
- data/spec/integration/optional_keys_spec.rb +0 -28
- data/spec/integration/params/predicates/array_spec.rb +0 -287
- data/spec/integration/params/predicates/empty_spec.rb +0 -263
- data/spec/integration/params/predicates/eql_spec.rb +0 -327
- data/spec/integration/params/predicates/even_spec.rb +0 -455
- data/spec/integration/params/predicates/excluded_from_spec.rb +0 -455
- data/spec/integration/params/predicates/excludes_spec.rb +0 -391
- data/spec/integration/params/predicates/false_spec.rb +0 -455
- data/spec/integration/params/predicates/filled_spec.rb +0 -467
- data/spec/integration/params/predicates/format_spec.rb +0 -454
- data/spec/integration/params/predicates/gt_spec.rb +0 -519
- data/spec/integration/params/predicates/gteq_spec.rb +0 -519
- data/spec/integration/params/predicates/included_in_spec.rb +0 -455
- data/spec/integration/params/predicates/includes_spec.rb +0 -391
- data/spec/integration/params/predicates/key_spec.rb +0 -67
- data/spec/integration/params/predicates/lt_spec.rb +0 -519
- data/spec/integration/params/predicates/lteq_spec.rb +0 -519
- data/spec/integration/params/predicates/max_size_spec.rb +0 -391
- data/spec/integration/params/predicates/min_size_spec.rb +0 -391
- data/spec/integration/params/predicates/none_spec.rb +0 -265
- data/spec/integration/params/predicates/not_eql_spec.rb +0 -327
- data/spec/integration/params/predicates/odd_spec.rb +0 -455
- data/spec/integration/params/predicates/size/fixed_spec.rb +0 -393
- data/spec/integration/params/predicates/size/range_spec.rb +0 -396
- data/spec/integration/params/predicates/true_spec.rb +0 -455
- data/spec/integration/params/predicates/type_spec.rb +0 -391
- data/spec/integration/result_spec.rb +0 -81
- data/spec/integration/schema/array_schema_spec.rb +0 -59
- data/spec/integration/schema/check_rules_spec.rb +0 -119
- data/spec/integration/schema/check_with_nested_el_spec.rb +0 -37
- data/spec/integration/schema/check_with_nth_el_spec.rb +0 -25
- data/spec/integration/schema/default_settings_spec.rb +0 -11
- data/spec/integration/schema/defining_base_schema_spec.rb +0 -41
- data/spec/integration/schema/dynamic_predicate_args_spec.rb +0 -43
- data/spec/integration/schema/each_with_set_spec.rb +0 -70
- data/spec/integration/schema/extending_dsl_spec.rb +0 -27
- data/spec/integration/schema/form_spec.rb +0 -236
- data/spec/integration/schema/hash_schema_spec.rb +0 -47
- data/spec/integration/schema/inheriting_schema_spec.rb +0 -31
- data/spec/integration/schema/input_processor_spec.rb +0 -46
- data/spec/integration/schema/json/explicit_types_spec.rb +0 -157
- data/spec/integration/schema/json_spec.rb +0 -163
- data/spec/integration/schema/macros/confirmation_spec.rb +0 -35
- data/spec/integration/schema/macros/each_spec.rb +0 -268
- data/spec/integration/schema/macros/filled_spec.rb +0 -87
- data/spec/integration/schema/macros/input_spec.rb +0 -139
- data/spec/integration/schema/macros/maybe_spec.rb +0 -99
- data/spec/integration/schema/macros/rule_spec.rb +0 -75
- data/spec/integration/schema/macros/value_spec.rb +0 -119
- data/spec/integration/schema/macros/when_spec.rb +0 -62
- data/spec/integration/schema/nested_schemas_spec.rb +0 -236
- data/spec/integration/schema/nested_values_spec.rb +0 -46
- data/spec/integration/schema/not_spec.rb +0 -34
- data/spec/integration/schema/numbers_spec.rb +0 -19
- data/spec/integration/schema/option_with_default_spec.rb +0 -64
- data/spec/integration/schema/or_spec.rb +0 -87
- data/spec/integration/schema/params/defining_base_schema_spec.rb +0 -41
- data/spec/integration/schema/params/explicit_types_spec.rb +0 -195
- data/spec/integration/schema/params_spec.rb +0 -234
- data/spec/integration/schema/predicate_verification_spec.rb +0 -9
- data/spec/integration/schema/predicates/array_spec.rb +0 -295
- data/spec/integration/schema/predicates/custom_spec.rb +0 -103
- data/spec/integration/schema/predicates/empty_spec.rb +0 -263
- data/spec/integration/schema/predicates/eql_spec.rb +0 -327
- data/spec/integration/schema/predicates/even_spec.rb +0 -455
- data/spec/integration/schema/predicates/excluded_from/array_spec.rb +0 -459
- data/spec/integration/schema/predicates/excluded_from/range_spec.rb +0 -459
- data/spec/integration/schema/predicates/excludes_spec.rb +0 -391
- data/spec/integration/schema/predicates/filled_spec.rb +0 -467
- data/spec/integration/schema/predicates/format_spec.rb +0 -455
- data/spec/integration/schema/predicates/gt_spec.rb +0 -519
- data/spec/integration/schema/predicates/gteq_spec.rb +0 -519
- data/spec/integration/schema/predicates/hash_spec.rb +0 -69
- data/spec/integration/schema/predicates/included_in/array_spec.rb +0 -459
- data/spec/integration/schema/predicates/included_in/range_spec.rb +0 -459
- data/spec/integration/schema/predicates/includes_spec.rb +0 -391
- data/spec/integration/schema/predicates/key_spec.rb +0 -88
- data/spec/integration/schema/predicates/lt_spec.rb +0 -520
- data/spec/integration/schema/predicates/lteq_spec.rb +0 -519
- data/spec/integration/schema/predicates/max_size_spec.rb +0 -391
- data/spec/integration/schema/predicates/min_size_spec.rb +0 -391
- data/spec/integration/schema/predicates/none_spec.rb +0 -265
- data/spec/integration/schema/predicates/not_eql_spec.rb +0 -391
- data/spec/integration/schema/predicates/odd_spec.rb +0 -455
- data/spec/integration/schema/predicates/size/fixed_spec.rb +0 -398
- data/spec/integration/schema/predicates/size/range_spec.rb +0 -395
- data/spec/integration/schema/predicates/type_spec.rb +0 -413
- data/spec/integration/schema/reusing_schema_spec.rb +0 -33
- data/spec/integration/schema/using_types_spec.rb +0 -135
- data/spec/integration/schema/validate_spec.rb +0 -120
- data/spec/integration/schema/xor_spec.rb +0 -35
- data/spec/integration/schema_builders_spec.rb +0 -17
- data/spec/integration/schema_spec.rb +0 -173
- data/spec/shared/message_compiler.rb +0 -11
- data/spec/shared/predicate_helper.rb +0 -15
- data/spec/shared/rule_compiler.rb +0 -8
- data/spec/spec_helper.rb +0 -62
- data/spec/support/define_struct.rb +0 -25
- data/spec/support/matchers.rb +0 -38
- data/spec/support/mutant.rb +0 -9
- data/spec/support/predicates_integration.rb +0 -7
- data/spec/unit/input_processor_compiler/json_spec.rb +0 -283
- data/spec/unit/input_processor_compiler/params_spec.rb +0 -328
- data/spec/unit/message_compiler/visit_failure_spec.rb +0 -38
- data/spec/unit/message_compiler/visit_spec.rb +0 -16
- data/spec/unit/message_compiler_spec.rb +0 -7
- data/spec/unit/predicate_registry_spec.rb +0 -34
- data/spec/unit/schema/key_spec.rb +0 -38
- data/spec/unit/schema/rule_spec.rb +0 -42
- data/spec/unit/schema/value_spec.rb +0 -131
- data/spec/unit/schema_spec.rb +0 -35
@@ -1,82 +1,224 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
require "dry/core/equalizer"
|
5
|
+
|
6
|
+
require "dry/validation/constants"
|
7
|
+
require "dry/validation/message_set"
|
8
|
+
require "dry/validation/values"
|
2
9
|
|
3
10
|
module Dry
|
4
11
|
module Validation
|
12
|
+
# Result objects are returned by contracts
|
13
|
+
#
|
14
|
+
# @api public
|
5
15
|
class Result
|
6
|
-
include Dry::Equalizer(:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
include Dry::Equalizer(:schema_result, :context, :errors, inspect: false)
|
17
|
+
|
18
|
+
# Build a new result
|
19
|
+
#
|
20
|
+
# @param [Dry::Schema::Result] schema_result
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def self.new(schema_result, context = ::Concurrent::Map.new, options = EMPTY_HASH)
|
24
|
+
result = super
|
25
|
+
yield(result) if block_given?
|
26
|
+
result.freeze
|
27
|
+
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
29
|
+
# Context that's shared between rules
|
30
|
+
#
|
31
|
+
# @return [Concurrent::Map]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
attr_reader :context
|
35
|
+
|
36
|
+
# Result from contract's schema
|
37
|
+
#
|
38
|
+
# @return [Dry::Schema::Result]
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
attr_reader :schema_result
|
42
|
+
|
43
|
+
# Result options
|
44
|
+
#
|
45
|
+
# @return [Hash]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
attr_reader :options
|
49
|
+
|
50
|
+
# Initialize a new result
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
def initialize(schema_result, context, options)
|
54
|
+
@schema_result = schema_result
|
55
|
+
@context = context
|
56
|
+
@options = options
|
57
|
+
@errors = initialize_errors
|
22
58
|
end
|
23
59
|
|
24
|
-
|
25
|
-
|
60
|
+
# Return values wrapper with the input processed by schema
|
61
|
+
#
|
62
|
+
# @return [Values]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def values
|
66
|
+
@values ||= Values.new(schema_result.to_h)
|
26
67
|
end
|
27
68
|
|
28
|
-
|
29
|
-
|
69
|
+
# Get error set
|
70
|
+
#
|
71
|
+
# @!macro errors-options
|
72
|
+
# @param [Hash] new_options
|
73
|
+
# @option new_options [Symbol] :locale Set locale for messages
|
74
|
+
# @option new_options [Boolean] :hints Enable/disable hints
|
75
|
+
# @option new_options [Boolean] :full Get messages that include key names
|
76
|
+
#
|
77
|
+
# @return [MessageSet]
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
def errors(new_options = EMPTY_HASH)
|
81
|
+
new_options.empty? ? @errors : @errors.with(schema_errors(new_options), new_options)
|
30
82
|
end
|
31
83
|
|
84
|
+
# Check if result is successful
|
85
|
+
#
|
86
|
+
# @return [Bool]
|
87
|
+
#
|
88
|
+
# @api public
|
32
89
|
def success?
|
33
|
-
|
90
|
+
@errors.empty?
|
34
91
|
end
|
35
92
|
|
93
|
+
# Check if result is not successful
|
94
|
+
#
|
95
|
+
# @return [Bool]
|
96
|
+
#
|
97
|
+
# @api public
|
36
98
|
def failure?
|
37
99
|
!success?
|
38
100
|
end
|
39
101
|
|
40
|
-
|
41
|
-
|
102
|
+
# Check if values include an error for the provided key
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def error?(key)
|
106
|
+
errors.any? { |msg| Schema::Path[msg.path].include?(Schema::Path[key]) }
|
107
|
+
end
|
108
|
+
|
109
|
+
# Check if the base schema (without rules) includes an error for the provided key
|
110
|
+
#
|
111
|
+
# @api private
|
112
|
+
def schema_error?(key)
|
113
|
+
schema_result.error?(key)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Check if the rules includes an error for the provided key
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
def rule_error?(key)
|
120
|
+
!schema_error?(key) && error?(key)
|
42
121
|
end
|
43
122
|
|
44
|
-
|
45
|
-
|
123
|
+
# Check if there's any error for the provided key
|
124
|
+
#
|
125
|
+
# This does not consider errors from the nested values
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
def base_error?(key)
|
129
|
+
schema_result.errors.any? { |error|
|
130
|
+
key_path = Schema::Path[key]
|
131
|
+
err_path = Schema::Path[error.path]
|
132
|
+
|
133
|
+
next unless key_path.same_root?(err_path)
|
134
|
+
|
135
|
+
key_path == err_path
|
136
|
+
}
|
46
137
|
end
|
47
138
|
|
48
|
-
|
49
|
-
|
139
|
+
# Add a new error for the provided key
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
def add_error(error)
|
143
|
+
@errors.add(error)
|
144
|
+
self
|
50
145
|
end
|
51
146
|
|
52
|
-
|
53
|
-
|
147
|
+
# Read a value under provided key
|
148
|
+
#
|
149
|
+
# @param [Symbol] key
|
150
|
+
#
|
151
|
+
# @return [Object]
|
152
|
+
#
|
153
|
+
# @api public
|
154
|
+
def [](key)
|
155
|
+
values[key]
|
54
156
|
end
|
55
157
|
|
56
|
-
|
57
|
-
|
58
|
-
|
158
|
+
# Check if a key was set
|
159
|
+
#
|
160
|
+
# @param [Symbol] key
|
161
|
+
#
|
162
|
+
# @return [Bool]
|
163
|
+
#
|
164
|
+
# @api public
|
165
|
+
def key?(key)
|
166
|
+
values.key?(key)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Coerce to a hash
|
170
|
+
#
|
171
|
+
# @api public
|
172
|
+
def to_h
|
173
|
+
values.to_h
|
174
|
+
end
|
175
|
+
|
176
|
+
# Return a string representation
|
177
|
+
#
|
178
|
+
# @api public
|
179
|
+
def inspect
|
180
|
+
if context.empty?
|
181
|
+
"#<#{self.class}#{to_h} errors=#{errors.to_h}>"
|
59
182
|
else
|
60
|
-
|
183
|
+
"#<#{self.class}#{to_h} errors=#{errors.to_h} context=#{context.each.to_h}>"
|
61
184
|
end
|
62
185
|
end
|
63
186
|
|
64
|
-
|
65
|
-
|
187
|
+
# Freeze result and its error set
|
188
|
+
#
|
189
|
+
# @api private
|
190
|
+
def freeze
|
191
|
+
values.freeze
|
192
|
+
errors.freeze
|
193
|
+
super
|
66
194
|
end
|
67
195
|
|
68
|
-
|
69
|
-
|
196
|
+
if RUBY_VERSION >= "2.7"
|
197
|
+
# Pattern matching
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
def deconstruct_keys(keys)
|
201
|
+
values.deconstruct_keys(keys)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Pattern matching
|
205
|
+
#
|
206
|
+
# @api private
|
207
|
+
def deconstruct
|
208
|
+
[values, context.each.to_h]
|
209
|
+
end
|
70
210
|
end
|
71
211
|
|
72
212
|
private
|
73
213
|
|
74
|
-
|
75
|
-
|
214
|
+
# @api private
|
215
|
+
def initialize_errors(options = self.options)
|
216
|
+
MessageSet.new(schema_errors(options), options)
|
76
217
|
end
|
77
218
|
|
78
|
-
|
79
|
-
|
219
|
+
# @api private
|
220
|
+
def schema_errors(options)
|
221
|
+
schema_result.message_set(options).to_a
|
80
222
|
end
|
81
223
|
end
|
82
224
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
|
5
|
+
require "dry/validation/constants"
|
6
|
+
require "dry/validation/function"
|
7
|
+
|
8
|
+
module Dry
|
9
|
+
module Validation
|
10
|
+
# Rules capture configuration and evaluator blocks
|
11
|
+
#
|
12
|
+
# When a rule is applied, it creates an `Evaluator` using schema result and its
|
13
|
+
# block will be evaluated in the context of the evaluator.
|
14
|
+
#
|
15
|
+
# @see Contract#rule
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
class Rule < Function
|
19
|
+
include Dry::Equalizer(:keys, :block, inspect: false)
|
20
|
+
|
21
|
+
# @!attribute [r] keys
|
22
|
+
# @return [Array<Symbol, String, Hash>]
|
23
|
+
# @api private
|
24
|
+
option :keys
|
25
|
+
|
26
|
+
# @!attribute [r] macros
|
27
|
+
# @return [Array<Symbol>]
|
28
|
+
# @api private
|
29
|
+
option :macros, default: proc { EMPTY_ARRAY.dup }
|
30
|
+
|
31
|
+
# Evaluate the rule within the provided context
|
32
|
+
#
|
33
|
+
# @param [Contract] contract
|
34
|
+
# @param [Result] result
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
def call(contract, result)
|
38
|
+
Evaluator.new(
|
39
|
+
contract,
|
40
|
+
keys: keys,
|
41
|
+
macros: macros,
|
42
|
+
block_options: block_options,
|
43
|
+
result: result,
|
44
|
+
values: result.values,
|
45
|
+
_context: result.context,
|
46
|
+
&block
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Define which macros should be executed
|
51
|
+
#
|
52
|
+
# @see Contract#rule
|
53
|
+
# @return [Rule]
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def validate(*macros, &block)
|
57
|
+
@macros = parse_macros(*macros)
|
58
|
+
@block = block if block
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Define a validation function for each element of an array
|
63
|
+
#
|
64
|
+
# The function will be applied only if schema checks passed
|
65
|
+
# for a given array item.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# rule(:nums).each do |index:|
|
69
|
+
# key([:number, index]).failure("must be greater than 0") if value < 0
|
70
|
+
# end
|
71
|
+
# rule(:nums).each(min: 3)
|
72
|
+
# rule(address: :city) do
|
73
|
+
# key.failure("oops") if value != 'Munich'
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# @return [Rule]
|
77
|
+
#
|
78
|
+
# @api public
|
79
|
+
def each(*macros, &block)
|
80
|
+
root = keys[0]
|
81
|
+
macros = parse_macros(*macros)
|
82
|
+
@keys = []
|
83
|
+
|
84
|
+
@block = proc do
|
85
|
+
unless result.base_error?(root) || !values.key?(root)
|
86
|
+
values[root].each_with_index do |_, idx|
|
87
|
+
path = [*Schema::Path[root].to_a, idx]
|
88
|
+
|
89
|
+
next if result.schema_error?(path)
|
90
|
+
|
91
|
+
evaluator = with(macros: macros, keys: [path], index: idx, &block)
|
92
|
+
|
93
|
+
failures.concat(evaluator.failures)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
@block_options = map_keywords(block) if block
|
99
|
+
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return a nice string representation
|
104
|
+
#
|
105
|
+
# @return [String]
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def inspect
|
109
|
+
%(#<#{self.class} keys=#{keys.inspect}>)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Parse function arguments into macros structure
|
113
|
+
#
|
114
|
+
# @return [Array]
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def parse_macros(*args)
|
118
|
+
args.each_with_object([]) do |spec, macros|
|
119
|
+
case spec
|
120
|
+
when Hash
|
121
|
+
add_macro_from_hash(macros, spec)
|
122
|
+
else
|
123
|
+
macros << Array(spec)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def add_macro_from_hash(macros, spec)
|
129
|
+
spec.each do |k, v|
|
130
|
+
macros << [k, v.is_a?(Array) ? v : [v]]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/schema/path"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Schema
|
7
|
+
class Path
|
8
|
+
# @api private
|
9
|
+
def multi_value?
|
10
|
+
last.is_a?(Array)
|
11
|
+
end
|
12
|
+
|
13
|
+
# @api private
|
14
|
+
def expand
|
15
|
+
to_a[0..-2].product(last).map { |spec| self.class[spec] }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/schema/path"
|
5
|
+
require "dry/validation/constants"
|
6
|
+
|
7
|
+
module Dry
|
8
|
+
module Validation
|
9
|
+
# A convenient wrapper for data processed by schemas
|
10
|
+
#
|
11
|
+
# Values are available within the rule blocks. They act as hash-like
|
12
|
+
# objects and expose a convenient API for accessing data.
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
class Values
|
16
|
+
include Enumerable
|
17
|
+
include Dry::Equalizer(:data)
|
18
|
+
|
19
|
+
# Schema's result output
|
20
|
+
#
|
21
|
+
# @return [Hash]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
attr_reader :data
|
25
|
+
|
26
|
+
# @api private
|
27
|
+
def initialize(data)
|
28
|
+
@data = data
|
29
|
+
end
|
30
|
+
|
31
|
+
# Read from the provided key
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
# rule(:age) do
|
35
|
+
# key.failure('must be > 18') if values[:age] <= 18
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @param args [Symbol, String, Hash, Array<Symbol>] If given as a single
|
39
|
+
# Symbol, String, Array or Hash, build a key array using
|
40
|
+
# {Dry::Schema::Path} digging for data. If given as positional
|
41
|
+
# arguments, use these with Hash#dig on the data directly.
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def [](*args)
|
47
|
+
return data.dig(*args) if args.size > 1
|
48
|
+
|
49
|
+
case (key = args[0])
|
50
|
+
when Symbol, String, Array, Hash
|
51
|
+
keys = Schema::Path[key].to_a
|
52
|
+
|
53
|
+
return data.dig(*keys) unless keys.last.is_a?(Array)
|
54
|
+
|
55
|
+
last = keys.pop
|
56
|
+
vals = self.class.new(data.dig(*keys))
|
57
|
+
vals.fetch_values(*last) { nil }
|
58
|
+
else
|
59
|
+
raise ArgumentError, "+key+ must be a valid path specification"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @api public
|
64
|
+
def key?(key, hash = data)
|
65
|
+
return hash.key?(key) if key.is_a?(Symbol)
|
66
|
+
|
67
|
+
Schema::Path[key].reduce(hash) do |a, e|
|
68
|
+
if e.is_a?(Array)
|
69
|
+
result = e.all? { |k| key?(k, a) }
|
70
|
+
return result
|
71
|
+
elsif e.is_a?(Symbol) && a.is_a?(Array)
|
72
|
+
return false
|
73
|
+
elsif a.nil?
|
74
|
+
return false
|
75
|
+
elsif a.is_a?(String)
|
76
|
+
return false
|
77
|
+
else
|
78
|
+
return false unless a.is_a?(Array) ? (e >= 0 && e < a.size) : a.key?(e)
|
79
|
+
end
|
80
|
+
a[e]
|
81
|
+
end
|
82
|
+
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api private
|
87
|
+
def respond_to_missing?(meth, include_private = false)
|
88
|
+
super || data.respond_to?(meth, include_private)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# @api private
|
94
|
+
def method_missing(meth, *args, &block)
|
95
|
+
if data.respond_to?(meth)
|
96
|
+
data.public_send(meth, *args, &block)
|
97
|
+
else
|
98
|
+
super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/dry/validation.rb
CHANGED
@@ -1,43 +1,60 @@
|
|
1
|
-
|
2
|
-
require 'dry/core/constants'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
require "dry/validation/constants"
|
4
|
+
require "dry/validation/contract"
|
5
|
+
require "dry/validation/macros"
|
6
|
+
|
7
|
+
# Main namespace
|
8
|
+
#
|
9
|
+
# @api public
|
4
10
|
module Dry
|
11
|
+
# Main library namespace
|
12
|
+
#
|
13
|
+
# @api public
|
5
14
|
module Validation
|
6
15
|
extend Dry::Core::Extensions
|
7
|
-
|
8
|
-
|
9
|
-
MissingMessageError = Class.new(StandardError)
|
10
|
-
InvalidSchemaError = Class.new(StandardError)
|
16
|
+
extend Macros::Registrar
|
11
17
|
|
12
|
-
|
13
|
-
|
18
|
+
register_extension(:monads) do
|
19
|
+
require "dry/validation/extensions/monads"
|
14
20
|
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
22
|
+
register_extension(:hints) do
|
23
|
+
require "dry/validation/extensions/hints"
|
24
|
+
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
klass.new
|
24
|
-
end
|
26
|
+
register_extension(:predicates_as_macros) do
|
27
|
+
require "dry/validation/extensions/predicates_as_macros"
|
25
28
|
end
|
26
29
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
# Define a contract and build its instance
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# my_contract = Dry::Validation.Contract do
|
34
|
+
# params do
|
35
|
+
# required(:name).filled(:string)
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# my_contract.call(name: "Jane")
|
40
|
+
#
|
41
|
+
# @param [Hash] options Contract options
|
42
|
+
#
|
43
|
+
# @see Contract
|
44
|
+
#
|
45
|
+
# @return [Contract]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
#
|
49
|
+
def self.Contract(options = EMPTY_HASH, &block)
|
50
|
+
Contract.build(options, &block)
|
30
51
|
end
|
31
52
|
|
32
|
-
|
33
|
-
|
34
|
-
|
53
|
+
# This is needed by Macros::Registrar
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def self.macros
|
57
|
+
Macros
|
35
58
|
end
|
36
59
|
end
|
37
60
|
end
|
38
|
-
|
39
|
-
require 'dry/validation/schema'
|
40
|
-
require 'dry/validation/schema/params'
|
41
|
-
require 'dry/validation/schema/json'
|
42
|
-
require 'dry/validation/extensions'
|
43
|
-
require 'dry/validation/version'
|
data/lib/dry-validation.rb
CHANGED