dry-validation 0.8.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|