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.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +506 -95
  3. data/LICENSE +1 -1
  4. data/README.md +16 -12
  5. data/config/errors.yml +3 -88
  6. data/dry-validation.gemspec +37 -24
  7. data/lib/dry/validation/config.rb +24 -0
  8. data/lib/dry/validation/constants.rb +43 -0
  9. data/lib/dry/validation/contract/class_interface.rb +230 -0
  10. data/lib/dry/validation/contract.rb +171 -0
  11. data/lib/dry/validation/evaluator.rb +224 -0
  12. data/lib/dry/validation/extensions/hints.rb +67 -0
  13. data/lib/dry/validation/extensions/monads.rb +24 -8
  14. data/lib/dry/validation/extensions/predicates_as_macros.rb +75 -0
  15. data/lib/dry/validation/failures.rb +70 -0
  16. data/lib/dry/validation/function.rb +44 -0
  17. data/lib/dry/validation/macro.rb +38 -0
  18. data/lib/dry/validation/macros.rb +104 -0
  19. data/lib/dry/validation/message.rb +80 -80
  20. data/lib/dry/validation/message_set.rb +80 -105
  21. data/lib/dry/validation/messages/resolver.rb +131 -0
  22. data/lib/dry/validation/result.rb +183 -41
  23. data/lib/dry/validation/rule.rb +135 -0
  24. data/lib/dry/validation/schema_ext.rb +19 -0
  25. data/lib/dry/validation/values.rb +104 -0
  26. data/lib/dry/validation/version.rb +3 -1
  27. data/lib/dry/validation.rb +45 -28
  28. data/lib/dry-validation.rb +3 -1
  29. metadata +46 -344
  30. data/.codeclimate.yml +0 -17
  31. data/.gitignore +0 -9
  32. data/.rspec +0 -3
  33. data/.travis.yml +0 -29
  34. data/CONTRIBUTING.md +0 -31
  35. data/Gemfile +0 -25
  36. data/Rakefile +0 -22
  37. data/benchmarks/benchmark_form_invalid.rb +0 -64
  38. data/benchmarks/benchmark_form_valid.rb +0 -64
  39. data/benchmarks/benchmark_schema_invalid_huge.rb +0 -52
  40. data/benchmarks/profile_schema_call_invalid.rb +0 -20
  41. data/benchmarks/profile_schema_call_valid.rb +0 -20
  42. data/benchmarks/profile_schema_definition.rb +0 -14
  43. data/benchmarks/profile_schema_huge_invalid.rb +0 -30
  44. data/benchmarks/profile_schema_messages_invalid.rb +0 -20
  45. data/benchmarks/suite.rb +0 -5
  46. data/examples/basic.rb +0 -15
  47. data/examples/each.rb +0 -14
  48. data/examples/json.rb +0 -12
  49. data/examples/multiple.rb +0 -27
  50. data/examples/nested.rb +0 -22
  51. data/examples/params.rb +0 -11
  52. data/lib/dry/validation/compat/form.rb +0 -67
  53. data/lib/dry/validation/deprecations.rb +0 -24
  54. data/lib/dry/validation/executor.rb +0 -91
  55. data/lib/dry/validation/extensions/struct.rb +0 -32
  56. data/lib/dry/validation/extensions.rb +0 -7
  57. data/lib/dry/validation/input_processor_compiler/json.rb +0 -45
  58. data/lib/dry/validation/input_processor_compiler/params.rb +0 -49
  59. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +0 -47
  60. data/lib/dry/validation/input_processor_compiler.rb +0 -137
  61. data/lib/dry/validation/message_compiler/visitor_opts.rb +0 -37
  62. data/lib/dry/validation/message_compiler.rb +0 -188
  63. data/lib/dry/validation/messages/abstract.rb +0 -119
  64. data/lib/dry/validation/messages/i18n.rb +0 -47
  65. data/lib/dry/validation/messages/namespaced.rb +0 -39
  66. data/lib/dry/validation/messages/yaml.rb +0 -61
  67. data/lib/dry/validation/messages.rb +0 -14
  68. data/lib/dry/validation/predicate_registry.rb +0 -115
  69. data/lib/dry/validation/predicates.rb +0 -19
  70. data/lib/dry/validation/schema/check.rb +0 -37
  71. data/lib/dry/validation/schema/class_interface.rb +0 -190
  72. data/lib/dry/validation/schema/deprecated.rb +0 -30
  73. data/lib/dry/validation/schema/dsl.rb +0 -118
  74. data/lib/dry/validation/schema/form.rb +0 -9
  75. data/lib/dry/validation/schema/json.rb +0 -21
  76. data/lib/dry/validation/schema/key.rb +0 -71
  77. data/lib/dry/validation/schema/params.rb +0 -22
  78. data/lib/dry/validation/schema/rule.rb +0 -202
  79. data/lib/dry/validation/schema/value.rb +0 -211
  80. data/lib/dry/validation/schema.rb +0 -126
  81. data/lib/dry/validation/schema_compiler.rb +0 -81
  82. data/lib/dry/validation/template.rb +0 -66
  83. data/lib/dry/validation/type_specs.rb +0 -70
  84. data/log/.gitkeep +0 -0
  85. data/spec/extensions/monads/result_spec.rb +0 -40
  86. data/spec/extensions/struct/schema_spec.rb +0 -32
  87. data/spec/fixtures/locales/en.yml +0 -8
  88. data/spec/fixtures/locales/pl.yml +0 -22
  89. data/spec/integration/custom_error_messages_spec.rb +0 -54
  90. data/spec/integration/custom_predicates_spec.rb +0 -228
  91. data/spec/integration/hints_spec.rb +0 -170
  92. data/spec/integration/injecting_rules_spec.rb +0 -30
  93. data/spec/integration/json/defining_base_schema_spec.rb +0 -41
  94. data/spec/integration/localized_error_messages_spec.rb +0 -72
  95. data/spec/integration/message_compiler_spec.rb +0 -405
  96. data/spec/integration/messages/i18n_spec.rb +0 -104
  97. data/spec/integration/optional_keys_spec.rb +0 -28
  98. data/spec/integration/params/predicates/array_spec.rb +0 -287
  99. data/spec/integration/params/predicates/empty_spec.rb +0 -263
  100. data/spec/integration/params/predicates/eql_spec.rb +0 -327
  101. data/spec/integration/params/predicates/even_spec.rb +0 -455
  102. data/spec/integration/params/predicates/excluded_from_spec.rb +0 -455
  103. data/spec/integration/params/predicates/excludes_spec.rb +0 -391
  104. data/spec/integration/params/predicates/false_spec.rb +0 -455
  105. data/spec/integration/params/predicates/filled_spec.rb +0 -467
  106. data/spec/integration/params/predicates/format_spec.rb +0 -454
  107. data/spec/integration/params/predicates/gt_spec.rb +0 -519
  108. data/spec/integration/params/predicates/gteq_spec.rb +0 -519
  109. data/spec/integration/params/predicates/included_in_spec.rb +0 -455
  110. data/spec/integration/params/predicates/includes_spec.rb +0 -391
  111. data/spec/integration/params/predicates/key_spec.rb +0 -67
  112. data/spec/integration/params/predicates/lt_spec.rb +0 -519
  113. data/spec/integration/params/predicates/lteq_spec.rb +0 -519
  114. data/spec/integration/params/predicates/max_size_spec.rb +0 -391
  115. data/spec/integration/params/predicates/min_size_spec.rb +0 -391
  116. data/spec/integration/params/predicates/none_spec.rb +0 -265
  117. data/spec/integration/params/predicates/not_eql_spec.rb +0 -327
  118. data/spec/integration/params/predicates/odd_spec.rb +0 -455
  119. data/spec/integration/params/predicates/size/fixed_spec.rb +0 -393
  120. data/spec/integration/params/predicates/size/range_spec.rb +0 -396
  121. data/spec/integration/params/predicates/true_spec.rb +0 -455
  122. data/spec/integration/params/predicates/type_spec.rb +0 -391
  123. data/spec/integration/result_spec.rb +0 -81
  124. data/spec/integration/schema/array_schema_spec.rb +0 -59
  125. data/spec/integration/schema/check_rules_spec.rb +0 -119
  126. data/spec/integration/schema/check_with_nested_el_spec.rb +0 -37
  127. data/spec/integration/schema/check_with_nth_el_spec.rb +0 -25
  128. data/spec/integration/schema/default_settings_spec.rb +0 -11
  129. data/spec/integration/schema/defining_base_schema_spec.rb +0 -41
  130. data/spec/integration/schema/dynamic_predicate_args_spec.rb +0 -43
  131. data/spec/integration/schema/each_with_set_spec.rb +0 -70
  132. data/spec/integration/schema/extending_dsl_spec.rb +0 -27
  133. data/spec/integration/schema/form_spec.rb +0 -236
  134. data/spec/integration/schema/hash_schema_spec.rb +0 -47
  135. data/spec/integration/schema/inheriting_schema_spec.rb +0 -31
  136. data/spec/integration/schema/input_processor_spec.rb +0 -46
  137. data/spec/integration/schema/json/explicit_types_spec.rb +0 -157
  138. data/spec/integration/schema/json_spec.rb +0 -163
  139. data/spec/integration/schema/macros/confirmation_spec.rb +0 -35
  140. data/spec/integration/schema/macros/each_spec.rb +0 -268
  141. data/spec/integration/schema/macros/filled_spec.rb +0 -87
  142. data/spec/integration/schema/macros/input_spec.rb +0 -139
  143. data/spec/integration/schema/macros/maybe_spec.rb +0 -99
  144. data/spec/integration/schema/macros/rule_spec.rb +0 -75
  145. data/spec/integration/schema/macros/value_spec.rb +0 -119
  146. data/spec/integration/schema/macros/when_spec.rb +0 -62
  147. data/spec/integration/schema/nested_schemas_spec.rb +0 -236
  148. data/spec/integration/schema/nested_values_spec.rb +0 -46
  149. data/spec/integration/schema/not_spec.rb +0 -34
  150. data/spec/integration/schema/numbers_spec.rb +0 -19
  151. data/spec/integration/schema/option_with_default_spec.rb +0 -64
  152. data/spec/integration/schema/or_spec.rb +0 -87
  153. data/spec/integration/schema/params/defining_base_schema_spec.rb +0 -41
  154. data/spec/integration/schema/params/explicit_types_spec.rb +0 -195
  155. data/spec/integration/schema/params_spec.rb +0 -234
  156. data/spec/integration/schema/predicate_verification_spec.rb +0 -9
  157. data/spec/integration/schema/predicates/array_spec.rb +0 -295
  158. data/spec/integration/schema/predicates/custom_spec.rb +0 -103
  159. data/spec/integration/schema/predicates/empty_spec.rb +0 -263
  160. data/spec/integration/schema/predicates/eql_spec.rb +0 -327
  161. data/spec/integration/schema/predicates/even_spec.rb +0 -455
  162. data/spec/integration/schema/predicates/excluded_from/array_spec.rb +0 -459
  163. data/spec/integration/schema/predicates/excluded_from/range_spec.rb +0 -459
  164. data/spec/integration/schema/predicates/excludes_spec.rb +0 -391
  165. data/spec/integration/schema/predicates/filled_spec.rb +0 -467
  166. data/spec/integration/schema/predicates/format_spec.rb +0 -455
  167. data/spec/integration/schema/predicates/gt_spec.rb +0 -519
  168. data/spec/integration/schema/predicates/gteq_spec.rb +0 -519
  169. data/spec/integration/schema/predicates/hash_spec.rb +0 -69
  170. data/spec/integration/schema/predicates/included_in/array_spec.rb +0 -459
  171. data/spec/integration/schema/predicates/included_in/range_spec.rb +0 -459
  172. data/spec/integration/schema/predicates/includes_spec.rb +0 -391
  173. data/spec/integration/schema/predicates/key_spec.rb +0 -88
  174. data/spec/integration/schema/predicates/lt_spec.rb +0 -520
  175. data/spec/integration/schema/predicates/lteq_spec.rb +0 -519
  176. data/spec/integration/schema/predicates/max_size_spec.rb +0 -391
  177. data/spec/integration/schema/predicates/min_size_spec.rb +0 -391
  178. data/spec/integration/schema/predicates/none_spec.rb +0 -265
  179. data/spec/integration/schema/predicates/not_eql_spec.rb +0 -391
  180. data/spec/integration/schema/predicates/odd_spec.rb +0 -455
  181. data/spec/integration/schema/predicates/size/fixed_spec.rb +0 -398
  182. data/spec/integration/schema/predicates/size/range_spec.rb +0 -395
  183. data/spec/integration/schema/predicates/type_spec.rb +0 -413
  184. data/spec/integration/schema/reusing_schema_spec.rb +0 -33
  185. data/spec/integration/schema/using_types_spec.rb +0 -135
  186. data/spec/integration/schema/validate_spec.rb +0 -120
  187. data/spec/integration/schema/xor_spec.rb +0 -35
  188. data/spec/integration/schema_builders_spec.rb +0 -17
  189. data/spec/integration/schema_spec.rb +0 -173
  190. data/spec/shared/message_compiler.rb +0 -11
  191. data/spec/shared/predicate_helper.rb +0 -15
  192. data/spec/shared/rule_compiler.rb +0 -8
  193. data/spec/spec_helper.rb +0 -62
  194. data/spec/support/define_struct.rb +0 -25
  195. data/spec/support/matchers.rb +0 -38
  196. data/spec/support/mutant.rb +0 -9
  197. data/spec/support/predicates_integration.rb +0 -7
  198. data/spec/unit/input_processor_compiler/json_spec.rb +0 -283
  199. data/spec/unit/input_processor_compiler/params_spec.rb +0 -328
  200. data/spec/unit/message_compiler/visit_failure_spec.rb +0 -38
  201. data/spec/unit/message_compiler/visit_spec.rb +0 -16
  202. data/spec/unit/message_compiler_spec.rb +0 -7
  203. data/spec/unit/predicate_registry_spec.rb +0 -34
  204. data/spec/unit/schema/key_spec.rb +0 -38
  205. data/spec/unit/schema/rule_spec.rb +0 -42
  206. data/spec/unit/schema/value_spec.rb +0 -131
  207. data/spec/unit/schema_spec.rb +0 -35
@@ -1,49 +0,0 @@
1
- module Dry
2
- module Validation
3
- class InputProcessorCompiler::Params < InputProcessorCompiler
4
- PREDICATE_MAP = {
5
- default: 'string',
6
- none?: 'params.nil',
7
- bool?: 'params.bool',
8
- true?: 'params.true',
9
- false?: 'params.false',
10
- str?: 'string',
11
- int?: 'params.integer',
12
- float?: 'params.float',
13
- decimal?: 'params.decimal',
14
- date?: 'params.date',
15
- date_time?: 'params.date_time',
16
- time?: 'params.time',
17
- hash?: 'params.hash',
18
- array?: 'params.array'
19
- }.freeze
20
-
21
- CONST_MAP = {
22
- NilClass => 'params.nil',
23
- String => 'string',
24
- Integer => 'params.integer',
25
- Float => 'params.float',
26
- BigDecimal => 'params.decimal',
27
- Array => 'params.array',
28
- Hash => 'params.hash',
29
- Date => 'params.date',
30
- DateTime => 'params.date_time',
31
- Time => 'params.time',
32
- TrueClass => 'params.true',
33
- FalseClass => 'params.false'
34
- }.freeze
35
-
36
- def identifier
37
- :params
38
- end
39
-
40
- def hash_node(schema)
41
- [:params_hash, [schema, {}]]
42
- end
43
-
44
- def array_node(members)
45
- [:params_array, [members, {}]]
46
- end
47
- end
48
- end
49
- end
@@ -1,47 +0,0 @@
1
- module Dry
2
- module Validation
3
- class InputProcessorCompiler::Sanitizer < InputProcessorCompiler
4
- PREDICATE_MAP = {
5
- default: 'string',
6
- none?: 'nil',
7
- bool?: 'bool',
8
- str?: 'string',
9
- int?: 'integer',
10
- float?: 'float',
11
- decimal?: 'decimal',
12
- date?: 'date',
13
- date_time?: 'date_time',
14
- time?: 'time',
15
- hash?: 'hash',
16
- array?: 'array'
17
- }.freeze
18
-
19
- CONST_MAP = {
20
- NilClass => 'nil',
21
- String => 'string',
22
- Integer => 'integer',
23
- Float => 'float',
24
- BigDecimal => 'decimal',
25
- Array => 'array',
26
- Hash => 'hash',
27
- Date => 'date',
28
- DateTime => 'date_time',
29
- Time => 'time',
30
- TrueClass => 'true',
31
- FalseClass => 'false'
32
- }.freeze
33
-
34
- def identifier
35
- :sanitizer
36
- end
37
-
38
- def hash_node(schema)
39
- [:hash, [:weak, schema, {}]]
40
- end
41
-
42
- def array_node(members)
43
- [:array, [members, {}]]
44
- end
45
- end
46
- end
47
- end
@@ -1,137 +0,0 @@
1
- require 'dry/types'
2
- require 'dry/types/compiler'
3
-
4
- module Dry
5
- module Validation
6
- class InputProcessorCompiler
7
- attr_reader :type_compiler
8
-
9
- DEFAULT_TYPE_NODE = [:definition, [String, {}]].freeze
10
-
11
- def initialize
12
- @type_compiler = Dry::Types::Compiler.new(Dry::Types)
13
- end
14
-
15
- def call(ast)
16
- type_compiler.(hash_node(schema_ast(ast)))
17
- end
18
-
19
- def schema_ast(ast)
20
- ast.map { |node| visit(node) }
21
- end
22
-
23
- def visit(node, *args)
24
- send(:"visit_#{node[0]}", node[1], *args)
25
- end
26
-
27
- def visit_rule(node, *args)
28
- _, rule = node
29
- visit(rule, *args)
30
- end
31
-
32
- def visit_type(type, *args)
33
- if type.is_a?(Types::Constructor)
34
- constructor(type)
35
- elsif type.respond_to?(:rule)
36
- visit(type.rule.to_ast, *args)
37
- end
38
- end
39
-
40
- def constructor(type)
41
- fn_id = type.__send__(:register_fn, type.fn)
42
- [:constructor, [[:definition, [type.primitive, {}]], fn_id, {}]]
43
- end
44
-
45
- def visit_schema(schema, *args)
46
- hash_node(schema.input_processor_ast(identifier))
47
- end
48
-
49
- def visit_or(node, *args)
50
- left, right = node
51
- [:sum, [visit(left, *args), visit(right, *args), {}]]
52
- end
53
-
54
- def visit_and(node, first = true)
55
- if first
56
- name, type = node.map { |n| visit(n, false) }.uniq
57
-
58
- if name.is_a?(Array)
59
- type
60
- else
61
- [:member, [name, type]]
62
- end
63
- else
64
- result = node.map { |n| visit(n, first) }.uniq
65
-
66
- if result.size == 1
67
- result.first
68
- else
69
- result.select { |r| r != self.class::DEFAULT_TYPE_NODE }.last
70
- end
71
- end
72
- end
73
-
74
- def visit_implication(node, *args)
75
- left, right = node
76
-
77
- key = visit(left)
78
-
79
- if key.is_a?(Symbol)
80
- [:member, [key, visit(right, false)]]
81
- else
82
- [:sum, [key, visit(right, false), {}]]
83
- end
84
- end
85
-
86
- def visit_not(node, *args)
87
- visit(node, *args)
88
- end
89
-
90
- def visit_key(node, *args)
91
- _, other = node
92
- visit(other, *args)
93
- end
94
-
95
- def visit_val(node, *args)
96
- visit(node, *args)
97
- end
98
-
99
- def visit_set(node, *)
100
- hash_node(node.map { |n| visit(n) })
101
- end
102
-
103
- def visit_each(node, *args)
104
- array_node(visit(node, *args))
105
- end
106
-
107
- def visit_predicate(node, *)
108
- id, args = node
109
-
110
- if id == :key?
111
- args[0][1]
112
- else
113
- type(id, args.map(&:last))
114
- end
115
- end
116
-
117
- def type(predicate, args)
118
- default = self.class::PREDICATE_MAP[:default]
119
-
120
- type_value = if predicate == :type?
121
- const = args[0]
122
- self.class::CONST_MAP[const] || Types.identifier(const)
123
- else
124
- self.class::PREDICATE_MAP[predicate] || default
125
- end
126
-
127
- require 'dry/types/compat/int' unless Types.container.key?(type_value)
128
-
129
- Types[type_value].to_ast
130
- end
131
- end
132
- end
133
- end
134
-
135
- require 'dry/validation/input_processor_compiler/sanitizer'
136
- require 'dry/validation/input_processor_compiler/json'
137
- require 'dry/validation/input_processor_compiler/params'
@@ -1,37 +0,0 @@
1
- module Dry
2
- module Validation
3
- class MessageCompiler
4
- class VisitorOpts < Hash
5
- def self.new
6
- opts = super
7
- opts[:path] = EMPTY_ARRAY
8
- opts[:rule] = nil
9
- opts[:message_type] = :failure
10
- opts
11
- end
12
-
13
- def path?
14
- ! path.empty?
15
- end
16
-
17
- def path
18
- self[:path]
19
- end
20
-
21
- def rule
22
- self[:rule]
23
- end
24
-
25
- def with_rule(new_rule, **other)
26
- opts = dup
27
- opts[:rule] = new_rule unless opts.rule
28
- opts.(other)
29
- end
30
-
31
- def call(other)
32
- merge(other.update(path: [*path, *other[:path]]))
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,188 +0,0 @@
1
- require 'dry/validation/message'
2
- require 'dry/validation/message_set'
3
- require 'dry/validation/message_compiler/visitor_opts'
4
-
5
- module Dry
6
- module Validation
7
- class MessageCompiler
8
- attr_reader :messages, :options, :locale, :default_lookup_options
9
-
10
- EMPTY_OPTS = VisitorOpts.new
11
- LIST_SEPARATOR = ', '.freeze
12
-
13
- def initialize(messages, options = {})
14
- @messages = messages
15
- @options = options
16
- @full = @options.fetch(:full, false)
17
- @hints = @options.fetch(:hints, true)
18
- @locale = @options.fetch(:locale, messages.default_locale)
19
- @default_lookup_options = { locale: locale }
20
- end
21
-
22
- def full?
23
- @full
24
- end
25
-
26
- def hints?
27
- @hints
28
- end
29
-
30
- def with(new_options)
31
- return self if new_options.empty?
32
- self.class.new(messages, options.merge(new_options))
33
- end
34
-
35
- def call(ast)
36
- MessageSet[ast.map { |node| visit(node) }, failures: options.fetch(:failures, true)]
37
- end
38
-
39
- def visit(node, *args)
40
- __send__(:"visit_#{node[0]}", node[1], *args)
41
- end
42
-
43
- def visit_failure(node, opts = EMPTY_OPTS)
44
- rule, other = node
45
- visit(other, opts.(rule: rule))
46
- end
47
-
48
- def visit_hint(node, opts = EMPTY_OPTS)
49
- if hints?
50
- visit(node, opts.(message_type: :hint))
51
- end
52
- end
53
-
54
- def visit_each(node, opts = EMPTY_OPTS)
55
- # TODO: we can still generate a hint for elements here!
56
- []
57
- end
58
-
59
- def visit_not(node, opts = EMPTY_OPTS)
60
- visit(node, opts.(not: true))
61
- end
62
-
63
- def visit_check(node, opts = EMPTY_OPTS)
64
- keys, other = node
65
- visit(other, opts.(path: keys.last, check: true))
66
- end
67
-
68
- def visit_rule(node, opts = EMPTY_OPTS)
69
- name, other = node
70
- visit(other, opts.(rule: name))
71
- end
72
-
73
- def visit_schema(node, opts = EMPTY_OPTS)
74
- node.rule_ast.map { |rule| visit(rule, opts) }
75
- end
76
-
77
- def visit_and(node, opts = EMPTY_OPTS)
78
- left, right = node.map { |n| visit(n, opts) }
79
-
80
- if right
81
- [left, right]
82
- else
83
- left
84
- end
85
- end
86
-
87
- def visit_or(node, opts = EMPTY_OPTS)
88
- left, right = node.map { |n| visit(n, opts) }
89
-
90
- if [left, right].flatten.map(&:path).uniq.size == 1
91
- Message::Or.new(left, right, -> k { messages[k, default_lookup_options] })
92
- elsif right.is_a?(Array)
93
- right
94
- else
95
- [left, right]
96
- end
97
- end
98
-
99
- def visit_predicate(node, base_opts = EMPTY_OPTS)
100
- predicate, args = node
101
-
102
- *arg_vals, val = args.map(&:last)
103
- tokens = message_tokens(args)
104
-
105
- input = val != Undefined ? val : nil
106
-
107
- options = base_opts.update(lookup_options(arg_vals: arg_vals, input: input))
108
- msg_opts = options.update(tokens)
109
-
110
- rule = msg_opts[:rule]
111
- path = msg_opts[:path]
112
-
113
- template = messages[rule] || messages[predicate, msg_opts]
114
-
115
- unless template
116
- raise MissingMessageError, "message for #{predicate} was not found"
117
- end
118
-
119
- text = message_text(rule, template, tokens, options)
120
-
121
- message_class = options[:message_type] == :hint ? Hint : Message
122
-
123
- message_class[
124
- predicate, path, text,
125
- args: arg_vals,
126
- input: input,
127
- rule: rule,
128
- check: base_opts[:check]
129
- ]
130
- end
131
-
132
- def visit_key(node, opts = EMPTY_OPTS)
133
- name, other = node
134
- visit(other, opts.(path: name))
135
- end
136
-
137
- def visit_set(node, opts = EMPTY_OPTS)
138
- node.map { |el| visit(el, opts) }
139
- end
140
-
141
- def visit_implication(node, *args)
142
- _, right = node
143
- visit(right, *args)
144
- end
145
-
146
- def visit_xor(node, opts = EMPTY_OPTS)
147
- left, right = node
148
- [visit(left, opts), visit(right, opts)].uniq
149
- end
150
-
151
- def visit_type(node, opts = EMPTY_OPTS)
152
- visit(node.rule.to_ast, opts)
153
- end
154
-
155
- def lookup_options(arg_vals: [], input: nil)
156
- default_lookup_options.merge(
157
- arg_type: arg_vals.size == 1 && arg_vals[0].class,
158
- val_type: input.class
159
- )
160
- end
161
-
162
- def message_text(rule, template, tokens, opts)
163
- text = template[template.data(tokens)]
164
-
165
- if full?
166
- rule_name = messages.rule(rule, opts) || rule
167
- "#{rule_name} #{text}"
168
- else
169
- text
170
- end
171
- end
172
-
173
- def message_tokens(args)
174
- args.each_with_object({}) { |arg, hash|
175
- case arg[1]
176
- when Array
177
- hash[arg[0]] = arg[1].join(LIST_SEPARATOR)
178
- when Range
179
- hash["#{arg[0]}_left".to_sym] = arg[1].first
180
- hash["#{arg[0]}_right".to_sym] = arg[1].last
181
- else
182
- hash[arg[0]] = arg[1]
183
- end
184
- }
185
- end
186
- end
187
- end
188
- end
@@ -1,119 +0,0 @@
1
- require 'pathname'
2
- require 'concurrent/map'
3
-
4
- require 'dry/equalizer'
5
- require 'dry/configurable'
6
-
7
- require 'dry/validation/template'
8
-
9
- module Dry
10
- module Validation
11
- module Messages
12
- class Abstract
13
- extend Dry::Configurable
14
- include Dry::Equalizer(:config)
15
-
16
- DEFAULT_PATH = Pathname(__dir__).join('../../../../config/errors.yml').realpath.freeze
17
-
18
- setting :paths, [DEFAULT_PATH]
19
- setting :root, 'errors'.freeze
20
- setting :lookup_options, [:root, :predicate, :rule, :val_type, :arg_type].freeze
21
-
22
- setting :lookup_paths, %w(
23
- %{root}.rules.%{rule}.%{predicate}.arg.%{arg_type}
24
- %{root}.rules.%{rule}.%{predicate}
25
- %{root}.%{predicate}.%{message_type}
26
- %{root}.%{predicate}.value.%{rule}.arg.%{arg_type}
27
- %{root}.%{predicate}.value.%{rule}
28
- %{root}.%{predicate}.value.%{val_type}.arg.%{arg_type}
29
- %{root}.%{predicate}.value.%{val_type}
30
- %{root}.%{predicate}.arg.%{arg_type}
31
- %{root}.%{predicate}
32
- ).freeze
33
-
34
- setting :arg_type_default, 'default'.freeze
35
- setting :val_type_default, 'default'.freeze
36
-
37
- setting :arg_types, Hash.new { |*| config.arg_type_default }.update(
38
- Range => 'range'
39
- )
40
-
41
- setting :val_types, Hash.new { |*| config.val_type_default }.update(
42
- Range => 'range',
43
- String => 'string'
44
- )
45
-
46
- CACHE_KEYS = %i[path message_type val_type arg_type locale].freeze
47
-
48
- def self.cache
49
- @cache ||= Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
50
- end
51
-
52
- attr_reader :config
53
-
54
- def initialize
55
- @config = self.class.config
56
- end
57
-
58
- def hash
59
- @hash ||= config.hash
60
- end
61
-
62
- def rule(name, options = {})
63
- path = "%{locale}.rules.#{name}"
64
- get(path, options) if key?(path, options)
65
- end
66
-
67
- def call(predicate, options = EMPTY_HASH)
68
- cache.fetch_or_store([predicate, options.reject { |k,| k.equal?(:input) }]) do
69
- path, opts = lookup(predicate, options)
70
- return unless path
71
- text = yield(path, opts)
72
- Template[text]
73
- end
74
- end
75
- alias_method :[], :call
76
-
77
- def lookup(predicate, options = {})
78
- tokens = options.merge(
79
- root: root,
80
- predicate: predicate,
81
- arg_type: config.arg_types[options[:arg_type]],
82
- val_type: config.val_types[options[:val_type]],
83
- message_type: options[:message_type] || :failure
84
- )
85
-
86
- tokens[:rule] = predicate unless tokens.key?(:rule)
87
-
88
- opts = options.select { |k, _| !config.lookup_options.include?(k) }
89
-
90
- path = lookup_paths(tokens).detect do |key|
91
- key?(key, opts) && get(key, opts).is_a?(String)
92
- end
93
-
94
- [path, opts]
95
- end
96
-
97
- def lookup_paths(tokens)
98
- config.lookup_paths.map { |path| path % tokens }
99
- end
100
-
101
- def namespaced(namespace)
102
- Messages::Namespaced.new(namespace, self)
103
- end
104
-
105
- def root
106
- config.root
107
- end
108
-
109
- def cache
110
- @cache ||= self.class.cache[self]
111
- end
112
-
113
- def default_locale
114
- :en
115
- end
116
- end
117
- end
118
- end
119
- end
@@ -1,47 +0,0 @@
1
- require 'i18n'
2
- require 'dry/validation/messages/abstract'
3
-
4
- module Dry
5
- module Validation
6
- class Messages::I18n < Messages::Abstract
7
- attr_reader :t
8
-
9
- ::I18n.load_path.concat(config.paths)
10
-
11
- def initialize
12
- super
13
- @t = I18n.method(:t)
14
- end
15
-
16
- def call(predicate, options = EMPTY_HASH)
17
- super do |path, opts|
18
- get(path, opts)
19
- end
20
- end
21
- alias_method :[], :call
22
-
23
- def get(key, options = {})
24
- t.(key, locale: options.fetch(:locale, default_locale)) if key
25
- end
26
-
27
- def rule(name, options = {})
28
- path = "rules.#{name}"
29
- get(path, options) if key?(path, options)
30
- end
31
-
32
- def key?(key, options)
33
- ::I18n.exists?(key, options.fetch(:locale, default_locale)) ||
34
- ::I18n.exists?(key, I18n.default_locale)
35
- end
36
-
37
- def merge(path)
38
- ::I18n.load_path << path
39
- self
40
- end
41
-
42
- def default_locale
43
- I18n.locale || I18n.default_locale || super
44
- end
45
- end
46
- end
47
- end
@@ -1,39 +0,0 @@
1
- module Dry
2
- module Validation
3
- module Messages
4
- class Namespaced < Messages::Abstract
5
- attr_reader :namespace, :messages, :root
6
-
7
- def initialize(namespace, messages)
8
- super()
9
- @namespace = namespace
10
- @messages = messages
11
- @root = messages.root
12
- end
13
-
14
- def call(predicate, options = EMPTY_HASH)
15
- super do |path, opts|
16
- messages.get(path, opts)
17
- end
18
- end
19
- alias_method :[], :call
20
-
21
- def key?(key, *args)
22
- messages.key?(key, *args)
23
- end
24
-
25
- def get(key, options = {})
26
- messages.get(key, options)
27
- end
28
-
29
- def lookup_paths(tokens)
30
- super(tokens.merge(root: "#{root}.rules.#{namespace}")) + super
31
- end
32
-
33
- def default_locale
34
- messages.default_locale
35
- end
36
- end
37
- end
38
- end
39
- end