dry-validation 0.7.4 → 0.8.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +9 -6
  4. data/CHANGELOG.md +58 -0
  5. data/Gemfile +3 -3
  6. data/benchmarks/benchmark_form_invalid.rb +64 -0
  7. data/benchmarks/benchmark_form_valid.rb +64 -0
  8. data/benchmarks/profile_schema_call_invalid.rb +20 -0
  9. data/benchmarks/profile_schema_call_valid.rb +20 -0
  10. data/benchmarks/profile_schema_definition.rb +14 -0
  11. data/benchmarks/profile_schema_messages_invalid.rb +20 -0
  12. data/benchmarks/suite.rb +5 -0
  13. data/config/errors.yml +12 -5
  14. data/dry-validation.gemspec +2 -2
  15. data/examples/basic.rb +2 -2
  16. data/examples/form.rb +2 -2
  17. data/examples/json.rb +2 -2
  18. data/examples/nested.rb +6 -6
  19. data/lib/dry/validation.rb +22 -3
  20. data/lib/dry/validation/deprecations.rb +23 -0
  21. data/lib/dry/validation/error_compiler.rb +31 -11
  22. data/lib/dry/validation/error_compiler/input.rb +44 -57
  23. data/lib/dry/validation/hint_compiler.rb +15 -7
  24. data/lib/dry/validation/input_processor_compiler.rb +13 -6
  25. data/lib/dry/validation/input_processor_compiler/form.rb +2 -0
  26. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +1 -1
  27. data/lib/dry/validation/messages/abstract.rb +9 -1
  28. data/lib/dry/validation/predicate_registry.rb +101 -0
  29. data/lib/dry/validation/result.rb +8 -1
  30. data/lib/dry/validation/schema.rb +110 -44
  31. data/lib/dry/validation/schema/check.rb +4 -2
  32. data/lib/dry/validation/schema/deprecated.rb +31 -0
  33. data/lib/dry/validation/schema/dsl.rb +18 -11
  34. data/lib/dry/validation/schema/form.rb +1 -0
  35. data/lib/dry/validation/schema/json.rb +1 -0
  36. data/lib/dry/validation/schema/key.rb +23 -10
  37. data/lib/dry/validation/schema/rule.rb +81 -20
  38. data/lib/dry/validation/schema/value.rb +110 -25
  39. data/lib/dry/validation/version.rb +1 -1
  40. data/spec/fixtures/locales/en.yml +1 -0
  41. data/spec/fixtures/locales/pl.yml +1 -1
  42. data/spec/integration/custom_error_messages_spec.rb +2 -2
  43. data/spec/integration/custom_predicates_spec.rb +98 -15
  44. data/spec/integration/error_compiler_spec.rb +111 -96
  45. data/spec/integration/form/predicates/array_spec.rb +243 -0
  46. data/spec/integration/form/predicates/empty_spec.rb +263 -0
  47. data/spec/integration/form/predicates/eql_spec.rb +327 -0
  48. data/spec/integration/form/predicates/even_spec.rb +455 -0
  49. data/spec/integration/form/predicates/excluded_from_spec.rb +455 -0
  50. data/spec/integration/form/predicates/excludes_spec.rb +391 -0
  51. data/spec/integration/form/predicates/false_spec.rb +455 -0
  52. data/spec/integration/form/predicates/filled_spec.rb +467 -0
  53. data/spec/integration/form/predicates/format_spec.rb +454 -0
  54. data/spec/integration/form/predicates/gt_spec.rb +519 -0
  55. data/spec/integration/form/predicates/gteq_spec.rb +519 -0
  56. data/spec/integration/form/predicates/included_in_spec.rb +455 -0
  57. data/spec/integration/form/predicates/includes_spec.rb +391 -0
  58. data/spec/integration/form/predicates/key_spec.rb +75 -0
  59. data/spec/integration/form/predicates/lt_spec.rb +519 -0
  60. data/spec/integration/form/predicates/lteq_spec.rb +519 -0
  61. data/spec/integration/form/predicates/max_size_spec.rb +391 -0
  62. data/spec/integration/form/predicates/min_size_spec.rb +391 -0
  63. data/spec/integration/form/predicates/none_spec.rb +265 -0
  64. data/spec/integration/form/predicates/not_eql_spec.rb +327 -0
  65. data/spec/integration/form/predicates/odd_spec.rb +455 -0
  66. data/spec/integration/form/predicates/size/fixed_spec.rb +399 -0
  67. data/spec/integration/form/predicates/size/range_spec.rb +398 -0
  68. data/spec/integration/form/predicates/true_spec.rb +455 -0
  69. data/spec/integration/form/predicates/type_spec.rb +391 -0
  70. data/spec/integration/hints_spec.rb +90 -4
  71. data/spec/integration/injecting_rules_spec.rb +2 -2
  72. data/spec/integration/localized_error_messages_spec.rb +2 -2
  73. data/spec/integration/messages/i18n_spec.rb +3 -3
  74. data/spec/integration/optional_keys_spec.rb +3 -3
  75. data/spec/integration/schema/array_schema_spec.rb +49 -13
  76. data/spec/integration/schema/check_rules_spec.rb +6 -6
  77. data/spec/integration/schema/check_with_nested_el_spec.rb +3 -3
  78. data/spec/integration/schema/check_with_nth_el_spec.rb +1 -1
  79. data/spec/integration/schema/each_with_set_spec.rb +3 -3
  80. data/spec/integration/schema/extending_dsl_spec.rb +27 -0
  81. data/spec/integration/schema/form/explicit_types_spec.rb +182 -0
  82. data/spec/integration/schema/form_spec.rb +90 -18
  83. data/spec/integration/schema/hash_schema_spec.rb +47 -0
  84. data/spec/integration/schema/inheriting_schema_spec.rb +4 -4
  85. data/spec/integration/schema/input_processor_spec.rb +8 -8
  86. data/spec/integration/schema/json/explicit_types_spec.rb +157 -0
  87. data/spec/integration/schema/json_spec.rb +18 -18
  88. data/spec/integration/schema/macros/confirmation_spec.rb +1 -1
  89. data/spec/integration/schema/macros/each_spec.rb +177 -43
  90. data/spec/integration/schema/macros/{required_spec.rb → filled_spec.rb} +34 -6
  91. data/spec/integration/schema/macros/input_spec.rb +21 -0
  92. data/spec/integration/schema/macros/maybe_spec.rb +39 -2
  93. data/spec/integration/schema/macros/value_spec.rb +105 -0
  94. data/spec/integration/schema/macros/when_spec.rb +28 -8
  95. data/spec/integration/schema/nested_values_spec.rb +11 -8
  96. data/spec/integration/schema/not_spec.rb +2 -2
  97. data/spec/integration/schema/numbers_spec.rb +1 -1
  98. data/spec/integration/schema/option_with_default_spec.rb +1 -1
  99. data/spec/integration/schema/predicate_verification_spec.rb +9 -0
  100. data/spec/integration/schema/predicates/array_spec.rb +295 -0
  101. data/spec/integration/schema/predicates/custom_spec.rb +103 -0
  102. data/spec/integration/schema/predicates/empty_spec.rb +263 -0
  103. data/spec/integration/schema/predicates/eql_spec.rb +327 -0
  104. data/spec/integration/schema/predicates/even_spec.rb +455 -0
  105. data/spec/integration/schema/predicates/excluded_from_spec.rb +455 -0
  106. data/spec/integration/schema/predicates/excludes_spec.rb +391 -0
  107. data/spec/integration/schema/predicates/filled_spec.rb +467 -0
  108. data/spec/integration/schema/predicates/format_spec.rb +455 -0
  109. data/spec/integration/schema/predicates/gt_spec.rb +519 -0
  110. data/spec/integration/schema/predicates/gteq_spec.rb +519 -0
  111. data/spec/integration/schema/predicates/hash_spec.rb +69 -0
  112. data/spec/integration/schema/predicates/included_in_spec.rb +455 -0
  113. data/spec/integration/schema/predicates/includes_spec.rb +391 -0
  114. data/spec/integration/schema/predicates/key_spec.rb +88 -0
  115. data/spec/integration/schema/predicates/lt_spec.rb +520 -0
  116. data/spec/integration/schema/predicates/lteq_spec.rb +519 -0
  117. data/spec/integration/schema/predicates/max_size_spec.rb +391 -0
  118. data/spec/integration/schema/predicates/min_size_spec.rb +391 -0
  119. data/spec/integration/schema/predicates/none_spec.rb +265 -0
  120. data/spec/integration/schema/predicates/not_eql_spec.rb +391 -0
  121. data/spec/integration/schema/predicates/odd_spec.rb +455 -0
  122. data/spec/integration/schema/predicates/size/fixed_spec.rb +401 -0
  123. data/spec/integration/schema/predicates/size/range_spec.rb +399 -0
  124. data/spec/integration/schema/predicates/type_spec.rb +391 -0
  125. data/spec/integration/schema/reusing_schema_spec.rb +4 -4
  126. data/spec/integration/schema/using_types_spec.rb +24 -6
  127. data/spec/integration/schema/xor_spec.rb +2 -2
  128. data/spec/integration/schema_builders_spec.rb +15 -0
  129. data/spec/integration/schema_spec.rb +37 -12
  130. data/spec/shared/predicate_helper.rb +13 -0
  131. data/spec/spec_helper.rb +10 -0
  132. data/spec/support/matchers.rb +38 -0
  133. data/spec/support/predicates_integration.rb +7 -0
  134. data/spec/unit/hint_compiler_spec.rb +10 -8
  135. data/spec/unit/input_processor_compiler/form_spec.rb +45 -43
  136. data/spec/unit/input_processor_compiler/json_spec.rb +37 -35
  137. data/spec/unit/predicate_registry_spec.rb +34 -0
  138. data/spec/unit/schema/key_spec.rb +12 -14
  139. data/spec/unit/schema/rule_spec.rb +4 -2
  140. data/spec/unit/schema/value_spec.rb +38 -121
  141. metadata +150 -16
  142. data/lib/dry/validation/schema/attr.rb +0 -15
  143. data/spec/integration/attr_spec.rb +0 -122
@@ -24,16 +24,6 @@ RSpec.describe Dry::Validation do
24
24
  end
25
25
 
26
26
  describe 'defining schema with custom predicates container' do
27
- subject(:schema) do
28
- Dry::Validation.Schema(base_class) do
29
- configure do
30
- config.predicates = Test::Predicates
31
- end
32
-
33
- key(:email) { filled? & email? }
34
- end
35
- end
36
-
37
27
  before do
38
28
  module Test
39
29
  module Predicates
@@ -46,7 +36,40 @@ RSpec.describe Dry::Validation do
46
36
  end
47
37
  end
48
38
 
49
- include_context 'uses custom predicates'
39
+ context 'when configured globally' do
40
+ before do
41
+ Dry::Validation::Schema.predicates(Test::Predicates)
42
+ end
43
+
44
+ subject!(:schema) do
45
+ Dry::Validation.Schema(base_class) do
46
+ required(:email) { filled? & email? }
47
+ end
48
+ end
49
+
50
+ after do
51
+ # HACK: reset global predicates configuration
52
+ Dry::Validation::Schema.configure do |config|
53
+ config.predicates = Dry::Logic::Predicates
54
+ end
55
+ end
56
+
57
+ include_context 'uses custom predicates'
58
+ end
59
+
60
+ context 'when configured locally' do
61
+ subject(:schema) do
62
+ Dry::Validation.Schema(base_class) do
63
+ configure do
64
+ predicates(Test::Predicates)
65
+ end
66
+
67
+ required(:email) { filled? & email? }
68
+ end
69
+ end
70
+
71
+ include_context 'uses custom predicates'
72
+ end
50
73
  end
51
74
 
52
75
  describe 'defining schema with custom predicate methods' do
@@ -58,7 +81,7 @@ RSpec.describe Dry::Validation do
58
81
  end
59
82
  end
60
83
 
61
- key(:email) { filled? & email? }
84
+ required(:email) { filled? & email? }
62
85
  end
63
86
  end
64
87
 
@@ -68,8 +91,6 @@ RSpec.describe Dry::Validation do
68
91
  describe 'custom predicate which requires an arbitrary dependency' do
69
92
  subject(:schema) do
70
93
  Dry::Validation.Schema(base_class) do
71
- key(:email).required(:email?)
72
-
73
94
  configure do
74
95
  option :email_check
75
96
 
@@ -77,6 +98,8 @@ RSpec.describe Dry::Validation do
77
98
  email_check.(value)
78
99
  end
79
100
  end
101
+
102
+ required(:email).filled(:email?)
80
103
  end
81
104
  end
82
105
 
@@ -97,11 +120,71 @@ RSpec.describe Dry::Validation do
97
120
  end
98
121
  end
99
122
 
100
- key(:email).required(:email?)
123
+ required(:email).filled(:email?)
101
124
  end
102
125
 
103
126
  expect { schema.(email: 'foo').messages }.to raise_error(
104
127
  Dry::Validation::MissingMessageError, /email/
105
128
  )
106
129
  end
130
+
131
+ it 'should work with custom predicate args' do
132
+ schema = Dry::Validation.Schema do
133
+ configure do
134
+ def self.messages
135
+ Dry::Validation::Messages.default.merge(
136
+ en: { errors: { fav_number?: 'must be %{expected}' } }
137
+ )
138
+ end
139
+ def fav_number?(expected, current)
140
+ current == expected
141
+ end
142
+ end
143
+
144
+ required(:foo) { fav_number?(23) }
145
+ end
146
+
147
+ expect(schema.(foo: 20).messages).to eql(
148
+ foo: ['must be 23']
149
+ )
150
+
151
+ end
152
+
153
+ it 'works when no predicate args' do
154
+ schema = Dry::Validation.Schema do
155
+ configure do
156
+ def self.messages
157
+ Dry::Validation::Messages.default.merge(
158
+ en: { errors: { with_no_args?: 'is always false' } }
159
+ )
160
+ end
161
+
162
+ def with_no_args?
163
+ false
164
+ end
165
+ end
166
+
167
+ required(:email).filled(:with_no_args?)
168
+ end
169
+
170
+ expect(schema.(email: 'foo').messages).to eql(
171
+ email: ['is always false']
172
+ )
173
+ end
174
+
175
+ it 'works with nested schemas' do
176
+ schema = Dry::Validation.Schema do
177
+ configure do
178
+ def ok?(_value)
179
+ true
180
+ end
181
+ end
182
+
183
+ required(:foo).schema do
184
+ required(:bar).value(:ok?)
185
+ end
186
+ end
187
+
188
+ expect(schema.(foo: { bar: "1" })).to be_success
189
+ end
107
190
  end
@@ -4,11 +4,20 @@ require 'dry/validation/error_compiler'
4
4
  RSpec.describe Dry::Validation::ErrorCompiler do
5
5
  subject(:error_compiler) { ErrorCompiler.new(messages) }
6
6
 
7
+ include_context 'predicate helper'
8
+
7
9
  let(:messages) do
8
10
  Messages.default.merge(
9
11
  en: {
10
12
  errors: {
11
- key?: '+%{name}+ key is missing in the hash',
13
+ key?: {
14
+ arg: {
15
+ default: '+%{name}+ key is missing in the hash',
16
+ },
17
+ value: {
18
+ gender: 'Please provide your gender'
19
+ }
20
+ },
12
21
  rules: {
13
22
  address: {
14
23
  filled?: 'Please provide your address'
@@ -30,16 +39,18 @@ RSpec.describe Dry::Validation::ErrorCompiler do
30
39
  describe '#call with flat inputs' do
31
40
  let(:ast) do
32
41
  [
33
- [:error, [:name, [:input, [:name, [:result, [nil, [:val, [:predicate, [:key?, [:name]]]]]]]]]],
34
- [:error, [:age, [:input, [:age, [:result, [18, [:val, [:predicate, [:gt?, [18]]]]]]]]]],
35
- [:error, [:email, [:input, [:email, [:result, ["", [:val, [:predicate, [:filled?, []]]]]]]]]],
36
- [:error, [:address, [:input, [:address, [:result, ["", [:val, [:predicate, [:filled?, []]]]]]]]]]
42
+ [:error, [:name, [:input, [:name, [:result, [nil, [:val, p(:key?, :name)]]]]]]],
43
+ [:error, [:gender, [:input, [:gender, [:result, [nil, [:val, p(:key?, :gender)]]]]]]],
44
+ [:error, [:age, [:input, [:age, [:result, [18, [:val, p(:gt?, 18)]]]]]]],
45
+ [:error, [:email, [:input, [:email, [:result, ["", [:val, p(:filled?)]]]]]]],
46
+ [:error, [:address, [:input, [:address, [:result, ["", [:val, p(:filled?)]]]]]]]
37
47
  ]
38
48
  end
39
49
 
40
50
  it 'converts error ast into another format' do
41
51
  expect(error_compiler.(ast)).to eql(
42
52
  name: ["+name+ key is missing in the hash"],
53
+ gender: ["Please provide your gender"],
43
54
  age: ["must be greater than 18"],
44
55
  email: ["must be filled"],
45
56
  address: ["Please provide your address"]
@@ -57,8 +68,8 @@ RSpec.describe Dry::Validation::ErrorCompiler do
57
68
  :check, [
58
69
  :newsletter,
59
70
  [:implication, [
60
- [:key, [[:settings, :offers], [:predicate, [:true?, []]]]],
61
- [:key, [[:settings, :newsletter], [:predicate, [:false?, []]]]]]
71
+ [:key, [[:settings, :offers], p(:true?)]],
72
+ [:key, [[:settings, :newsletter], p(:false?)]]]
62
73
  ]
63
74
  ]
64
75
  ]
@@ -83,7 +94,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
83
94
  [{ method: "cc", amount: 1.23 }, { amount: 4.56 }], [:each, [
84
95
  [:el, [
85
96
  1, [
86
- :result, [{ amount: 4.56 }, [:val, [:predicate, [:key?, [:method]]]]]
97
+ :result, [{ amount: 4.56 }, [:val, p(:key?, :method)]]
87
98
  ]
88
99
  ]]
89
100
  ]]]
@@ -102,9 +113,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
102
113
  context 'full message' do
103
114
  it 'returns full message including rule name' do
104
115
  msg = error_compiler.with(full: true).visit(
105
- [:input, [:num, [
106
- :result, ['2', [:val, [:predicate, [:int?, []]]]]]
107
- ]]
116
+ [:input, [:num, [:result, ['2', [:val, p(:int?)]]]]]
108
117
  )
109
118
 
110
119
  expect(msg).to eql(num: ['num must be an integer'])
@@ -114,9 +123,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
114
123
  context 'rule name translations' do
115
124
  it 'translates rule name and its message' do
116
125
  msg = error_compiler.with(locale: :pl, full: true).visit(
117
- [:input, [:email, [
118
- :result, ['oops', [:val, [:predicate, [:email?, []]]]]]
119
- ]]
126
+ [:input, [:email, [:result, ['oops', [:val, p(:email?)]]]]]
120
127
  )
121
128
 
122
129
  expect(msg).to eql(email: ['adres email nie jest poprawny'])
@@ -126,43 +133,57 @@ RSpec.describe Dry::Validation::ErrorCompiler do
126
133
  describe ':empty?' do
127
134
  it 'returns valid message' do
128
135
  msg = error_compiler.visit(
129
- [:input, [:tags, [:result, [nil, [:val, [:predicate, [:empty?, []]]]]]]]
136
+ [:input, [:tags, [:result, [nil, [:val, p(:empty?)]]]]]
130
137
  )
131
138
 
132
139
  expect(msg).to eql(tags: ['must be empty'])
133
140
  end
134
141
  end
135
142
 
136
- describe ':exclusion?' do
143
+ describe ':excluded_from?' do
137
144
  it 'returns valid message' do
138
145
  msg = error_compiler.visit(
139
- [:input, [:num, [
140
- :result, [2, [:val, [:predicate, [:exclusion?, [[1, 2, 3]]]]]]]
141
- ]]
146
+ [:input, [:num, [:result, [2, [:val, p(:excluded_from?, [1, 2, 3])]]]]]
142
147
  )
143
148
 
144
149
  expect(msg).to eql(num: ['must not be one of: 1, 2, 3'])
145
150
  end
146
151
  end
147
152
 
148
- describe ':inclusion?' do
153
+ describe ':excludes?' do
149
154
  it 'returns valid message' do
150
155
  msg = error_compiler.visit(
151
- [:input, [:num, [
152
- :result, [2, [:val, [:predicate, [:inclusion?, [[1, 2, 3]]]]]]]
153
- ]]
156
+ [:input, [:array, [:result, [[1, 2, 3], [:val, p(:excludes?, 2)]]]]]
157
+ )
158
+
159
+ expect(msg).to eql(array: ['must not include 2'])
160
+ end
161
+ end
162
+
163
+ describe ':included_in?' do
164
+ it 'returns valid message' do
165
+ msg = error_compiler.visit(
166
+ [:input, [:num, [:result, [2, [:val, p(:included_in?, [1, 2, 3])]]]]]
154
167
  )
155
168
 
156
169
  expect(msg).to eql(num: ['must be one of: 1, 2, 3'])
157
170
  end
158
171
  end
159
172
 
173
+ describe ':includes?' do
174
+ it 'returns valid message' do
175
+ msg = error_compiler.visit(
176
+ [:input, [:num, [:result, [[1, 2, 3], [:val, p(:includes?, 2)]]]]]
177
+ )
178
+
179
+ expect(msg).to eql(num: ['must include 2'])
180
+ end
181
+ end
182
+
160
183
  describe ':gt?' do
161
184
  it 'returns valid message' do
162
185
  msg = error_compiler.visit(
163
- [:input, [:num, [
164
- :result, [2, [:val, [:predicate, [:gt?, [3]]]]]]
165
- ]]
186
+ [:input, [:num, [:result, [2, [:val, p(:gt?, 3)]]]]]
166
187
  )
167
188
 
168
189
  expect(msg).to eql(num: ['must be greater than 3'])
@@ -172,9 +193,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
172
193
  describe ':gteq?' do
173
194
  it 'returns valid message' do
174
195
  msg = error_compiler.visit(
175
- [:input, [:num, [
176
- :result, [2, [:val, [:predicate, [:gteq?, [3]]]]]]
177
- ]]
196
+ [:input, [:num, [:result, [2, [:val, p(:gteq?, 3)]]]]]
178
197
  )
179
198
 
180
199
  expect(msg).to eql(num: ['must be greater than or equal to 3'])
@@ -184,21 +203,17 @@ RSpec.describe Dry::Validation::ErrorCompiler do
184
203
  describe ':lt?' do
185
204
  it 'returns valid message' do
186
205
  msg = error_compiler.visit(
187
- [:input, [:num, [
188
- :result, [2, [:val, [:predicate, [:lt?, [3]]]]]]
189
- ]]
206
+ [:input, [:num, [:result, [2, [:val, p(:lt?, 3)]]]]]
190
207
  )
191
208
 
192
- expect(msg).to eql(num: ['must be less than 3 (2 was given)'])
209
+ expect(msg).to eql(num: ['must be less than 3'])
193
210
  end
194
211
  end
195
212
 
196
213
  describe ':lteq?' do
197
214
  it 'returns valid message' do
198
215
  msg = error_compiler.visit(
199
- [:input, [:num, [
200
- :result, [2, [:val, [:predicate, [:lteq?, [3]]]]]]
201
- ]]
216
+ [:input, [:num, [:result, [2, [:val, p(:lteq?, 3)]]]]]
202
217
  )
203
218
 
204
219
  expect(msg).to eql(num: ['must be less than or equal to 3'])
@@ -208,9 +223,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
208
223
  describe ':hash?' do
209
224
  it 'returns valid message' do
210
225
  msg = error_compiler.visit(
211
- [:input, [:address, [
212
- :result, ['', [:val, [:predicate, [:hash?, []]]]]]
213
- ]]
226
+ [:input, [:address, [:result, ['', [:val, p(:hash?, [])]]]]]
214
227
  )
215
228
 
216
229
  expect(msg).to eql(address: ['must be a hash'])
@@ -220,9 +233,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
220
233
  describe ':array?' do
221
234
  it 'returns valid message' do
222
235
  msg = error_compiler.visit(
223
- [:input, [:phone_numbers, [
224
- :result, ['', [:val, [:predicate, [:array?, []]]]]]
225
- ]]
236
+ [:input, [:phone_numbers, [:result, ['', [:val, p(:array?)]]]]]
226
237
  )
227
238
 
228
239
  expect(msg).to eql(phone_numbers: ['must be an array'])
@@ -232,9 +243,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
232
243
  describe ':int?' do
233
244
  it 'returns valid message' do
234
245
  msg = error_compiler.visit(
235
- [:input, [:num, [
236
- :result, ['2', [:val, [:predicate, [:int?, []]]]]]
237
- ]]
246
+ [:input, [:num, [:result, ['2', [:val, p(:int?)]]]]]
238
247
  )
239
248
 
240
249
  expect(msg).to eql(num: ['must be an integer'])
@@ -244,9 +253,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
244
253
  describe ':float?' do
245
254
  it 'returns valid message' do
246
255
  msg = error_compiler.visit(
247
- [:input, [:num, [
248
- :result, ['2', [:val, [:predicate, [:float?, []]]]]]
249
- ]]
256
+ [:input, [:num, [:result, ['2', [:val, p(:float?)]]]]]
250
257
  )
251
258
 
252
259
  expect(msg).to eql(num: ['must be a float'])
@@ -256,9 +263,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
256
263
  describe ':decimal?' do
257
264
  it 'returns valid message' do
258
265
  msg = error_compiler.visit(
259
- [:input, [:num, [
260
- :result, ['2', [:val, [:predicate, [:decimal?, []]]]]]
261
- ]]
266
+ [:input, [:num, [:result, ['2', [:val, p(:decimal?)]]]]]
262
267
  )
263
268
 
264
269
  expect(msg).to eql(num: ['must be a decimal'])
@@ -268,9 +273,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
268
273
  describe ':date?' do
269
274
  it 'returns valid message' do
270
275
  msg = error_compiler.visit(
271
- [:input, [:num, [
272
- :result, ['2', [:val, [:predicate, [:date?, []]]]]]
273
- ]]
276
+ [:input, [:num, [:result, ['2', [:val, p(:date?)]]]]]
274
277
  )
275
278
 
276
279
  expect(msg).to eql(num: ['must be a date'])
@@ -280,9 +283,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
280
283
  describe ':date_time?' do
281
284
  it 'returns valid message' do
282
285
  msg = error_compiler.visit(
283
- [:input, [:num, [
284
- :result, ['2', [:val, [:predicate, [:date_time?, []]]]]]
285
- ]]
286
+ [:input, [:num, [:result, ['2', [:val, p(:date_time?)]]]]]
286
287
  )
287
288
 
288
289
  expect(msg).to eql(num: ['must be a date time'])
@@ -292,9 +293,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
292
293
  describe ':time?' do
293
294
  it 'returns valid message' do
294
295
  msg = error_compiler.visit(
295
- [:input, [:num, [
296
- :result, ['2', [:val, [:predicate, [:time?, []]]]]]
297
- ]]
296
+ [:input, [:num, [:result, ['2', [:val, p(:time?)]]]]]
298
297
  )
299
298
 
300
299
  expect(msg).to eql(num: ['must be a time'])
@@ -304,9 +303,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
304
303
  describe ':max_size?' do
305
304
  it 'returns valid message' do
306
305
  msg = error_compiler.visit(
307
- [:input, [:num, [
308
- :result, ['abcd', [:val, [:predicate, [:max_size?, [3]]]]]]
309
- ]]
306
+ [:input, [:num, [:result, ['abcd', [:val, p(:max_size?, 3)]]]]]
310
307
  )
311
308
 
312
309
  expect(msg).to eql(num: ['size cannot be greater than 3'])
@@ -316,9 +313,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
316
313
  describe ':min_size?' do
317
314
  it 'returns valid message' do
318
315
  msg = error_compiler.visit(
319
- [:input, [:num, [
320
- :result, ['ab', [:val, [:predicate, [:min_size?, [3]]]]]]
321
- ]]
316
+ [:input, [:num, [:result, ['ab', [:val, p(:min_size?, 3)]]]]]
322
317
  )
323
318
 
324
319
  expect(msg).to eql(num: ['size cannot be less than 3'])
@@ -328,9 +323,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
328
323
  describe ':none?' do
329
324
  it 'returns valid message' do
330
325
  msg = error_compiler.visit(
331
- [:input, [:num, [
332
- :result, [nil, [:val, [:predicate, [:none?, []]]]]]
333
- ]]
326
+ [:input, [:num, [:result, [nil, [:val, p(:none?)]]]]]
334
327
  )
335
328
 
336
329
  expect(msg).to eql(num: ['cannot be defined'])
@@ -340,9 +333,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
340
333
  describe ':size?' do
341
334
  it 'returns valid message when val is array and arg is int' do
342
335
  msg = error_compiler.visit(
343
- [:input, [:numbers, [
344
- :result, [[1], [:val, [:predicate, [:size?, [3]]]]]]
345
- ]]
336
+ [:input, [:numbers, [:result, [[1], [:val, p(:size?, 3)]]]]]
346
337
  )
347
338
 
348
339
  expect(msg).to eql(numbers: ['size must be 3'])
@@ -350,9 +341,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
350
341
 
351
342
  it 'returns valid message when val is array and arg is range' do
352
343
  msg = error_compiler.visit(
353
- [:input, [:numbers, [
354
- :result, [[1], [:val, [:predicate, [:size?, [3..4]]]]]]
355
- ]]
344
+ [:input, [:numbers, [:result, [[1], [:val, p(:size?, 3..4)]]]]]
356
345
  )
357
346
 
358
347
  expect(msg).to eql(numbers: ['size must be within 3 - 4'])
@@ -360,9 +349,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
360
349
 
361
350
  it 'returns valid message when arg is int' do
362
351
  msg = error_compiler.visit(
363
- [:input, [:num, [
364
- :result, ['ab', [:val, [:predicate, [:size?, [3]]]]]]
365
- ]]
352
+ [:input, [:num, [:result, ['ab', [:val, p(:size?, 3)]]]]]
366
353
  )
367
354
 
368
355
  expect(msg).to eql(num: ['length must be 3'])
@@ -370,9 +357,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
370
357
 
371
358
  it 'returns valid message when arg is range' do
372
359
  msg = error_compiler.visit(
373
- [:input, [:num, [
374
- :result, ['ab', [:val, [:predicate, [:size?, [3..4]]]]]]
375
- ]]
360
+ [:input, [:num, [:result, ['ab', [:val, p(:size?, 3..4)]]]]]
376
361
  )
377
362
 
378
363
  expect(msg).to eql(num: ['length must be within 3 - 4'])
@@ -382,9 +367,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
382
367
  describe ':str?' do
383
368
  it 'returns valid message' do
384
369
  msg = error_compiler.visit(
385
- [:input, [:num, [
386
- :result, [3, [:val, [:predicate, [:str?, []]]]]]
387
- ]]
370
+ [:input, [:num, [:result, [3, [:val, p(:str?)]]]]]
388
371
  )
389
372
 
390
373
  expect(msg).to eql(num: ['must be a string'])
@@ -394,9 +377,7 @@ RSpec.describe Dry::Validation::ErrorCompiler do
394
377
  describe ':bool?' do
395
378
  it 'returns valid message' do
396
379
  msg = error_compiler.visit(
397
- [:input, [:num, [
398
- :result, [3, [:val, [:predicate, [:bool?, []]]]]]
399
- ]]
380
+ [:input, [:num, [:result, [3, [:val, p(:bool?)]]]]]
400
381
  )
401
382
 
402
383
  expect(msg).to eql(num: ['must be boolean'])
@@ -406,33 +387,67 @@ RSpec.describe Dry::Validation::ErrorCompiler do
406
387
  describe ':format?' do
407
388
  it 'returns valid message' do
408
389
  msg = error_compiler.visit(
409
- [:input, [:str, [
410
- :result, ['Bar', [:val, [:predicate, [:format?, [/^F/]]]]]]
411
- ]]
390
+ [:input, [:str, [:result, ['Bar', [:val, p(:format?, /^F/)]]]]]
412
391
  )
413
392
 
414
393
  expect(msg).to eql(str: ['is in invalid format'])
415
394
  end
416
395
  end
417
396
 
397
+ describe ':number?' do
398
+ it 'returns valid message' do
399
+ msg = error_compiler.visit(
400
+ [:input, [:str, [:result, ["not a number", [:val, p(:number?)]]]]]
401
+ )
402
+
403
+ expect(msg).to eql(str: ['must be a number'])
404
+ end
405
+ end
406
+
407
+ describe ':odd?' do
408
+ it 'returns valid message' do
409
+ msg = error_compiler.visit(
410
+ [:input, [:str, [:result, [1, [:val, p(:odd?)]]]]]
411
+ )
412
+
413
+ expect(msg).to eql(str: ['must be odd'])
414
+ end
415
+ end
416
+
417
+ describe ':even?' do
418
+ it 'returns valid message' do
419
+ msg = error_compiler.visit(
420
+ [:input, [:str, [:result, [2, [:val, p(:even?)]]]]]
421
+ )
422
+
423
+ expect(msg).to eql(str: ['must be even'])
424
+ end
425
+ end
426
+
418
427
  describe ':eql?' do
419
428
  it 'returns valid message' do
420
429
  msg = error_compiler.visit(
421
- [:input, [:str, [
422
- :result, ['Foo', [:val, [:predicate, [:eql?, ['Bar']]]]]]
423
- ]]
430
+ [:input, [:str, [:result, ['Foo', [:val, p(:eql?, 'Bar')]]]]]
424
431
  )
425
432
 
426
433
  expect(msg).to eql(str: ['must be equal to Bar'])
427
434
  end
428
435
  end
429
436
 
437
+ describe ':not_eql?' do
438
+ it 'returns valid message' do
439
+ msg = error_compiler.visit(
440
+ [:input, [:str, [:result, ['Foo', [:val, p(:not_eql?, 'Foo')]]]]]
441
+ )
442
+
443
+ expect(msg).to eql(str: ['must not be equal to Foo'])
444
+ end
445
+ end
446
+
430
447
  describe ':type??' do
431
448
  it 'returns valid message' do
432
449
  msg = error_compiler.visit(
433
- [:input, [:age, [
434
- :result, ['1', [:val, [:predicate, [:type?, [Integer]]]]]]
435
- ]]
450
+ [:input, [:age, [:result, ['1', [:val, p(:type?, Integer)]]]]]
436
451
  )
437
452
 
438
453
  expect(msg).to eql(age: ['must be Integer'])