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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +39 -1
  4. data/benchmarks/benchmark_schema_invalid_huge.rb +52 -0
  5. data/benchmarks/profile_schema_huge_invalid.rb +30 -0
  6. data/config/errors.yml +3 -2
  7. data/dry-validation.gemspec +2 -2
  8. data/lib/dry/validation.rb +20 -32
  9. data/lib/dry/validation/constants.rb +6 -0
  10. data/lib/dry/validation/error.rb +5 -2
  11. data/lib/dry/validation/error_compiler.rb +46 -116
  12. data/lib/dry/validation/executor.rb +105 -0
  13. data/lib/dry/validation/hint_compiler.rb +36 -68
  14. data/lib/dry/validation/message.rb +86 -0
  15. data/lib/dry/validation/message_compiler.rb +141 -0
  16. data/lib/dry/validation/message_set.rb +70 -0
  17. data/lib/dry/validation/messages/abstract.rb +1 -1
  18. data/lib/dry/validation/messages/i18n.rb +5 -0
  19. data/lib/dry/validation/predicate_registry.rb +8 -3
  20. data/lib/dry/validation/result.rb +6 -7
  21. data/lib/dry/validation/schema.rb +21 -227
  22. data/lib/dry/validation/schema/check.rb +1 -1
  23. data/lib/dry/validation/schema/class_interface.rb +193 -0
  24. data/lib/dry/validation/schema/deprecated.rb +1 -2
  25. data/lib/dry/validation/schema/key.rb +4 -0
  26. data/lib/dry/validation/schema/value.rb +12 -7
  27. data/lib/dry/validation/schema_compiler.rb +20 -1
  28. data/lib/dry/validation/type_specs.rb +70 -0
  29. data/lib/dry/validation/version.rb +1 -1
  30. data/spec/fixtures/locales/pl.yml +1 -1
  31. data/spec/integration/custom_predicates_spec.rb +37 -0
  32. data/spec/integration/error_compiler_spec.rb +39 -39
  33. data/spec/integration/form/predicates/key_spec.rb +10 -18
  34. data/spec/integration/form/predicates/size/fixed_spec.rb +8 -12
  35. data/spec/integration/form/predicates/size/range_spec.rb +7 -7
  36. data/spec/integration/hints_spec.rb +17 -0
  37. data/spec/integration/messages/i18n_spec.rb +2 -2
  38. data/spec/integration/schema/check_rules_spec.rb +2 -2
  39. data/spec/integration/schema/defining_base_schema_spec.rb +38 -0
  40. data/spec/integration/schema/dynamic_predicate_args_spec.rb +18 -0
  41. data/spec/integration/schema/macros/each_spec.rb +2 -2
  42. data/spec/integration/schema/macros/input_spec.rb +102 -10
  43. data/spec/integration/schema/macros/maybe_spec.rb +30 -0
  44. data/spec/integration/schema/nested_schemas_spec.rb +200 -0
  45. data/spec/integration/schema/nested_values_spec.rb +3 -1
  46. data/spec/integration/schema/option_with_default_spec.rb +54 -20
  47. data/spec/integration/schema/predicates/size/fixed_spec.rb +10 -10
  48. data/spec/integration/schema/predicates/size/range_spec.rb +8 -10
  49. data/spec/unit/error_compiler_spec.rb +1 -1
  50. data/spec/unit/hint_compiler_spec.rb +2 -2
  51. metadata +18 -7
  52. data/examples/rule_ast.rb +0 -25
  53. 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
- xit "should raise error" do
6
- expect { Dry::Validation.Form do
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
- subject(:schema) do
14
- xit "should raise error" do
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
- xit "should raise error" do
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
- xit "should raise error" do
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
- xit "should raise error" do
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
- xit "should raise error" do
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
- xit "should raise error" do
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
- xit "should raise error" do
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
187
- xit 'is not successful' do
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
335
- xit 'is not successful' do
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
- xit 'is not successful' do
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
- xit 'is not successful' do
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
186
- xit 'is not successful' do
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
- xit 'is not successful' do
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
- #see: https://github.com/dry-rb/dry-validation/issues/121
334
- xit 'is not successful' do
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 %{left} - %{right}")
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 %{left} a %{right}")
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[:destiny]).to eql(
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({0=>["size must be 3"]})
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", "size must be 3"]
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
- subject(:schema) do
3
- Dry::Validation.Schema do
4
- input :hash?
2
+ context 'with a flat schema' do
3
+ subject(:schema) do
4
+ Dry::Validation.Schema do
5
+ input :hash?
5
6
 
6
- required(:foo).filled
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
- it 'passes when input is valid' do
11
- expect(schema.(foo: "bar")).to be_successful
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
- it 'prepends a rule for the input' do
15
- expect(schema.(nil).messages).to eql(["must be a hash"])
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
- it 'applies other rules when input has expected type' do
19
- expect(schema.(foo: '').messages).to eql(foo: ["must be filled"])
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