dry-validation 0.6.0 → 0.7.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/.codeclimate.yml +1 -0
- data/.travis.yml +3 -2
- data/CHANGELOG.md +42 -0
- data/Gemfile +8 -1
- data/README.md +13 -89
- data/config/errors.yml +35 -29
- data/dry-validation.gemspec +2 -2
- data/examples/basic.rb +3 -7
- data/examples/each.rb +3 -8
- data/examples/form.rb +3 -6
- data/examples/nested.rb +7 -15
- data/lib/dry/validation.rb +33 -5
- data/lib/dry/validation/error.rb +10 -26
- data/lib/dry/validation/error_compiler.rb +69 -99
- data/lib/dry/validation/error_compiler/input.rb +148 -0
- data/lib/dry/validation/hint_compiler.rb +83 -33
- data/lib/dry/validation/input_processor_compiler.rb +98 -0
- data/lib/dry/validation/input_processor_compiler/form.rb +46 -0
- data/lib/dry/validation/input_processor_compiler/sanitizer.rb +46 -0
- data/lib/dry/validation/messages/abstract.rb +30 -10
- data/lib/dry/validation/messages/i18n.rb +2 -1
- data/lib/dry/validation/messages/namespaced.rb +1 -0
- data/lib/dry/validation/messages/yaml.rb +8 -5
- data/lib/dry/validation/result.rb +33 -25
- data/lib/dry/validation/schema.rb +168 -61
- data/lib/dry/validation/schema/attr.rb +5 -27
- data/lib/dry/validation/schema/check.rb +24 -0
- data/lib/dry/validation/schema/dsl.rb +97 -0
- data/lib/dry/validation/schema/form.rb +2 -26
- data/lib/dry/validation/schema/key.rb +32 -28
- data/lib/dry/validation/schema/rule.rb +88 -32
- data/lib/dry/validation/schema/value.rb +77 -27
- data/lib/dry/validation/schema_compiler.rb +38 -0
- data/lib/dry/validation/version.rb +1 -1
- data/spec/fixtures/locales/pl.yml +1 -1
- data/spec/integration/attr_spec.rb +122 -0
- data/spec/integration/custom_error_messages_spec.rb +9 -11
- data/spec/integration/custom_predicates_spec.rb +68 -18
- data/spec/integration/error_compiler_spec.rb +259 -65
- data/spec/integration/hints_spec.rb +28 -9
- data/spec/integration/injecting_rules_spec.rb +11 -12
- data/spec/integration/localized_error_messages_spec.rb +16 -16
- data/spec/integration/messages/i18n_spec.rb +9 -5
- data/spec/integration/optional_keys_spec.rb +9 -11
- data/spec/integration/schema/array_schema_spec.rb +23 -0
- data/spec/integration/schema/check_rules_spec.rb +39 -31
- data/spec/integration/schema/check_with_nth_el_spec.rb +25 -0
- data/spec/integration/schema/each_with_set_spec.rb +23 -24
- data/spec/integration/schema/form_spec.rb +122 -0
- data/spec/integration/schema/inheriting_schema_spec.rb +31 -0
- data/spec/integration/schema/input_processor_spec.rb +46 -0
- data/spec/integration/schema/macros/confirmation_spec.rb +33 -0
- data/spec/integration/schema/macros/maybe_spec.rb +32 -0
- data/spec/integration/schema/macros/required_spec.rb +59 -0
- data/spec/integration/schema/macros/when_spec.rb +65 -0
- data/spec/integration/schema/nested_values_spec.rb +41 -0
- data/spec/integration/schema/not_spec.rb +14 -14
- data/spec/integration/schema/option_with_default_spec.rb +30 -0
- data/spec/integration/schema/reusing_schema_spec.rb +33 -0
- data/spec/integration/schema/using_types_spec.rb +29 -0
- data/spec/integration/schema/xor_spec.rb +17 -14
- data/spec/integration/schema_spec.rb +75 -245
- data/spec/shared/rule_compiler.rb +8 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/unit/hint_compiler_spec.rb +10 -10
- data/spec/unit/{input_type_compiler_spec.rb → input_processor_compiler/form_spec.rb} +88 -73
- data/spec/unit/schema/key_spec.rb +33 -0
- data/spec/unit/schema/rule_spec.rb +7 -6
- data/spec/unit/schema/value_spec.rb +187 -54
- metadata +53 -31
- data/.rubocop.yml +0 -16
- data/.rubocop_todo.yml +0 -7
- data/lib/dry/validation/input_type_compiler.rb +0 -83
- data/lib/dry/validation/schema/definition.rb +0 -74
- data/lib/dry/validation/schema/result.rb +0 -68
- data/rakelib/rubocop.rake +0 -18
- data/spec/integration/rule_groups_spec.rb +0 -94
- data/spec/integration/schema/attrs_spec.rb +0 -38
- data/spec/integration/schema/default_key_behavior_spec.rb +0 -23
- data/spec/integration/schema/grouped_rules_spec.rb +0 -57
- data/spec/integration/schema/nested_spec.rb +0 -31
- data/spec/integration/schema_form_spec.rb +0 -97
@@ -1,94 +0,0 @@
|
|
1
|
-
RSpec.describe Dry::Validation::Schema do
|
2
|
-
subject(:validation) { schema.new }
|
3
|
-
|
4
|
-
before do
|
5
|
-
def schema.messages
|
6
|
-
Messages.default.merge(
|
7
|
-
en: { errors: { password_confirmation: 'does not match' } }
|
8
|
-
)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe 'defining schema with rule groups' do
|
13
|
-
let(:schema) do
|
14
|
-
Class.new(Dry::Validation::Schema) do
|
15
|
-
confirmation(:password)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#call' do
|
20
|
-
it 'returns empty errors when password matches confirmation' do
|
21
|
-
expect(validation.(password: 'foo', password_confirmation: 'foo')).to be_empty
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'returns error for a failed group rule' do
|
25
|
-
expect(validation.(password: 'foo', password_confirmation: 'bar')).to match_array([
|
26
|
-
[:error, [
|
27
|
-
:input, [
|
28
|
-
:password_confirmation,
|
29
|
-
["foo", "bar"],
|
30
|
-
[[:group, [:password_confirmation, [:predicate, [:eql?, []]]]]]]]
|
31
|
-
]
|
32
|
-
])
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'returns messages for a failed group rule' do
|
36
|
-
expect(validation.(password: 'foo', password_confirmation: 'bar').messages).to eql(
|
37
|
-
password_confirmation: [['does not match'], ['foo', 'bar']]
|
38
|
-
)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'returns errors for the dependent predicates, not the group rule, when any of the dependent predicates fail' do
|
42
|
-
expect(validation.(password: '', password_confirmation: '')).to match_array([
|
43
|
-
[:error, [:input, [:password, "", [[:val, [:password, [:predicate, [:filled?, []]]]]]]]],
|
44
|
-
[:error, [:input, [:password_confirmation, "", [[:val, [:password_confirmation, [:predicate, [:filled?, []]]]]]]]]
|
45
|
-
])
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
describe 'confirmation' do
|
50
|
-
shared_examples_for 'confirmation behavior' do
|
51
|
-
it 'applies custom rules' do
|
52
|
-
expect(validation.(password: 'abcd').messages).to include(
|
53
|
-
password: [['password size cannot be less than 6'], 'abcd']
|
54
|
-
)
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'applies confirmation equality predicate' do
|
58
|
-
expect(validation.(password: 'abcdef', password_confirmation: 'abcd').messages).to include(
|
59
|
-
password_confirmation: [['does not match'], ['abcdef', 'abcd']]
|
60
|
-
)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'skips default predicate' do
|
64
|
-
expect(validation.(password: '', password_confirmation: '').messages).to include(
|
65
|
-
password: [['password size cannot be less than 6'], ''],
|
66
|
-
password_confirmation: [['password_confirmation must be filled'], '']
|
67
|
-
)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
describe 'custom predicates' do
|
72
|
-
let(:schema) do
|
73
|
-
Class.new(Dry::Validation::Schema) do
|
74
|
-
key(:password) { |value| value.min_size?(6) }
|
75
|
-
|
76
|
-
confirmation(:password)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
it_behaves_like 'confirmation behavior'
|
81
|
-
end
|
82
|
-
|
83
|
-
describe 'custom predicates using shortcut options' do
|
84
|
-
let(:schema) do
|
85
|
-
Class.new(Dry::Validation::Schema) do
|
86
|
-
confirmation(:password, min_size: 6)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
it_behaves_like 'confirmation behavior'
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
|
-
RSpec.describe Schema, 'defining schema with attrs' do
|
4
|
-
subject(:validation) { schema.new }
|
5
|
-
|
6
|
-
let(:schema) do
|
7
|
-
Class.new(Dry::Validation::Schema) do
|
8
|
-
attr(:email) { |email| email.filled? }
|
9
|
-
|
10
|
-
attr(:address) do |address|
|
11
|
-
address.attr(:city, &:filled?)
|
12
|
-
address.attr(:street, &:filled?)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '#call' do
|
18
|
-
context 'when valid input' do
|
19
|
-
let(:input) do
|
20
|
-
struct_from_hash(email: "email@test.com", address: { city: 'NYC', street: 'Street 1/2' })
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'should be valid' do
|
24
|
-
expect(validation.(input)).to be_empty
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'when input does not have proper attributes' do
|
29
|
-
let(:input) do
|
30
|
-
struct_from_hash(name: "John", address: { country: 'US', street: 'Street 1/2' })
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'should not be valid' do
|
34
|
-
expect(validation.(input)).to_not be_empty
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
RSpec.describe 'Schema::Form / Default key behavior' do
|
2
|
-
subject(:validate) { schema.new }
|
3
|
-
|
4
|
-
let(:schema) do
|
5
|
-
Class.new(Dry::Validation::Schema::Form) do
|
6
|
-
key(:name)
|
7
|
-
key(:age, &:int?)
|
8
|
-
optional(:address)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'applies filled? predicate by default' do
|
13
|
-
expect(validate.('name' => 'jane', 'age' => '21').params).to eql(
|
14
|
-
name: 'jane', age: 21
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'applies filled? predicate by default to optional key' do
|
19
|
-
expect(validate.('name' => 'jane', 'age' => '21', 'address' => 'Earth').params).to eql(
|
20
|
-
name: 'jane', age: 21, address: 'Earth'
|
21
|
-
)
|
22
|
-
end
|
23
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
RSpec.describe Schema, 'using high-level grouped rules' do
|
2
|
-
subject(:validate) { schema.new }
|
3
|
-
|
4
|
-
let(:schema) do
|
5
|
-
Class.new(Schema) do
|
6
|
-
def self.messages
|
7
|
-
Messages.default.merge(
|
8
|
-
en: {
|
9
|
-
errors: {
|
10
|
-
email: {
|
11
|
-
absence: 'email must not be selected',
|
12
|
-
presence: 'email must be selected',
|
13
|
-
format: 'this is not an email lol',
|
14
|
-
inclusion: 'sorry, did not expect this lol'
|
15
|
-
}
|
16
|
-
}
|
17
|
-
}
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
key(:email) { |email| email.none? | email.filled? }
|
22
|
-
key(:login) { |login| login.bool? }
|
23
|
-
|
24
|
-
rule(email: :absence) do
|
25
|
-
value(:login).false? > value(:email).none?
|
26
|
-
end
|
27
|
-
|
28
|
-
rule(email: :presence) do
|
29
|
-
value(:login).true? > value(:email).filled?
|
30
|
-
end
|
31
|
-
|
32
|
-
rule(email: :format) do
|
33
|
-
value(:email).filled? > value(:email).format?(/[a-z]@[a-z]/)
|
34
|
-
end
|
35
|
-
|
36
|
-
rule(email: :inclusion) do
|
37
|
-
value(:email).filled? > value(:email).inclusion?(%w[jane@doe])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'passes when login is true and email is present' do
|
43
|
-
expect(validate.(login: true, email: 'jane@doe').messages).to be_empty
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'fails when login is false and email is not present' do
|
47
|
-
expect(validate.(login: true, email: nil).messages).to_not be_empty
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'provides merged error messages' do
|
51
|
-
expect(validate.(login: true, email: 'not-an-email-lol').messages).to eql(
|
52
|
-
email: [
|
53
|
-
["sorry, did not expect this lol", "this is not an email lol"], nil
|
54
|
-
]
|
55
|
-
)
|
56
|
-
end
|
57
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
RSpec.describe Schema, 'using nested schemas' do
|
2
|
-
subject(:validate) { schema.new }
|
3
|
-
|
4
|
-
let(:schema) do
|
5
|
-
Class.new(Schema) do
|
6
|
-
schema(:location) do |loc|
|
7
|
-
loc.key(:lat, &:filled?)
|
8
|
-
loc.key(:lng, &:filled?)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'passes when location has lat and lng filled' do
|
14
|
-
expect(validate.(location: { lat: 1.23, lng: 4.56 })).to be_empty
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'fails when location has missing lat' do
|
18
|
-
expect(validate.(location: { lng: 4.56 })).to match_array([
|
19
|
-
[
|
20
|
-
:error, [
|
21
|
-
:input, [
|
22
|
-
:location, { lng: 4.56 },
|
23
|
-
[
|
24
|
-
[:input, [:lat, nil, [[:key, [:lat, [:predicate, [:key?, [:lat]]]]]]]]
|
25
|
-
]
|
26
|
-
]
|
27
|
-
]
|
28
|
-
]
|
29
|
-
])
|
30
|
-
end
|
31
|
-
end
|
@@ -1,97 +0,0 @@
|
|
1
|
-
RSpec.describe Dry::Validation::Schema::Form do
|
2
|
-
subject(:validation) { schema.new }
|
3
|
-
|
4
|
-
describe 'defining schema' do
|
5
|
-
let(:schema) do
|
6
|
-
Class.new(Dry::Validation::Schema::Form) do
|
7
|
-
key(:email) { |email| email.filled? }
|
8
|
-
|
9
|
-
key(:age) { |age| age.none? | (age.int? & age.gt?(18)) }
|
10
|
-
|
11
|
-
key(:address) do |address|
|
12
|
-
address.hash? do
|
13
|
-
address.key(:city, &:filled?)
|
14
|
-
address.key(:street, &:filled?)
|
15
|
-
|
16
|
-
address.key(:loc) do |loc|
|
17
|
-
loc.key(:lat) { |lat| lat.filled? & lat.float? }
|
18
|
-
loc.key(:lng) { |lng| lng.filled? & lng.float? }
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
optional(:phone_number) do |phone_number|
|
24
|
-
phone_number.none? | (phone_number.int? & phone_number.gt?(0))
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '#messages' do
|
30
|
-
it 'returns compiled error messages' do
|
31
|
-
result = validation.('email' => '', 'age' => '19')
|
32
|
-
|
33
|
-
expect(result.messages).to match_array([
|
34
|
-
[:email, [['email must be filled'], '']],
|
35
|
-
[:address, [['address is missing'], nil]]
|
36
|
-
])
|
37
|
-
|
38
|
-
expect(result.params).to eql(email: '', age: 19)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '#call' do
|
43
|
-
it 'passes when attributes are valid' do
|
44
|
-
result = validation.(
|
45
|
-
'email' => 'jane@doe.org',
|
46
|
-
'age' => '19',
|
47
|
-
'address' => {
|
48
|
-
'city' => 'NYC',
|
49
|
-
'street' => 'Street 1/2',
|
50
|
-
'loc' => { 'lat' => '123.456', 'lng' => '456.123' }
|
51
|
-
}
|
52
|
-
)
|
53
|
-
|
54
|
-
expect(result).to be_empty
|
55
|
-
|
56
|
-
expect(result.params).to eql(
|
57
|
-
email: 'jane@doe.org', age: 19,
|
58
|
-
address: {
|
59
|
-
city: 'NYC', street: 'Street 1/2',
|
60
|
-
loc: { lat: 123.456, lng: 456.123 }
|
61
|
-
}
|
62
|
-
)
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'validates presence of an email and min age value' do
|
66
|
-
expect(validation.('email' => '', 'age' => '18')).to match_array([
|
67
|
-
[:error, [:input, [:age, 18, [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]],
|
68
|
-
[:error, [:input, [:email, "", [[:val, [:email, [:predicate, [:filled?, []]]]]]]]],
|
69
|
-
[:error, [:input, [:address, nil, [[:key, [:address, [:predicate, [:key?, [:address]]]]]]]]]
|
70
|
-
])
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'handles optionals' do
|
74
|
-
result = validation.(
|
75
|
-
'email' => 'jane@doe.org',
|
76
|
-
'age' => '19',
|
77
|
-
'phone_number' => '12',
|
78
|
-
'address' => {
|
79
|
-
'city' => 'NYC',
|
80
|
-
'street' => 'Street 1/2',
|
81
|
-
'loc' => { 'lat' => '123.456', 'lng' => '456.123' }
|
82
|
-
}
|
83
|
-
)
|
84
|
-
|
85
|
-
expect(result).to be_empty
|
86
|
-
|
87
|
-
expect(result.params).to eql(
|
88
|
-
email: 'jane@doe.org', age: 19, phone_number: 12,
|
89
|
-
address: {
|
90
|
-
city: 'NYC', street: 'Street 1/2',
|
91
|
-
loc: { lat: 123.456, lng: 456.123 }
|
92
|
-
}
|
93
|
-
)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|