dry-validation 0.2.0 → 0.3.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 +26 -2
- data/Gemfile +4 -0
- data/README.md +131 -42
- data/config/errors.yml +36 -27
- data/examples/basic.rb +2 -4
- data/examples/each.rb +2 -2
- data/examples/form.rb +1 -2
- data/examples/nested.rb +2 -4
- data/examples/rule_ast.rb +0 -8
- data/lib/dry/validation.rb +0 -5
- data/lib/dry/validation/error.rb +2 -6
- data/lib/dry/validation/error_compiler.rb +19 -5
- data/lib/dry/validation/input_type_compiler.rb +2 -1
- data/lib/dry/validation/messages.rb +7 -58
- data/lib/dry/validation/messages/abstract.rb +75 -0
- data/lib/dry/validation/messages/i18n.rb +24 -0
- data/lib/dry/validation/messages/namespaced.rb +27 -0
- data/lib/dry/validation/messages/yaml.rb +50 -0
- data/lib/dry/validation/result.rb +19 -49
- data/lib/dry/validation/rule.rb +2 -2
- data/lib/dry/validation/rule/group.rb +21 -0
- data/lib/dry/validation/rule/result.rb +73 -0
- data/lib/dry/validation/rule_compiler.rb +5 -0
- data/lib/dry/validation/schema.rb +33 -14
- data/lib/dry/validation/schema/definition.rb +16 -0
- data/lib/dry/validation/schema/result.rb +21 -3
- data/lib/dry/validation/schema/rule.rb +1 -1
- data/lib/dry/validation/schema/value.rb +2 -1
- data/lib/dry/validation/version.rb +1 -1
- data/spec/fixtures/locales/en.yml +5 -0
- data/spec/fixtures/locales/pl.yml +14 -0
- data/spec/integration/custom_error_messages_spec.rb +4 -16
- data/spec/{unit → integration}/error_compiler_spec.rb +81 -39
- data/spec/integration/localized_error_messages_spec.rb +52 -0
- data/spec/integration/messages/i18n_spec.rb +71 -0
- data/spec/integration/rule_groups_spec.rb +35 -0
- data/spec/integration/schema_form_spec.rb +9 -9
- data/spec/integration/schema_spec.rb +2 -2
- data/spec/shared/predicates.rb +2 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/rule/group_spec.rb +12 -0
- data/spec/unit/schema_spec.rb +35 -0
- metadata +24 -6
- data/spec/fixtures/errors.yml +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4c5757cf4c0d97231cb5077aa617a731ae8b77c
|
4
|
+
data.tar.gz: bb77ddf24b5108592e5ab92961bff80caefb20e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78c62e2a2585215397e12cb0621497e1b4332750b61501534b0e1f643f7d5ea7f82e05f3c64b9977a313acf87466c8031ec160e3706794b34ce90c1fe8a7300a
|
7
|
+
data.tar.gz: 1a9685102ac167001c910e617dee87cea45f6413d105f5231e93790bc3dad0c7199e67a0d838b294185520e26a5475ab56f9b13df08be92e5880a630b129ca20
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,34 @@
|
|
1
|
-
# v0.
|
1
|
+
# v0.3.0 to-be-released
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* I18n messages support (solnic)
|
6
|
+
* Ability to configure `messages` via `configure { config.messages = :i18n }` (solnic)
|
7
|
+
* `rule` interface in DSL for defining rules that depend on other rules (solnic)
|
8
|
+
* `confirmation` interface as a shortcut for defining "confirmation of" rule (solnic)
|
9
|
+
* Error messages can be now matched by input value type too (solnic)
|
10
|
+
|
11
|
+
### Fixed
|
12
|
+
|
13
|
+
* `optional` rule with coercions work correctly with `|` + multiple `&`s (solnic)
|
14
|
+
* `Schema#[]` checks registered predicates first before defaulting to its own predicates (solnic)
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
|
18
|
+
* `Schema#messages(input)` => `Schema#call(input).messages` (solnic)
|
19
|
+
* `Schema#call` returns `Schema::Result` which has access to all rule results,
|
20
|
+
errors and messages
|
21
|
+
* `Schema::Result#messages` returns a hash with rule names, messages and input values (solnic)
|
22
|
+
|
23
|
+
[Compare v0.2.0...HEAD](https://github.com/dryrb/dry-validation/compare/v0.2.0...HEAD)
|
24
|
+
|
25
|
+
# v0.2.0 2015-11-30
|
2
26
|
|
3
27
|
### Added
|
4
28
|
|
5
29
|
* `Schema::Form` with a built-in coercer inferred from type-check predicates (solnic)
|
6
30
|
* Ability to pass a block to predicate check in the DSL ie `value.hash? { ... }` (solnic)
|
7
|
-
* Optional keys using `
|
31
|
+
* Optional keys using `optional(:key_name) { ... }` interface in the DSL (solnic)
|
8
32
|
* New predicates:
|
9
33
|
- `bool?`
|
10
34
|
- `date?`
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -75,15 +75,15 @@ end
|
|
75
75
|
|
76
76
|
schema = Schema.new
|
77
77
|
|
78
|
-
errors = schema.
|
78
|
+
errors = schema.call(email: 'jane@doe.org', age: 19).messages
|
79
79
|
|
80
80
|
puts errors.inspect
|
81
81
|
# []
|
82
82
|
|
83
|
-
errors = schema.
|
83
|
+
errors = schema.call(email: nil, age: 19).messages
|
84
84
|
|
85
85
|
puts errors.inspect
|
86
|
-
#
|
86
|
+
# { :email => [["email must be filled", nil]] }
|
87
87
|
```
|
88
88
|
|
89
89
|
A couple of remarks:
|
@@ -112,15 +112,15 @@ end
|
|
112
112
|
|
113
113
|
schema = Schema.new
|
114
114
|
|
115
|
-
errors = schema.
|
115
|
+
errors = schema.call(email: 'jane@doe.org').messages
|
116
116
|
|
117
117
|
puts errors.inspect
|
118
118
|
# []
|
119
119
|
|
120
|
-
errors = schema.
|
120
|
+
errors = schema.call(email: 'jane@doe.org', age: 17).messages
|
121
121
|
|
122
122
|
puts errors.inspect
|
123
|
-
#
|
123
|
+
# { :age => [["age must be greater than 18"], 17] }
|
124
124
|
```
|
125
125
|
|
126
126
|
### Optional Values
|
@@ -140,20 +140,20 @@ end
|
|
140
140
|
|
141
141
|
schema = Schema.new
|
142
142
|
|
143
|
-
errors = schema.
|
143
|
+
errors = schema.call(email: 'jane@doe.org', age: nil).messages
|
144
144
|
|
145
145
|
puts errors.inspect
|
146
146
|
# []
|
147
147
|
|
148
|
-
errors = schema.
|
148
|
+
errors = schema.call(email: 'jane@doe.org', age: 19).messages
|
149
149
|
|
150
150
|
puts errors.inspect
|
151
151
|
# []
|
152
152
|
|
153
|
-
errors = schema.
|
153
|
+
errors = schema.call(email: 'jane@doe.org', age: 17).messages
|
154
154
|
|
155
155
|
puts errors.inspect
|
156
|
-
#
|
156
|
+
# { :age => [["age must be greater than 18"], 17] }
|
157
157
|
```
|
158
158
|
|
159
159
|
### Optional Key vs Value
|
@@ -201,15 +201,20 @@ end
|
|
201
201
|
|
202
202
|
schema = Schema.new
|
203
203
|
|
204
|
-
errors = schema.
|
204
|
+
errors = schema.call({}).messages
|
205
205
|
|
206
206
|
puts errors.inspect
|
207
|
-
#
|
207
|
+
# { :address => ["address is missing"] }
|
208
208
|
|
209
|
-
errors = schema.
|
209
|
+
errors = schema.call(address: { city: 'NYC' }).messages
|
210
210
|
|
211
|
-
puts errors.inspect
|
212
|
-
#
|
211
|
+
puts errors.to_h.inspect
|
212
|
+
# {
|
213
|
+
# :address => [
|
214
|
+
# { :street => ["street is missing"] },
|
215
|
+
# { :country => ["country is missing"] }
|
216
|
+
# ]
|
217
|
+
# }
|
213
218
|
```
|
214
219
|
|
215
220
|
### Array Elements
|
@@ -227,17 +232,50 @@ end
|
|
227
232
|
|
228
233
|
schema = Schema.new
|
229
234
|
|
230
|
-
errors = schema.
|
235
|
+
errors = schema.call(phone_numbers: '').messages
|
231
236
|
|
232
237
|
puts errors.inspect
|
233
|
-
#
|
238
|
+
# { :phone_numbers => [["phone_numbers must be an array", ""]] }
|
234
239
|
|
235
|
-
errors = schema.
|
240
|
+
errors = schema.call(phone_numbers: ['123456789', 123456789]).messages
|
236
241
|
|
237
242
|
puts errors.inspect
|
238
|
-
#
|
243
|
+
# {
|
244
|
+
# :phone_numbers => [
|
245
|
+
# {
|
246
|
+
# :phone_numbers => [
|
247
|
+
# ["phone_numbers must be a string", 123456789]
|
248
|
+
# ]
|
249
|
+
# }
|
250
|
+
# ]
|
251
|
+
# }
|
252
|
+
```
|
253
|
+
|
254
|
+
### Rules Depending On Other Rules
|
255
|
+
|
256
|
+
When a rule needs input from other rules and depends on their results you can
|
257
|
+
define it using `rule` DSL. A common example of this is "confirmation validation":
|
258
|
+
|
259
|
+
``` ruby
|
260
|
+
class Schema < Dry::Validation::Schema
|
261
|
+
key(:password, &:filled?)
|
262
|
+
key(:password_confirmation, &:filled?)
|
263
|
+
|
264
|
+
rule(:password_confirmation, eql?: [:password, :password_confirmation])
|
265
|
+
end
|
239
266
|
```
|
240
267
|
|
268
|
+
A short version of the same thing:
|
269
|
+
|
270
|
+
``` ruby
|
271
|
+
class Schema < Dry::Validation::Schema
|
272
|
+
confirmation(:password)
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
Notice that you must add `:password_confirmation` error message configuration if
|
277
|
+
you want to have the error converted to a message.
|
278
|
+
|
241
279
|
### Form Validation With Coercions
|
242
280
|
|
243
281
|
Probably the most common use case is to validate form params. This is a special
|
@@ -262,11 +300,13 @@ end
|
|
262
300
|
|
263
301
|
schema = UserFormSchema.new
|
264
302
|
|
265
|
-
errors = schema.
|
303
|
+
errors = schema.call('email' => '', 'age' => '18').messages
|
266
304
|
|
267
305
|
puts errors.inspect
|
268
|
-
|
269
|
-
#
|
306
|
+
# {
|
307
|
+
# :email => [["email must be filled", nil]],
|
308
|
+
# :age => [["age must be greater than 18 (18 was given)", 18]]
|
309
|
+
# }
|
270
310
|
```
|
271
311
|
|
272
312
|
There are few major differences between how it works here and in `ActiveModel`:
|
@@ -350,7 +390,8 @@ You can learn how to do that in the [Error Messages](https://github.com/dryrb/dr
|
|
350
390
|
* `lteq?`
|
351
391
|
* `max_size?`
|
352
392
|
* `min_size?`
|
353
|
-
* `size
|
393
|
+
* `size?(int)`
|
394
|
+
* `size?(range)`
|
354
395
|
* `format?`
|
355
396
|
* `inclusion?`
|
356
397
|
* `exclusion?`
|
@@ -359,7 +400,7 @@ You can learn how to do that in the [Error Messages](https://github.com/dryrb/dr
|
|
359
400
|
|
360
401
|
By default `dry-validation` comes with a set of pre-defined error messages for
|
361
402
|
every built-in predicate. They are defined in [a yaml file](https://github.com/dryrb/dry-validation/blob/master/config/errors.yml)
|
362
|
-
which is shipped with the gem.
|
403
|
+
which is shipped with the gem. This file is compatible with `I18n` format.
|
363
404
|
|
364
405
|
You can provide your own messages and configure your schemas to use it like that:
|
365
406
|
|
@@ -380,18 +421,31 @@ end
|
|
380
421
|
Lookup rules:
|
381
422
|
|
382
423
|
``` yaml
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
424
|
+
en:
|
425
|
+
errors:
|
426
|
+
size?:
|
427
|
+
arg:
|
428
|
+
default: "%{name} size must be %{num}"
|
429
|
+
range: "%{name} size must be within %{left} - %{right}"
|
430
|
+
|
431
|
+
value:
|
432
|
+
string:
|
433
|
+
arg:
|
434
|
+
default: "%{name} length must be %{num}"
|
435
|
+
range: "%{name} length must be within %{left} - %{right}"
|
436
|
+
|
437
|
+
filled?: "%{name} must be filled"
|
438
|
+
|
439
|
+
rules:
|
440
|
+
email:
|
441
|
+
filled?: "the email is missing"
|
442
|
+
|
443
|
+
user:
|
444
|
+
filled?: "%{name} name cannot be blank"
|
445
|
+
|
446
|
+
rules:
|
447
|
+
address:
|
448
|
+
filled?: "You gotta tell us where you live"
|
395
449
|
```
|
396
450
|
|
397
451
|
Given the yaml file above, messages lookup works as follows:
|
@@ -399,15 +453,25 @@ Given the yaml file above, messages lookup works as follows:
|
|
399
453
|
``` ruby
|
400
454
|
messages = Dry::Validation::Messages.load('/path/to/our/errors.yml')
|
401
455
|
|
402
|
-
|
403
|
-
messages
|
404
|
-
messages
|
456
|
+
# matching arg type for size? predicate
|
457
|
+
messages[:size?, rule: :name, arg_type: Fixnum] # => "%{name} size must be %{num}"
|
458
|
+
messages[:size?, rule: :name, arg_type: Range] # => "%{name} size must within %{left} - %{right}"
|
459
|
+
|
460
|
+
# matching val type for size? predicate
|
461
|
+
messages[:size?, rule: :name, val_type: String] # => "%{name} length must be %{num}"
|
462
|
+
|
463
|
+
# matching predicate
|
464
|
+
messages[:filled?, rule: :age] # => "%{name} must be filled"
|
465
|
+
messages[:filled?, rule: :address] # => "%{name} must be filled"
|
466
|
+
|
467
|
+
# matching predicate for a specific rule
|
468
|
+
messages[:filled?, rule: :email] # => "the email is missing"
|
405
469
|
|
406
470
|
# with namespaced messages
|
407
471
|
user_messages = messages.namespaced(:user)
|
408
472
|
|
409
|
-
user_messages
|
410
|
-
user_messages
|
473
|
+
user_messages[:filled?, rule: :age] # "%{name} cannot be blank"
|
474
|
+
user_messages[:filled?, rule: :address] # "You gotta tell us where you live"
|
411
475
|
```
|
412
476
|
|
413
477
|
By configuring `messages_file` and/or `namespace` in a schema, default messages
|
@@ -415,7 +479,32 @@ are going to be automatically merged with your overrides and/or namespaced.
|
|
415
479
|
|
416
480
|
## I18n Integration
|
417
481
|
|
418
|
-
|
482
|
+
If you are using `i18n` gem and load it before `dry-validation` then you'll be
|
483
|
+
able to configure a schema to use `i18n` messages:
|
484
|
+
|
485
|
+
``` ruby
|
486
|
+
require 'i18n'
|
487
|
+
require 'dry-validation'
|
488
|
+
|
489
|
+
class Schema < Dry::Validation::Schema
|
490
|
+
configure { config.messages = :i18n }
|
491
|
+
|
492
|
+
key(:email, &:filled?)
|
493
|
+
end
|
494
|
+
|
495
|
+
schema = Schema.new
|
496
|
+
|
497
|
+
# return default translations
|
498
|
+
puts schema.call(email: '').messages
|
499
|
+
{ :email => ["email must be filled"] }
|
500
|
+
|
501
|
+
# return other translations (assuming you have it :))
|
502
|
+
puts schema.call(email: '').messages(locale: :pl)
|
503
|
+
{ :email => ["email musi być wypełniony"] }
|
504
|
+
```
|
505
|
+
|
506
|
+
Important: I18n must be initialized before using schema, `dry-validation` does
|
507
|
+
not try to do it for you, it only sets its default error translations automatically.
|
419
508
|
|
420
509
|
## Rule AST
|
421
510
|
|
data/config/errors.yml
CHANGED
@@ -1,51 +1,60 @@
|
|
1
|
-
|
1
|
+
en:
|
2
|
+
errors:
|
3
|
+
array?: "%{name} must be an array"
|
2
4
|
|
3
|
-
empty?: "%{name} cannot be empty"
|
5
|
+
empty?: "%{name} cannot be empty"
|
4
6
|
|
5
|
-
exclusion?: "%{name} must not be one of: %{list}"
|
7
|
+
exclusion?: "%{name} must not be one of: %{list}"
|
6
8
|
|
7
|
-
eql?: "%{name} must be equal to %{eql_value}"
|
9
|
+
eql?: "%{name} must be equal to %{eql_value}"
|
8
10
|
|
9
|
-
filled?: "%{name} must be filled"
|
11
|
+
filled?: "%{name} must be filled"
|
10
12
|
|
11
|
-
format?: "%{name} is in invalid format"
|
13
|
+
format?: "%{name} is in invalid format"
|
12
14
|
|
13
|
-
gt?: "%{name} must be greater than %{num}
|
15
|
+
gt?: "%{name} must be greater than %{num}"
|
14
16
|
|
15
|
-
gteq?: "%{name} must be greater than or equal to %{num}"
|
17
|
+
gteq?: "%{name} must be greater than or equal to %{num}"
|
16
18
|
|
17
|
-
hash?: "%{name} must be a hash"
|
19
|
+
hash?: "%{name} must be a hash"
|
18
20
|
|
19
|
-
inclusion?: "%{name} must be one of: %{list}"
|
21
|
+
inclusion?: "%{name} must be one of: %{list}"
|
20
22
|
|
21
|
-
bool?: "%{name} must be boolean"
|
23
|
+
bool?: "%{name} must be boolean"
|
22
24
|
|
23
|
-
int?: "%{name} must be an integer"
|
25
|
+
int?: "%{name} must be an integer"
|
24
26
|
|
25
|
-
float?: "%{name} must be a float"
|
27
|
+
float?: "%{name} must be a float"
|
26
28
|
|
27
|
-
decimal?: "%{name} must be a decimal"
|
29
|
+
decimal?: "%{name} must be a decimal"
|
28
30
|
|
29
|
-
date?: "%{name} must be a date"
|
31
|
+
date?: "%{name} must be a date"
|
30
32
|
|
31
|
-
date_time?: "%{name} must be a date time"
|
33
|
+
date_time?: "%{name} must be a date time"
|
32
34
|
|
33
|
-
time?: "%{name} must be a time"
|
35
|
+
time?: "%{name} must be a time"
|
34
36
|
|
35
|
-
key?: "%{name} is missing"
|
37
|
+
key?: "%{name} is missing"
|
36
38
|
|
37
|
-
lt?: "%{name} must be less than %{num} (%{value} was given)"
|
39
|
+
lt?: "%{name} must be less than %{num} (%{value} was given)"
|
38
40
|
|
39
|
-
lteq?: "%{name} must be less than or equal to %{num}"
|
41
|
+
lteq?: "%{name} must be less than or equal to %{num}"
|
40
42
|
|
41
|
-
max_size?: "%{name} size cannot be greater than %{num}"
|
43
|
+
max_size?: "%{name} size cannot be greater than %{num}"
|
42
44
|
|
43
|
-
min_size?: "%{name} size cannot be less than %{num}"
|
45
|
+
min_size?: "%{name} size cannot be less than %{num}"
|
44
46
|
|
45
|
-
|
47
|
+
none?: "%{name} cannot be defined"
|
46
48
|
|
47
|
-
|
48
|
-
range: "%{name} size must be within %{left} - %{right}"
|
49
|
-
default: "%{name} size must be %{num}"
|
49
|
+
str?: "%{name} must be a string"
|
50
50
|
|
51
|
-
|
51
|
+
size?:
|
52
|
+
arg:
|
53
|
+
default: "%{name} size must be %{num}"
|
54
|
+
range: "%{name} size must be within %{left} - %{right}"
|
55
|
+
|
56
|
+
value:
|
57
|
+
string:
|
58
|
+
arg:
|
59
|
+
default: "%{name} length must be %{num}"
|
60
|
+
range: "%{name} length must be within %{left} - %{right}"
|
data/examples/basic.rb
CHANGED
@@ -10,12 +10,10 @@ end
|
|
10
10
|
|
11
11
|
schema = Schema.new
|
12
12
|
|
13
|
-
errors = schema.
|
13
|
+
errors = schema.call(email: 'jane@doe.org', age: 19).messages
|
14
14
|
|
15
15
|
puts errors.inspect
|
16
|
-
# []
|
17
16
|
|
18
|
-
errors = schema.
|
17
|
+
errors = schema.call(email: nil, age: 19).messages
|
19
18
|
|
20
19
|
puts errors.inspect
|
21
|
-
# [[:email, ["email must be filled"]]]
|
data/examples/each.rb
CHANGED
@@ -10,10 +10,10 @@ end
|
|
10
10
|
|
11
11
|
schema = Schema.new
|
12
12
|
|
13
|
-
errors = schema.
|
13
|
+
errors = schema.call(phone_numbers: '').messages
|
14
14
|
|
15
15
|
puts errors.inspect
|
16
16
|
|
17
|
-
errors = schema.
|
17
|
+
errors = schema.call(phone_numbers: ['123456789', 123456789]).messages
|
18
18
|
|
19
19
|
puts errors.inspect
|