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,98 +1,98 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
module Validation
|
5
|
-
class Message
|
6
|
-
include Dry::Equalizer(:predicate, :path, :text, :options)
|
7
|
-
|
8
|
-
attr_reader :predicate, :path, :text, :rule, :args, :options
|
9
|
-
|
10
|
-
class Or
|
11
|
-
attr_reader :left
|
12
|
-
|
13
|
-
attr_reader :right
|
3
|
+
require "dry/core/equalizer"
|
14
4
|
|
15
|
-
|
5
|
+
require "dry/schema/constants"
|
6
|
+
require "dry/schema/message"
|
16
7
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
8
|
+
module Dry
|
9
|
+
module Validation
|
10
|
+
# Message message
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
class Message < Schema::Message
|
14
|
+
include Dry::Equalizer(:text, :path, :meta)
|
15
|
+
|
16
|
+
# The error message text
|
17
|
+
#
|
18
|
+
# @return [String] text
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
attr_reader :text
|
22
|
+
|
23
|
+
# The path to the value with the error
|
24
|
+
#
|
25
|
+
# @return [Array<Symbol, Integer>]
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
attr_reader :path
|
29
|
+
|
30
|
+
# Optional hash with meta-data
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
#
|
34
|
+
# @api public
|
35
|
+
attr_reader :meta
|
36
|
+
|
37
|
+
# A localized message type
|
38
|
+
#
|
39
|
+
# Localized messsages can be translated to other languages at run-time
|
40
|
+
#
|
41
|
+
# @api public
|
42
|
+
class Localized < Message
|
43
|
+
# Evaluate message text using provided locale
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# result.errors[:email].evaluate(locale: :en, full: true)
|
47
|
+
# # "email is invalid"
|
48
|
+
#
|
49
|
+
# @param [Hash] opts
|
50
|
+
# @option opts [Symbol] :locale Which locale to use
|
51
|
+
# @option opts [Boolean] :full Whether message text should include the key name
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
def evaluate(**opts)
|
55
|
+
evaluated_text, rest = text.(**opts)
|
56
|
+
Message.new(evaluated_text, path: path, meta: rest.merge(meta))
|
36
57
|
end
|
37
58
|
end
|
38
59
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
60
|
+
# Build an error
|
61
|
+
#
|
62
|
+
# @return [Message, Message::Localized]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def self.[](text, path, meta)
|
66
|
+
klass = text.respond_to?(:call) ? Localized : Message
|
67
|
+
klass.new(text, path: path, meta: meta)
|
44
68
|
end
|
45
69
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
Message.new(predicate, path, text, options)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def initialize(predicate, path, text, options)
|
55
|
-
@predicate = predicate
|
56
|
-
@path = path
|
70
|
+
# Initialize a new error object
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def initialize(text, path:, meta: EMPTY_HASH)
|
57
74
|
@text = text
|
58
|
-
@
|
59
|
-
@
|
60
|
-
|
75
|
+
@path = Array(path)
|
76
|
+
@meta = meta
|
77
|
+
end
|
61
78
|
|
62
|
-
|
63
|
-
|
64
|
-
|
79
|
+
# Check if this is a base error not associated with any key
|
80
|
+
#
|
81
|
+
# @return [Boolean]
|
82
|
+
#
|
83
|
+
# @api public
|
84
|
+
def base?
|
85
|
+
@base ||= path.compact.empty?
|
65
86
|
end
|
66
87
|
|
88
|
+
# Dump error to a string
|
89
|
+
#
|
90
|
+
# @return [String]
|
91
|
+
#
|
92
|
+
# @api public
|
67
93
|
def to_s
|
68
94
|
text
|
69
95
|
end
|
70
|
-
|
71
|
-
def signature
|
72
|
-
@signature ||= [predicate, args, path].hash
|
73
|
-
end
|
74
|
-
|
75
|
-
def hint?
|
76
|
-
false
|
77
|
-
end
|
78
|
-
|
79
|
-
def root?
|
80
|
-
path.empty?
|
81
|
-
end
|
82
|
-
|
83
|
-
def eql?(other)
|
84
|
-
other.is_a?(String) ? text == other : super
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
class Hint < Message
|
89
|
-
def self.[](predicate, path, text, options)
|
90
|
-
Hint.new(predicate, path, text, options)
|
91
|
-
end
|
92
|
-
|
93
|
-
def hint?
|
94
|
-
true
|
95
|
-
end
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
@@ -1,121 +1,96 @@
|
|
1
|
-
|
2
|
-
module Validation
|
3
|
-
class MessageSet
|
4
|
-
include Enumerable
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
key? filled? none? bool?
|
8
|
-
str? int? float? decimal?
|
9
|
-
date? date_time? time? hash?
|
10
|
-
array? format?
|
11
|
-
).freeze
|
3
|
+
require "dry/schema/message_set"
|
12
4
|
|
13
|
-
|
14
|
-
|
15
|
-
def self.[](messages, options = EMPTY_HASH)
|
16
|
-
new(messages.flatten, options)
|
17
|
-
end
|
5
|
+
require "dry/validation/constants"
|
6
|
+
require "dry/validation/message"
|
18
7
|
|
8
|
+
module Dry
|
9
|
+
module Validation
|
10
|
+
# MessageSet is a specialized message set for handling validation messages
|
11
|
+
#
|
12
|
+
# @api public
|
13
|
+
class MessageSet < Schema::MessageSet
|
14
|
+
# Return the source set of messages used to produce final evaluated messages
|
15
|
+
#
|
16
|
+
# @return [Array<Message, Message::Localized, Schema::Message>]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
attr_reader :source_messages
|
20
|
+
|
21
|
+
# Configured locale
|
22
|
+
#
|
23
|
+
# @return [Symbol]
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
attr_reader :locale
|
27
|
+
|
28
|
+
# @api private
|
19
29
|
def initialize(messages, options = EMPTY_HASH)
|
20
|
-
@
|
21
|
-
@
|
22
|
-
|
23
|
-
@paths = failures.map(&:path).uniq
|
24
|
-
@options = options
|
25
|
-
|
26
|
-
initialize_hints!
|
27
|
-
initialize_placeholders!
|
28
|
-
end
|
29
|
-
|
30
|
-
def dump
|
31
|
-
root? ? to_a : to_h
|
32
|
-
end
|
33
|
-
|
34
|
-
def failures?
|
35
|
-
options[:failures].equal?(true)
|
36
|
-
end
|
37
|
-
|
38
|
-
def empty?
|
39
|
-
messages.empty?
|
40
|
-
end
|
41
|
-
|
42
|
-
def root?
|
43
|
-
!empty? && failures.all?(&:root?)
|
44
|
-
end
|
45
|
-
|
46
|
-
def each(&block)
|
47
|
-
return to_enum unless block
|
48
|
-
messages.each(&block)
|
49
|
-
end
|
50
|
-
|
51
|
-
def to_h
|
52
|
-
if root?
|
53
|
-
{ nil => failures.map(&:to_s) }
|
54
|
-
else
|
55
|
-
failures? ? messages_map : hints_map
|
56
|
-
end
|
57
|
-
end
|
58
|
-
alias_method :to_hash, :to_h
|
59
|
-
|
60
|
-
def to_a
|
61
|
-
to_h.values.flatten
|
62
|
-
end
|
63
|
-
|
64
|
-
private
|
65
|
-
|
66
|
-
def messages_map
|
67
|
-
failures.group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
|
68
|
-
node = path.reduce(hash) { |a, e| a[e] }
|
69
|
-
|
70
|
-
msgs.each do |msg|
|
71
|
-
node << msg
|
72
|
-
|
73
|
-
msg_hints = hint_groups[msg.path]
|
74
|
-
node.concat(msg_hints) if msg_hints
|
75
|
-
end
|
76
|
-
|
77
|
-
node.map!(&:to_s)
|
78
|
-
|
79
|
-
hash
|
80
|
-
end
|
30
|
+
@locale = options[:locale]
|
31
|
+
@source_messages = options.fetch(:source) { messages.dup }
|
32
|
+
super
|
81
33
|
end
|
82
34
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
35
|
+
# Return a new message set using updated options
|
36
|
+
#
|
37
|
+
# @return [MessageSet]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def with(other, new_options = EMPTY_HASH)
|
41
|
+
return self if new_options.empty? && other.eql?(messages)
|
42
|
+
|
43
|
+
self.class.new(
|
44
|
+
other | select { |err| err.is_a?(Message) },
|
45
|
+
options.merge(source: source_messages, **new_options)
|
46
|
+
).freeze
|
95
47
|
end
|
96
48
|
|
97
|
-
|
98
|
-
|
49
|
+
# Add a new message
|
50
|
+
#
|
51
|
+
# This is used when result is being prepared
|
52
|
+
#
|
53
|
+
# @return [MessageSet]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
def add(message)
|
57
|
+
@empty = nil
|
58
|
+
source_messages << message
|
59
|
+
messages << message
|
60
|
+
self
|
99
61
|
end
|
100
62
|
|
101
|
-
|
102
|
-
|
63
|
+
# Filter message set using provided predicates
|
64
|
+
#
|
65
|
+
# This method is open to any predicate because messages can be anything that
|
66
|
+
# implements Message API, thus they can implement whatever predicates you
|
67
|
+
# may need.
|
68
|
+
#
|
69
|
+
# @example get a list of base messages
|
70
|
+
# message_set = contract.(input).errors
|
71
|
+
# message_set.filter(:base?)
|
72
|
+
#
|
73
|
+
# @param [Array<Symbol>] predicates
|
74
|
+
#
|
75
|
+
# @return [MessageSet]
|
76
|
+
#
|
77
|
+
# @api public
|
78
|
+
def filter(*predicates)
|
79
|
+
messages = select { |msg|
|
80
|
+
predicates.all? { |predicate| msg.respond_to?(predicate) && msg.public_send(predicate) }
|
81
|
+
}
|
82
|
+
self.class.new(messages)
|
103
83
|
end
|
104
84
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
while curr_idx <= last_idx do
|
112
|
-
key = path[curr_idx]
|
113
|
-
node = (node[key] || node[key] = curr_idx < last_idx ? {} : [])
|
114
|
-
curr_idx += 1
|
115
|
-
end
|
116
|
-
|
117
|
-
hash
|
85
|
+
# @api private
|
86
|
+
def freeze
|
87
|
+
source_messages.select { |err| err.respond_to?(:evaluate) }.each do |err|
|
88
|
+
idx = messages.index(err) || source_messages.index(err)
|
89
|
+
msg = err.evaluate(locale: locale, full: options[:full])
|
90
|
+
messages[idx] = msg
|
118
91
|
end
|
92
|
+
to_h
|
93
|
+
self
|
119
94
|
end
|
120
95
|
end
|
121
96
|
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/validation/message"
|
4
|
+
require "dry/schema/message_compiler"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Validation
|
8
|
+
module Messages
|
9
|
+
FULL_MESSAGE_WHITESPACE = Dry::Schema::MessageCompiler::FULL_MESSAGE_WHITESPACE
|
10
|
+
|
11
|
+
# Resolve translated messages from failure arguments
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class Resolver
|
15
|
+
# @!attribute [r] messages
|
16
|
+
# @return [Messages::I18n, Messages::YAML] messages backend
|
17
|
+
# @api private
|
18
|
+
attr_reader :messages
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def initialize(messages)
|
22
|
+
@messages = messages
|
23
|
+
end
|
24
|
+
|
25
|
+
# Resolve Message object from provided args and path
|
26
|
+
#
|
27
|
+
# This is used internally by contracts when rules are applied
|
28
|
+
# If message argument is a Hash, then it MUST have a :text key,
|
29
|
+
# which value will be used as the message value
|
30
|
+
#
|
31
|
+
# @return [Message, Message::Localized]
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def call(message:, tokens:, path:, meta: EMPTY_HASH)
|
35
|
+
case message
|
36
|
+
when Symbol
|
37
|
+
Message[->(**opts) { message(message, path: path, tokens: tokens, **opts) }, path, meta]
|
38
|
+
when String
|
39
|
+
Message[->(**opts) { [message_text(message, path: path, **opts), meta] }, path, meta]
|
40
|
+
when Hash
|
41
|
+
meta = message.dup
|
42
|
+
text = meta.delete(:text) { |key|
|
43
|
+
raise ArgumentError, <<~STR
|
44
|
+
+message+ Hash must contain :#{key} key (#{message.inspect} given)
|
45
|
+
STR
|
46
|
+
}
|
47
|
+
|
48
|
+
call(message: text, tokens: tokens, path: path, meta: meta)
|
49
|
+
else
|
50
|
+
raise ArgumentError, <<~STR
|
51
|
+
+message+ must be either a Symbol, String or Hash (#{message.inspect} given)
|
52
|
+
STR
|
53
|
+
end
|
54
|
+
end
|
55
|
+
alias_method :[], :call
|
56
|
+
|
57
|
+
# Resolve a message
|
58
|
+
#
|
59
|
+
# @return [String]
|
60
|
+
#
|
61
|
+
# @api public
|
62
|
+
#
|
63
|
+
# rubocop:disable Metrics/AbcSize
|
64
|
+
def message(rule, tokens: EMPTY_HASH, locale: nil, full: false, path:)
|
65
|
+
keys = path.to_a.compact
|
66
|
+
msg_opts = tokens.merge(path: keys, locale: locale || messages.default_locale)
|
67
|
+
|
68
|
+
if keys.empty?
|
69
|
+
template, meta = messages["rules.#{rule}", msg_opts]
|
70
|
+
else
|
71
|
+
template, meta = messages[rule, msg_opts.merge(path: keys.join(DOT))]
|
72
|
+
template, meta = messages[rule, msg_opts.merge(path: keys.last)] unless template
|
73
|
+
end
|
74
|
+
|
75
|
+
if !template && keys.size > 1
|
76
|
+
non_index_keys = keys.reject { |k| k.is_a?(Integer) }
|
77
|
+
template, meta = messages[rule, msg_opts.merge(path: non_index_keys.join(DOT))]
|
78
|
+
end
|
79
|
+
|
80
|
+
unless template
|
81
|
+
raise MissingMessageError, <<~STR
|
82
|
+
Message template for #{rule.inspect} under #{keys.join(DOT).inspect} was not found
|
83
|
+
STR
|
84
|
+
end
|
85
|
+
|
86
|
+
parsed_tokens = parse_tokens(tokens)
|
87
|
+
text = template.(template.data(parsed_tokens))
|
88
|
+
|
89
|
+
[message_text(text, path: path, locale: locale, full: full), meta]
|
90
|
+
end
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def message_text(text, path:, locale: nil, full: false)
|
96
|
+
return text unless full
|
97
|
+
|
98
|
+
key = key_text(path: path, locale: locale)
|
99
|
+
|
100
|
+
[key, text].compact.join(FULL_MESSAGE_WHITESPACE[locale])
|
101
|
+
end
|
102
|
+
|
103
|
+
def key_text(path:, locale: nil)
|
104
|
+
locale ||= messages.default_locale
|
105
|
+
|
106
|
+
keys = path.to_a.compact
|
107
|
+
msg_opts = {path: keys, locale: locale}
|
108
|
+
|
109
|
+
messages.rule(keys.last, msg_opts) || keys.last
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_tokens(tokens)
|
113
|
+
Hash[
|
114
|
+
tokens.map do |key, token|
|
115
|
+
[key, parse_token(token)]
|
116
|
+
end
|
117
|
+
]
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse_token(token)
|
121
|
+
case token
|
122
|
+
when Array
|
123
|
+
token.join(", ")
|
124
|
+
else
|
125
|
+
token
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|