dry-validation 0.8.0 → 0.9.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/.travis.yml +1 -1
- data/CHANGELOG.md +39 -1
- data/benchmarks/benchmark_schema_invalid_huge.rb +52 -0
- data/benchmarks/profile_schema_huge_invalid.rb +30 -0
- data/config/errors.yml +3 -2
- data/dry-validation.gemspec +2 -2
- data/lib/dry/validation.rb +20 -32
- data/lib/dry/validation/constants.rb +6 -0
- data/lib/dry/validation/error.rb +5 -2
- data/lib/dry/validation/error_compiler.rb +46 -116
- data/lib/dry/validation/executor.rb +105 -0
- data/lib/dry/validation/hint_compiler.rb +36 -68
- data/lib/dry/validation/message.rb +86 -0
- data/lib/dry/validation/message_compiler.rb +141 -0
- data/lib/dry/validation/message_set.rb +70 -0
- data/lib/dry/validation/messages/abstract.rb +1 -1
- data/lib/dry/validation/messages/i18n.rb +5 -0
- data/lib/dry/validation/predicate_registry.rb +8 -3
- data/lib/dry/validation/result.rb +6 -7
- data/lib/dry/validation/schema.rb +21 -227
- data/lib/dry/validation/schema/check.rb +1 -1
- data/lib/dry/validation/schema/class_interface.rb +193 -0
- data/lib/dry/validation/schema/deprecated.rb +1 -2
- data/lib/dry/validation/schema/key.rb +4 -0
- data/lib/dry/validation/schema/value.rb +12 -7
- data/lib/dry/validation/schema_compiler.rb +20 -1
- data/lib/dry/validation/type_specs.rb +70 -0
- data/lib/dry/validation/version.rb +1 -1
- data/spec/fixtures/locales/pl.yml +1 -1
- data/spec/integration/custom_predicates_spec.rb +37 -0
- data/spec/integration/error_compiler_spec.rb +39 -39
- data/spec/integration/form/predicates/key_spec.rb +10 -18
- data/spec/integration/form/predicates/size/fixed_spec.rb +8 -12
- data/spec/integration/form/predicates/size/range_spec.rb +7 -7
- data/spec/integration/hints_spec.rb +17 -0
- data/spec/integration/messages/i18n_spec.rb +2 -2
- data/spec/integration/schema/check_rules_spec.rb +2 -2
- data/spec/integration/schema/defining_base_schema_spec.rb +38 -0
- data/spec/integration/schema/dynamic_predicate_args_spec.rb +18 -0
- data/spec/integration/schema/macros/each_spec.rb +2 -2
- data/spec/integration/schema/macros/input_spec.rb +102 -10
- data/spec/integration/schema/macros/maybe_spec.rb +30 -0
- data/spec/integration/schema/nested_schemas_spec.rb +200 -0
- data/spec/integration/schema/nested_values_spec.rb +3 -1
- data/spec/integration/schema/option_with_default_spec.rb +54 -20
- data/spec/integration/schema/predicates/size/fixed_spec.rb +10 -10
- data/spec/integration/schema/predicates/size/range_spec.rb +8 -10
- data/spec/unit/error_compiler_spec.rb +1 -1
- data/spec/unit/hint_compiler_spec.rb +2 -2
- metadata +18 -7
- data/examples/rule_ast.rb +0 -25
- data/lib/dry/validation/error_compiler/input.rb +0 -135
@@ -1,28 +1,20 @@
|
|
1
|
-
#see: https://github.com/dry-rb/dry-validation/issues/127
|
2
|
-
|
3
1
|
RSpec.describe 'Predicates: Key' do
|
4
2
|
context 'with required' do
|
5
|
-
|
6
|
-
expect { Dry::Validation.Form
|
7
|
-
required(:foo) { key? }
|
8
|
-
end }.to raise_error InvalidSchemaError
|
3
|
+
it "raises error" do
|
4
|
+
expect { Dry::Validation.Form { required(:foo) { key? } } }.to raise_error InvalidSchemaError
|
9
5
|
end
|
10
6
|
end
|
11
7
|
|
12
8
|
context 'with optional' do
|
13
|
-
|
14
|
-
|
15
|
-
expect { Dry::Validation.Form do
|
16
|
-
optional(:foo) { key? }
|
17
|
-
end }.to raise_error InvalidSchemaError
|
18
|
-
end
|
9
|
+
it "raises error" do
|
10
|
+
expect { Dry::Validation.Form { optional(:foo) { key? } } }.to raise_error InvalidSchemaError
|
19
11
|
end
|
20
12
|
end
|
21
13
|
|
22
14
|
context 'as macro' do
|
23
15
|
context 'with required' do
|
24
16
|
context 'with value' do
|
25
|
-
|
17
|
+
it "raises error" do
|
26
18
|
expect { Dry::Validation.Form do
|
27
19
|
required(:foo).value(:key?)
|
28
20
|
end }.to raise_error InvalidSchemaError
|
@@ -30,7 +22,7 @@ RSpec.describe 'Predicates: Key' do
|
|
30
22
|
end
|
31
23
|
|
32
24
|
context 'with filled' do
|
33
|
-
|
25
|
+
it "raises error" do
|
34
26
|
expect { Dry::Validation.Form do
|
35
27
|
required(:foo).filled(:key?)
|
36
28
|
end }.to raise_error InvalidSchemaError
|
@@ -38,7 +30,7 @@ RSpec.describe 'Predicates: Key' do
|
|
38
30
|
end
|
39
31
|
|
40
32
|
context 'with maybe' do
|
41
|
-
|
33
|
+
it "raises error" do
|
42
34
|
expect { Dry::Validation.Form do
|
43
35
|
required(:foo).maybe(:key?)
|
44
36
|
end }.to raise_error InvalidSchemaError
|
@@ -48,7 +40,7 @@ RSpec.describe 'Predicates: Key' do
|
|
48
40
|
|
49
41
|
context 'with optional' do
|
50
42
|
context 'with value' do
|
51
|
-
|
43
|
+
it "raises error" do
|
52
44
|
expect { Dry::Validation.Schema do
|
53
45
|
optional(:foo).value(:key?)
|
54
46
|
end }.to raise_error InvalidSchemaError
|
@@ -56,7 +48,7 @@ RSpec.describe 'Predicates: Key' do
|
|
56
48
|
end
|
57
49
|
|
58
50
|
context 'with filled' do
|
59
|
-
|
51
|
+
it "raises error" do
|
60
52
|
expect { Dry::Validation.Schema do
|
61
53
|
optional(:foo).filled(:key?)
|
62
54
|
end }.to raise_error InvalidSchemaError
|
@@ -64,7 +56,7 @@ RSpec.describe 'Predicates: Key' do
|
|
64
56
|
end
|
65
57
|
|
66
58
|
context 'with maybe' do
|
67
|
-
|
59
|
+
it "raises error" do
|
68
60
|
expect { Dry::Validation.Schema do
|
69
61
|
optional(:foo).maybe(:key?)
|
70
62
|
end }.to raise_error InvalidSchemaError
|
@@ -34,8 +34,7 @@ RSpec.describe 'Predicates: Size' do
|
|
34
34
|
context 'with blank input' do
|
35
35
|
let(:input) { { 'foo' => '' } }
|
36
36
|
|
37
|
-
|
38
|
-
xit 'is not successful' do
|
37
|
+
it 'is not successful' do
|
39
38
|
expect(result).to be_failing ['length must be 3']
|
40
39
|
end
|
41
40
|
end
|
@@ -83,8 +82,7 @@ RSpec.describe 'Predicates: Size' do
|
|
83
82
|
context 'with blank input' do
|
84
83
|
let(:input) { { 'foo' => '' } }
|
85
84
|
|
86
|
-
|
87
|
-
xit 'is not successful' do
|
85
|
+
it 'is not successful' do
|
88
86
|
expect(result).to be_failing ['length must be 3']
|
89
87
|
end
|
90
88
|
end
|
@@ -134,8 +132,7 @@ RSpec.describe 'Predicates: Size' do
|
|
134
132
|
context 'with blank input' do
|
135
133
|
let(:input) { { 'foo' => '' } }
|
136
134
|
|
137
|
-
|
138
|
-
xit 'is not successful' do
|
135
|
+
it 'is not successful' do
|
139
136
|
expect(result).to be_failing ['length must be 3']
|
140
137
|
end
|
141
138
|
end
|
@@ -183,8 +180,8 @@ RSpec.describe 'Predicates: Size' do
|
|
183
180
|
context 'with blank input' do
|
184
181
|
let(:input) { { 'foo' => '' } }
|
185
182
|
|
186
|
-
|
187
|
-
|
183
|
+
it 'is not successful' do
|
184
|
+
pending
|
188
185
|
expect(result).to be_failing ['must be filled', 'length must be 3']
|
189
186
|
end
|
190
187
|
end
|
@@ -282,8 +279,7 @@ RSpec.describe 'Predicates: Size' do
|
|
282
279
|
context 'with blank input' do
|
283
280
|
let(:input) { { 'foo' => '' } }
|
284
281
|
|
285
|
-
|
286
|
-
xit 'is not successful' do
|
282
|
+
it 'is not successful' do
|
287
283
|
expect(result).to be_failing ['length must be 3']
|
288
284
|
end
|
289
285
|
end
|
@@ -331,8 +327,8 @@ RSpec.describe 'Predicates: Size' do
|
|
331
327
|
context 'with blank input' do
|
332
328
|
let(:input) { { 'foo' => '' } }
|
333
329
|
|
334
|
-
|
335
|
-
|
330
|
+
it 'is not successful' do
|
331
|
+
pending
|
336
332
|
expect(result).to be_failing ['must be filled', 'length must be 3']
|
337
333
|
end
|
338
334
|
end
|
@@ -35,7 +35,7 @@ RSpec.describe 'Predicates: Size' do
|
|
35
35
|
let(:input) { { 'foo' => '' } }
|
36
36
|
|
37
37
|
#see: https://github.com/dry-rb/dry-validation/issues/121
|
38
|
-
|
38
|
+
it 'is not successful' do
|
39
39
|
expect(result).to be_failing ['length must be within 2 - 3']
|
40
40
|
end
|
41
41
|
end
|
@@ -84,7 +84,7 @@ RSpec.describe 'Predicates: Size' do
|
|
84
84
|
let(:input) { { 'foo' => '' } }
|
85
85
|
|
86
86
|
#see: https://github.com/dry-rb/dry-validation/issues/121
|
87
|
-
|
87
|
+
it 'is not successful' do
|
88
88
|
expect(result).to be_failing ['length must be within 2 - 3']
|
89
89
|
end
|
90
90
|
end
|
@@ -182,8 +182,8 @@ RSpec.describe 'Predicates: Size' do
|
|
182
182
|
context 'with blank input' do
|
183
183
|
let(:input) { { 'foo' => '' } }
|
184
184
|
|
185
|
-
|
186
|
-
|
185
|
+
it 'is not successful' do
|
186
|
+
pending
|
187
187
|
expect(result).to be_failing ['must be filled', 'length must be within 2 - 3']
|
188
188
|
end
|
189
189
|
end
|
@@ -282,7 +282,7 @@ RSpec.describe 'Predicates: Size' do
|
|
282
282
|
let(:input) { { 'foo' => '' } }
|
283
283
|
|
284
284
|
#see: https://github.com/dry-rb/dry-validation/issues/121
|
285
|
-
|
285
|
+
it 'is not successful' do
|
286
286
|
expect(result).to be_failing ['length must be within 2 - 3']
|
287
287
|
end
|
288
288
|
end
|
@@ -330,8 +330,8 @@ RSpec.describe 'Predicates: Size' do
|
|
330
330
|
context 'with blank input' do
|
331
331
|
let(:input) { { 'foo' => '' } }
|
332
332
|
|
333
|
-
|
334
|
-
|
333
|
+
it 'is not successful' do
|
334
|
+
pending
|
335
335
|
expect(result).to be_failing ['must be filled', 'length must be within 2 - 3']
|
336
336
|
end
|
337
337
|
end
|
@@ -103,6 +103,23 @@ RSpec.describe 'Validation hints' do
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
+
context 'with an each rule' do
|
107
|
+
subject(:schema) do
|
108
|
+
Dry::Validation.Schema do
|
109
|
+
required(:nums).each(:int?, gt?: 0)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'provides hints for each element' do
|
114
|
+
expect(schema.(nums: [1, 'foo', 0]).messages).to eql(
|
115
|
+
nums: {
|
116
|
+
1 => ['must be an integer', 'must be greater than 0'],
|
117
|
+
2 => ['must be greater than 0']
|
118
|
+
}
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
106
123
|
context 'when the message uses input value' do
|
107
124
|
subject(:schema) do
|
108
125
|
Dry::Validation.Schema do
|
@@ -42,7 +42,7 @@ RSpec.describe Messages::I18n do
|
|
42
42
|
it 'returns a message for a specific rule and its arg type' do
|
43
43
|
message = messages[:size?, rule: :pages, arg_type: Range]
|
44
44
|
|
45
|
-
expect(message).to eql("size must be within %{
|
45
|
+
expect(message).to eql("size must be within %{size_left} - %{size_right}")
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -68,7 +68,7 @@ RSpec.describe Messages::I18n do
|
|
68
68
|
it 'returns a message for a specific rule and its arg type' do
|
69
69
|
message = messages[:size?, rule: :pages, arg_type: Range, locale: :pl]
|
70
70
|
|
71
|
-
expect(message).to eql("wielkość musi być między %{
|
71
|
+
expect(message).to eql("wielkość musi być między %{size_left} a %{size_right}")
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -28,8 +28,8 @@ RSpec.describe Schema, 'using high-level rules' do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'fails when red and blue are not filled ' do
|
31
|
-
expect(schema.(red: nil, blue: nil).messages
|
32
|
-
['you must select either red or blue']
|
31
|
+
expect(schema.(red: nil, blue: nil).messages).to eql(
|
32
|
+
destiny: ['you must select either red or blue']
|
33
33
|
)
|
34
34
|
end
|
35
35
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
RSpec.describe 'Defining base schema class' do
|
2
|
+
subject(:schema) do
|
3
|
+
Dry::Validation.Schema(BaseSchema) do
|
4
|
+
required(:email).filled(:email?)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
class BaseSchema < Dry::Validation::Schema
|
10
|
+
configure do |config|
|
11
|
+
config.messages_file = SPEC_ROOT.join('fixtures/locales/en.yml')
|
12
|
+
config.messages = :i18n
|
13
|
+
end
|
14
|
+
|
15
|
+
def email?(value)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
define! do
|
20
|
+
required(:name).filled
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
Object.send(:remove_const, :BaseSchema)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'inherits predicates' do
|
30
|
+
expect(schema).to respond_to(:email?)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'inherits rules' do
|
34
|
+
expect(schema.(name: nil).messages).to eql(
|
35
|
+
name: ['must be filled'], email: ['is missing', 'must be an email']
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
RSpec.describe Dry::Validation::Schema, 'dynamic predicate args' do
|
2
|
+
subject(:schema) do
|
3
|
+
Dry::Validation.Schema do
|
4
|
+
configure do
|
5
|
+
def data
|
6
|
+
%w(a b c)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
required(:letter).filled(included_in?: data)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'evaluates predicate arguments' do
|
15
|
+
expect(schema.(letter: 'a')).to be_success
|
16
|
+
expect(schema.(letter: 'f')).to be_failure
|
17
|
+
end
|
18
|
+
end
|
@@ -50,7 +50,7 @@ RSpec.describe 'Macros #each' do
|
|
50
50
|
let(:input) { { foo: [[1,2], "foo"] } }
|
51
51
|
|
52
52
|
it 'is not successful' do
|
53
|
-
expect(result).to be_failing(
|
53
|
+
expect(result).to be_failing(0 => ["size must be 3"])
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -58,7 +58,7 @@ RSpec.describe 'Macros #each' do
|
|
58
58
|
let(:input) { { foo: nil } }
|
59
59
|
|
60
60
|
it 'is not successful' do
|
61
|
-
expect(result).to be_failing ["must be an array"
|
61
|
+
expect(result).to be_failing ["must be an array"]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -1,21 +1,113 @@
|
|
1
1
|
RSpec.describe 'Macros #input' do
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
context 'with a flat schema' do
|
3
|
+
subject(:schema) do
|
4
|
+
Dry::Validation.Schema do
|
5
|
+
input :hash?
|
5
6
|
|
6
|
-
|
7
|
+
required(:foo).filled
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'passes when input is valid' do
|
12
|
+
expect(schema.(foo: "bar")).to be_successful
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'prepends a rule for the input' do
|
16
|
+
expect(schema.(nil).messages).to eql(["must be a hash"])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'applies other rules when input has expected type' do
|
20
|
+
expect(schema.(foo: '').messages).to eql(foo: ["must be filled"])
|
7
21
|
end
|
8
22
|
end
|
9
23
|
|
10
|
-
|
11
|
-
|
24
|
+
context 'with a nested schema' do
|
25
|
+
subject(:schema) do
|
26
|
+
Dry::Validation.Schema do
|
27
|
+
input(:hash?)
|
28
|
+
|
29
|
+
required(:foo).schema do
|
30
|
+
required(:bar).schema do
|
31
|
+
required(:baz).filled
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'passes when input is valid' do
|
38
|
+
expect(schema.(foo: { bar: { baz: "present" }})).to be_successful
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'fails when input has invalid type' do
|
42
|
+
expect(schema.(nil).messages).to eql(["must be a hash"])
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'fails when 1-level key is missing' do
|
46
|
+
expect(schema.(foo: {}).messages).to eql(foo: { bar: ["is missing"] })
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'fails when 2-level key has invalid value' do
|
50
|
+
expect(schema.(foo: { bar: { baz: '' }}).messages).to eql(
|
51
|
+
foo: { bar: { baz: ['must be filled'] } }
|
52
|
+
)
|
53
|
+
end
|
12
54
|
end
|
13
55
|
|
14
|
-
|
15
|
-
|
56
|
+
context 'when 2 nested schemas are under the same key' do
|
57
|
+
subject(:schema) do
|
58
|
+
Dry::Validation.Schema do
|
59
|
+
input :hash?
|
60
|
+
|
61
|
+
required(:meta).schema do
|
62
|
+
required(:meta).schema do
|
63
|
+
required(:data).filled
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'passes when input is valid' do
|
70
|
+
expect(schema.(meta: { meta: { data: 'sutin' } })).to be_success
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'fails when root key is missing' do
|
74
|
+
expect(schema.({}).messages).to eql(meta: ['is missing'])
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'fails when 1-level key is missing' do
|
78
|
+
expect(schema.(meta: {}).messages).to eql(meta: { meta: ['is missing'] })
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'fails when 1-level key value is invalid' do
|
82
|
+
expect(schema.(meta: { meta: '' }).messages).to eql(
|
83
|
+
meta: { meta: ['must be a hash'] }
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'fails when 2-level key value is invalid' do
|
88
|
+
expect(schema.(meta: { meta: { data: '' } }).messages).to eql(
|
89
|
+
meta: { meta: { data: ['must be filled'] } }
|
90
|
+
)
|
91
|
+
end
|
16
92
|
end
|
17
93
|
|
18
|
-
|
19
|
-
|
94
|
+
context 'using more than one predicate' do
|
95
|
+
subject(:schema) do
|
96
|
+
Dry::Validation.Schema do
|
97
|
+
input :hash?, size?: 2
|
98
|
+
|
99
|
+
required(:foo).filled
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'passes when input is valid' do
|
104
|
+
expect(schema.(foo: "bar", bar: "baz")).to be_successful
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'fails when one of the root-rules fails' do
|
108
|
+
expect(schema.(foo: "bar", bar: "baz", oops: "heh").messages).to eql(
|
109
|
+
['size must be 2']
|
110
|
+
)
|
111
|
+
end
|
20
112
|
end
|
21
113
|
end
|
@@ -48,6 +48,36 @@ RSpec.describe 'Macros #maybe' do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
describe 'with an optional key and a block with schema' do
|
52
|
+
subject(:schema) do
|
53
|
+
Dry::Validation.Schema do
|
54
|
+
optional(:employee).maybe do
|
55
|
+
schema do
|
56
|
+
required(:id).filled(:str?)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'passes when input is valid' do
|
63
|
+
expect(schema.(employee: { id: '1' })).to be_success
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'passes when key is missing' do
|
67
|
+
expect(schema.({})).to be_success
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'passes when value is nil' do
|
71
|
+
expect(schema.(employee: nil)).to be_success
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'fails when value for nested schema is invalid' do
|
75
|
+
expect(schema.(employee: { id: 1 }).messages).to eql(
|
76
|
+
employee: { id: ['must be a string'] }
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
51
81
|
describe 'with a predicate and a block' do
|
52
82
|
subject(:schema) do
|
53
83
|
Dry::Validation.Schema do
|