dry-validation 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'dry/logic/rule_compiler'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Validation
|
5
|
+
class Guard
|
6
|
+
attr_reader :rule, :deps
|
7
|
+
|
8
|
+
def initialize(rule, deps)
|
9
|
+
@rule = rule
|
10
|
+
@deps = deps
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(input, results)
|
14
|
+
rule.(input) if deps_valid?(results)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def deps_valid?(results)
|
20
|
+
deps.all? do |path|
|
21
|
+
result = Array(path).reduce(results) { |a, e| a[e] }
|
22
|
+
result.success? if result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class SchemaCompiler < Logic::RuleCompiler
|
28
|
+
def visit_schema(klass)
|
29
|
+
klass.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit_guard(node)
|
33
|
+
deps, other = node
|
34
|
+
Guard.new(visit(other), deps)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
RSpec.describe Dry::Validation::Schema, 'defining attr-based schema' do
|
2
|
+
describe 'with a flat structure' do
|
3
|
+
subject(:schema) do
|
4
|
+
Dry::Validation.Schema do
|
5
|
+
attr(:email).required
|
6
|
+
attr(:age) { none? | (int? & gt?(18)) }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:model) { Class.new(OpenStruct) }
|
11
|
+
|
12
|
+
it 'passes when input is valid' do
|
13
|
+
expect(schema.(model.new(email: 'jane@doe', age: 19))).to be_success
|
14
|
+
expect(schema.(model.new(email: 'jane@doe', age: nil))).to be_success
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'fails when input is not valid' do
|
18
|
+
expect(schema.(model.new(email: 'jane@doe', age: 17))).to_not be_success
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'with nested structures' do
|
23
|
+
subject(:schema) do
|
24
|
+
Dry::Validation.Schema do
|
25
|
+
attr(:email).required
|
26
|
+
|
27
|
+
attr(:age) { none? | (int? & gt?(18)) }
|
28
|
+
|
29
|
+
attr(:address) do
|
30
|
+
attr(:city) { min_size?(3) }
|
31
|
+
|
32
|
+
attr(:street).required
|
33
|
+
|
34
|
+
attr(:country) do
|
35
|
+
attr(:name).required
|
36
|
+
attr(:code).required
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr(:phone_numbers) do
|
41
|
+
array? { each(&:str?) }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:input) do
|
47
|
+
OpenStruct.new(
|
48
|
+
email: 'jane@doe.org',
|
49
|
+
age: 19,
|
50
|
+
address: OpenStruct.new(
|
51
|
+
city: 'NYC', street: 'Street 1/2', country: OpenStruct.new(code: 'US', name: 'USA')
|
52
|
+
),
|
53
|
+
phone_numbers: [
|
54
|
+
'123456', '234567'
|
55
|
+
]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#messages' do
|
60
|
+
it 'returns compiled error messages' do
|
61
|
+
input.email = ''
|
62
|
+
|
63
|
+
expect(schema.(input).messages).to eql(
|
64
|
+
email: ['must be filled']
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#call' do
|
70
|
+
it 'passes when attributes are valid' do
|
71
|
+
expect(schema.(input)).to be_success
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'validates presence of an email and min age value' do
|
75
|
+
input.email = ''
|
76
|
+
input.age = 18
|
77
|
+
|
78
|
+
expect(schema.(input).messages).to eql(
|
79
|
+
email: ['must be filled'], age: ['must be greater than 18']
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'validates type of age value' do
|
84
|
+
input.age = '18'
|
85
|
+
|
86
|
+
expect(schema.(input).messages).to eql(
|
87
|
+
age: ['must be an integer', 'must be greater than 18']
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'validates presence of phone_number keys' do
|
92
|
+
input.phone_numbers = nil
|
93
|
+
|
94
|
+
expect(schema.(input).messages).to eql(
|
95
|
+
phone_numbers: ['must be an array']
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'validates presence of address street & county & min size of the city' do
|
100
|
+
input.address.city = 'NY'
|
101
|
+
input.address.street = nil
|
102
|
+
input.address.country.name = nil
|
103
|
+
|
104
|
+
expect(schema.(input).messages).to eql(
|
105
|
+
address: {
|
106
|
+
street: ['must be filled'],
|
107
|
+
country: { name: ['must be filled'] },
|
108
|
+
city: ['size cannot be less than 3']
|
109
|
+
}
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'validates each phone number' do
|
114
|
+
input.phone_numbers = ['123', 312]
|
115
|
+
|
116
|
+
expect(schema.(input).messages).to eql(
|
117
|
+
phone_numbers: { 1 => ['must be a string'] }
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,22 +1,20 @@
|
|
1
1
|
require 'dry/validation/messages/i18n'
|
2
2
|
|
3
3
|
RSpec.describe Dry::Validation do
|
4
|
-
subject(:validation) { schema.new }
|
5
|
-
|
6
4
|
shared_context 'schema with customized messages' do
|
7
5
|
describe '#messages' do
|
8
6
|
it 'returns compiled error messages' do
|
9
|
-
expect(
|
10
|
-
|
11
|
-
|
7
|
+
expect(schema.(email: '').messages).to eql(
|
8
|
+
email: ['Please provide your email']
|
9
|
+
)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
15
13
|
|
16
14
|
context 'yaml' do
|
17
|
-
|
18
|
-
|
19
|
-
configure do
|
15
|
+
subject(:schema) do
|
16
|
+
Dry::Validation.Schema do
|
17
|
+
configure do
|
20
18
|
config.messages_file = SPEC_ROOT.join('fixtures/locales/en.yml')
|
21
19
|
end
|
22
20
|
|
@@ -34,9 +32,9 @@ RSpec.describe Dry::Validation do
|
|
34
32
|
I18n.backend.load_translations
|
35
33
|
end
|
36
34
|
|
37
|
-
|
38
|
-
|
39
|
-
configure do
|
35
|
+
subject(:schema) do
|
36
|
+
Dry::Validation.Schema do
|
37
|
+
configure do
|
40
38
|
config.messages = :i18n
|
41
39
|
end
|
42
40
|
|
@@ -1,28 +1,36 @@
|
|
1
1
|
RSpec.describe Dry::Validation do
|
2
|
-
subject(:validation) { schema.new }
|
3
|
-
|
4
2
|
shared_context 'uses custom predicates' do
|
5
3
|
it 'uses provided custom predicates' do
|
6
|
-
expect(
|
4
|
+
expect(schema.(email: 'jane@doe')).to be_success
|
7
5
|
|
8
|
-
expect(
|
9
|
-
|
10
|
-
|
6
|
+
expect(schema.(email: nil).messages).to eql(
|
7
|
+
email: ['must be filled', 'must be a valid email']
|
8
|
+
)
|
11
9
|
|
12
|
-
expect(
|
13
|
-
|
14
|
-
|
10
|
+
expect(schema.(email: 'jane').messages).to eql(
|
11
|
+
email: ['must be a valid email']
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:base_class) do
|
17
|
+
Class.new(Dry::Validation::Schema) do
|
18
|
+
def self.messages
|
19
|
+
Dry::Validation::Messages.default.merge(
|
20
|
+
en: { errors: { email?: 'must be a valid email' } }
|
21
|
+
)
|
22
|
+
end
|
15
23
|
end
|
16
24
|
end
|
17
25
|
|
18
26
|
describe 'defining schema with custom predicates container' do
|
19
|
-
|
20
|
-
|
21
|
-
configure do
|
27
|
+
subject(:schema) do
|
28
|
+
Dry::Validation.Schema(base_class) do
|
29
|
+
configure do
|
22
30
|
config.predicates = Test::Predicates
|
23
31
|
end
|
24
32
|
|
25
|
-
key(:email) {
|
33
|
+
key(:email) { filled? & email? }
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
@@ -42,16 +50,58 @@ RSpec.describe Dry::Validation do
|
|
42
50
|
end
|
43
51
|
|
44
52
|
describe 'defining schema with custom predicate methods' do
|
45
|
-
|
46
|
-
|
47
|
-
|
53
|
+
subject(:schema) do
|
54
|
+
Dry::Validation.Schema(base_class) do
|
55
|
+
configure do
|
56
|
+
def email?(value)
|
57
|
+
value.include?('@')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
key(:email) { filled? & email? }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
include_context 'uses custom predicates'
|
66
|
+
end
|
48
67
|
|
68
|
+
describe 'custom predicate which requires an arbitrary dependency' do
|
69
|
+
subject(:schema) do
|
70
|
+
Dry::Validation.Schema(base_class) do
|
71
|
+
key(:email).required(:email?)
|
72
|
+
|
73
|
+
configure do
|
74
|
+
option :email_check
|
75
|
+
|
76
|
+
def email?(value)
|
77
|
+
email_check.(value)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'uses injected dependency for the custom predicate' do
|
84
|
+
email_check = -> input { input.include?('@') }
|
85
|
+
|
86
|
+
expect(schema.with(email_check: email_check).(email: 'foo').messages).to eql(
|
87
|
+
email: ['must be a valid email']
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'raises an error when message is missing' do
|
93
|
+
schema = Dry::Validation.Schema do
|
94
|
+
configure do
|
49
95
|
def email?(value)
|
50
|
-
|
96
|
+
false
|
51
97
|
end
|
52
98
|
end
|
99
|
+
|
100
|
+
key(:email).required(:email?)
|
53
101
|
end
|
54
102
|
|
55
|
-
|
103
|
+
expect { schema.(email: 'foo').messages }.to raise_error(
|
104
|
+
Dry::Validation::MissingMessageError, /email/
|
105
|
+
)
|
56
106
|
end
|
57
107
|
end
|
@@ -9,239 +9,433 @@ RSpec.describe Dry::Validation::ErrorCompiler do
|
|
9
9
|
en: {
|
10
10
|
errors: {
|
11
11
|
key?: '+%{name}+ key is missing in the hash',
|
12
|
-
attr?: 'Object does not respond to the +%{name}+ attr',
|
13
12
|
rules: {
|
14
13
|
address: {
|
15
14
|
filled?: 'Please provide your address'
|
16
15
|
}
|
17
16
|
}
|
18
17
|
}
|
18
|
+
},
|
19
|
+
pl: {
|
20
|
+
rules: {
|
21
|
+
email: 'adres email'
|
22
|
+
},
|
23
|
+
errors: {
|
24
|
+
email?: 'nie jest poprawny'
|
25
|
+
}
|
19
26
|
}
|
20
27
|
)
|
21
28
|
end
|
22
29
|
|
23
|
-
describe '#call' do
|
30
|
+
describe '#call with flat inputs' do
|
24
31
|
let(:ast) do
|
25
32
|
[
|
26
|
-
[:error, [:input, [:name,
|
27
|
-
[:error, [:
|
28
|
-
[:error, [:
|
29
|
-
[:error, [:input, [:
|
30
|
-
[:error, [:input, [:address, "", [[:val, [:address, [:predicate, [:filled?, []]]]]]]]]
|
33
|
+
[:error, [:name, [:input, [:name, [:result, [nil, [:val, [:predicate, [:key?, [:name]]]]]]]]]],
|
34
|
+
[:error, [:age, [:input, [:age, [:result, [18, [:val, [:predicate, [:gt?, [18]]]]]]]]]],
|
35
|
+
[:error, [:email, [:input, [:email, [:result, ["", [:val, [:predicate, [:filled?, []]]]]]]]]],
|
36
|
+
[:error, [:address, [:input, [:address, [:result, ["", [:val, [:predicate, [:filled?, []]]]]]]]]]
|
31
37
|
]
|
32
38
|
end
|
33
39
|
|
34
40
|
it 'converts error ast into another format' do
|
35
41
|
expect(error_compiler.(ast)).to eql(
|
36
|
-
name: [
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
address: [["Please provide your address"], '']
|
42
|
+
name: ["+name+ key is missing in the hash"],
|
43
|
+
age: ["must be greater than 18"],
|
44
|
+
email: ["must be filled"],
|
45
|
+
address: ["Please provide your address"]
|
41
46
|
)
|
42
47
|
end
|
43
48
|
end
|
44
49
|
|
45
|
-
describe '#
|
50
|
+
describe '#call with check errors' do
|
51
|
+
let(:ast) do
|
52
|
+
[[:error, [:newsletter, [
|
53
|
+
:input, [[:settings, :newsletter], [
|
54
|
+
:result, [
|
55
|
+
[true, true],
|
56
|
+
[
|
57
|
+
:check, [
|
58
|
+
:newsletter,
|
59
|
+
[:implication, [
|
60
|
+
[:key, [[:settings, :offers], [:predicate, [:true?, []]]]],
|
61
|
+
[:key, [[:settings, :newsletter], [:predicate, [:false?, []]]]]]
|
62
|
+
]
|
63
|
+
]
|
64
|
+
]
|
65
|
+
]
|
66
|
+
]
|
67
|
+
]]]
|
68
|
+
]]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'converts error ast into another format' do
|
72
|
+
expect(error_compiler.(ast)).to eql(
|
73
|
+
settings: { newsletter: ['must be false'] }
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#call with arr inputs' do
|
79
|
+
let(:ast) do
|
80
|
+
[[:error, [:payments,
|
81
|
+
[:input, [
|
82
|
+
:payments, [:result, [
|
83
|
+
[{ method: "cc", amount: 1.23 }, { amount: 4.56 }], [:each, [
|
84
|
+
[:el, [
|
85
|
+
1, [
|
86
|
+
:result, [{ amount: 4.56 }, [:val, [:predicate, [:key?, [:method]]]]]
|
87
|
+
]
|
88
|
+
]]
|
89
|
+
]]]
|
90
|
+
]]]
|
91
|
+
]]]
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'converts error ast into another format' do
|
95
|
+
expect(error_compiler.(ast)).to eql(
|
96
|
+
payments: { 1 => { method: ['+method+ key is missing in the hash'] } }
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#visit with an :input node' do
|
102
|
+
context 'full message' do
|
103
|
+
it 'returns full message including rule name' do
|
104
|
+
msg = error_compiler.with(full: true).visit(
|
105
|
+
[:input, [:num, [
|
106
|
+
:result, ['2', [:val, [:predicate, [:int?, []]]]]]
|
107
|
+
]]
|
108
|
+
)
|
109
|
+
|
110
|
+
expect(msg).to eql(num: ['num must be an integer'])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'rule name translations' do
|
115
|
+
it 'translates rule name and its message' do
|
116
|
+
msg = error_compiler.with(locale: :pl, full: true).visit(
|
117
|
+
[:input, [:email, [
|
118
|
+
:result, ['oops', [:val, [:predicate, [:email?, []]]]]]
|
119
|
+
]]
|
120
|
+
)
|
121
|
+
|
122
|
+
expect(msg).to eql(email: ['adres email nie jest poprawny'])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
46
126
|
describe ':empty?' do
|
47
127
|
it 'returns valid message' do
|
48
|
-
msg = error_compiler.
|
128
|
+
msg = error_compiler.visit(
|
129
|
+
[:input, [:tags, [:result, [nil, [:val, [:predicate, [:empty?, []]]]]]]]
|
130
|
+
)
|
49
131
|
|
50
|
-
expect(msg).to eql(
|
132
|
+
expect(msg).to eql(tags: ['cannot be empty'])
|
51
133
|
end
|
52
134
|
end
|
53
135
|
|
54
136
|
describe ':exclusion?' do
|
55
137
|
it 'returns valid message' do
|
56
|
-
msg = error_compiler.
|
138
|
+
msg = error_compiler.visit(
|
139
|
+
[:input, [:num, [
|
140
|
+
:result, [2, [:val, [:predicate, [:exclusion?, [[1, 2, 3]]]]]]]
|
141
|
+
]]
|
142
|
+
)
|
57
143
|
|
58
|
-
expect(msg).to eql(
|
144
|
+
expect(msg).to eql(num: ['must not be one of: 1, 2, 3'])
|
59
145
|
end
|
60
146
|
end
|
61
147
|
|
62
148
|
describe ':inclusion?' do
|
63
149
|
it 'returns valid message' do
|
64
|
-
msg = error_compiler.
|
150
|
+
msg = error_compiler.visit(
|
151
|
+
[:input, [:num, [
|
152
|
+
:result, [2, [:val, [:predicate, [:inclusion?, [[1, 2, 3]]]]]]]
|
153
|
+
]]
|
154
|
+
)
|
65
155
|
|
66
|
-
expect(msg).to eql(
|
156
|
+
expect(msg).to eql(num: ['must be one of: 1, 2, 3'])
|
67
157
|
end
|
68
158
|
end
|
69
159
|
|
70
160
|
describe ':gt?' do
|
71
161
|
it 'returns valid message' do
|
72
|
-
msg = error_compiler.
|
162
|
+
msg = error_compiler.visit(
|
163
|
+
[:input, [:num, [
|
164
|
+
:result, [2, [:val, [:predicate, [:gt?, [3]]]]]]
|
165
|
+
]]
|
166
|
+
)
|
73
167
|
|
74
|
-
expect(msg).to eql(
|
168
|
+
expect(msg).to eql(num: ['must be greater than 3'])
|
75
169
|
end
|
76
170
|
end
|
77
171
|
|
78
172
|
describe ':gteq?' do
|
79
173
|
it 'returns valid message' do
|
80
|
-
msg = error_compiler.
|
174
|
+
msg = error_compiler.visit(
|
175
|
+
[:input, [:num, [
|
176
|
+
:result, [2, [:val, [:predicate, [:gteq?, [3]]]]]]
|
177
|
+
]]
|
178
|
+
)
|
81
179
|
|
82
|
-
expect(msg).to eql(
|
180
|
+
expect(msg).to eql(num: ['must be greater than or equal to 3'])
|
83
181
|
end
|
84
182
|
end
|
85
183
|
|
86
184
|
describe ':lt?' do
|
87
185
|
it 'returns valid message' do
|
88
|
-
msg = error_compiler.
|
186
|
+
msg = error_compiler.visit(
|
187
|
+
[:input, [:num, [
|
188
|
+
:result, [2, [:val, [:predicate, [:lt?, [3]]]]]]
|
189
|
+
]]
|
190
|
+
)
|
89
191
|
|
90
|
-
expect(msg).to eql(
|
192
|
+
expect(msg).to eql(num: ['must be less than 3 (2 was given)'])
|
91
193
|
end
|
92
194
|
end
|
93
195
|
|
94
196
|
describe ':lteq?' do
|
95
197
|
it 'returns valid message' do
|
96
|
-
msg = error_compiler.
|
198
|
+
msg = error_compiler.visit(
|
199
|
+
[:input, [:num, [
|
200
|
+
:result, [2, [:val, [:predicate, [:lteq?, [3]]]]]]
|
201
|
+
]]
|
202
|
+
)
|
97
203
|
|
98
|
-
expect(msg).to eql(
|
204
|
+
expect(msg).to eql(num: ['must be less than or equal to 3'])
|
99
205
|
end
|
100
206
|
end
|
101
207
|
|
102
208
|
describe ':hash?' do
|
103
209
|
it 'returns valid message' do
|
104
|
-
msg = error_compiler.
|
210
|
+
msg = error_compiler.visit(
|
211
|
+
[:input, [:address, [
|
212
|
+
:result, ['', [:val, [:predicate, [:hash?, []]]]]]
|
213
|
+
]]
|
214
|
+
)
|
105
215
|
|
106
|
-
expect(msg).to eql(
|
216
|
+
expect(msg).to eql(address: ['must be a hash'])
|
107
217
|
end
|
108
218
|
end
|
109
219
|
|
110
220
|
describe ':array?' do
|
111
221
|
it 'returns valid message' do
|
112
|
-
msg = error_compiler.
|
222
|
+
msg = error_compiler.visit(
|
223
|
+
[:input, [:phone_numbers, [
|
224
|
+
:result, ['', [:val, [:predicate, [:array?, []]]]]]
|
225
|
+
]]
|
226
|
+
)
|
113
227
|
|
114
|
-
expect(msg).to eql(
|
228
|
+
expect(msg).to eql(phone_numbers: ['must be an array'])
|
115
229
|
end
|
116
230
|
end
|
117
231
|
|
118
232
|
describe ':int?' do
|
119
233
|
it 'returns valid message' do
|
120
|
-
msg = error_compiler.
|
234
|
+
msg = error_compiler.visit(
|
235
|
+
[:input, [:num, [
|
236
|
+
:result, ['2', [:val, [:predicate, [:int?, []]]]]]
|
237
|
+
]]
|
238
|
+
)
|
121
239
|
|
122
|
-
expect(msg).to eql(
|
240
|
+
expect(msg).to eql(num: ['must be an integer'])
|
123
241
|
end
|
124
242
|
end
|
125
243
|
|
126
244
|
describe ':float?' do
|
127
245
|
it 'returns valid message' do
|
128
|
-
msg = error_compiler.
|
246
|
+
msg = error_compiler.visit(
|
247
|
+
[:input, [:num, [
|
248
|
+
:result, ['2', [:val, [:predicate, [:float?, []]]]]]
|
249
|
+
]]
|
250
|
+
)
|
129
251
|
|
130
|
-
expect(msg).to eql(
|
252
|
+
expect(msg).to eql(num: ['must be a float'])
|
131
253
|
end
|
132
254
|
end
|
133
255
|
|
134
256
|
describe ':decimal?' do
|
135
257
|
it 'returns valid message' do
|
136
|
-
msg = error_compiler.
|
258
|
+
msg = error_compiler.visit(
|
259
|
+
[:input, [:num, [
|
260
|
+
:result, ['2', [:val, [:predicate, [:decimal?, []]]]]]
|
261
|
+
]]
|
262
|
+
)
|
137
263
|
|
138
|
-
expect(msg).to eql(
|
264
|
+
expect(msg).to eql(num: ['must be a decimal'])
|
139
265
|
end
|
140
266
|
end
|
141
267
|
|
142
268
|
describe ':date?' do
|
143
269
|
it 'returns valid message' do
|
144
|
-
msg = error_compiler.
|
270
|
+
msg = error_compiler.visit(
|
271
|
+
[:input, [:num, [
|
272
|
+
:result, ['2', [:val, [:predicate, [:date?, []]]]]]
|
273
|
+
]]
|
274
|
+
)
|
145
275
|
|
146
|
-
expect(msg).to eql(
|
276
|
+
expect(msg).to eql(num: ['must be a date'])
|
147
277
|
end
|
148
278
|
end
|
149
279
|
|
150
280
|
describe ':date_time?' do
|
151
281
|
it 'returns valid message' do
|
152
|
-
msg = error_compiler.
|
282
|
+
msg = error_compiler.visit(
|
283
|
+
[:input, [:num, [
|
284
|
+
:result, ['2', [:val, [:predicate, [:date_time?, []]]]]]
|
285
|
+
]]
|
286
|
+
)
|
153
287
|
|
154
|
-
expect(msg).to eql(
|
288
|
+
expect(msg).to eql(num: ['must be a date time'])
|
155
289
|
end
|
156
290
|
end
|
157
291
|
|
158
292
|
describe ':time?' do
|
159
293
|
it 'returns valid message' do
|
160
|
-
msg = error_compiler.
|
294
|
+
msg = error_compiler.visit(
|
295
|
+
[:input, [:num, [
|
296
|
+
:result, ['2', [:val, [:predicate, [:time?, []]]]]]
|
297
|
+
]]
|
298
|
+
)
|
161
299
|
|
162
|
-
expect(msg).to eql(
|
300
|
+
expect(msg).to eql(num: ['must be a time'])
|
163
301
|
end
|
164
302
|
end
|
165
303
|
|
166
304
|
describe ':max_size?' do
|
167
305
|
it 'returns valid message' do
|
168
|
-
msg = error_compiler.
|
306
|
+
msg = error_compiler.visit(
|
307
|
+
[:input, [:num, [
|
308
|
+
:result, ['abcd', [:val, [:predicate, [:max_size?, [3]]]]]]
|
309
|
+
]]
|
310
|
+
)
|
169
311
|
|
170
|
-
expect(msg).to eql(
|
312
|
+
expect(msg).to eql(num: ['size cannot be greater than 3'])
|
171
313
|
end
|
172
314
|
end
|
173
315
|
|
174
316
|
describe ':min_size?' do
|
175
317
|
it 'returns valid message' do
|
176
|
-
msg = error_compiler.
|
318
|
+
msg = error_compiler.visit(
|
319
|
+
[:input, [:num, [
|
320
|
+
:result, ['ab', [:val, [:predicate, [:min_size?, [3]]]]]]
|
321
|
+
]]
|
322
|
+
)
|
177
323
|
|
178
|
-
expect(msg).to eql(
|
324
|
+
expect(msg).to eql(num: ['size cannot be less than 3'])
|
179
325
|
end
|
180
326
|
end
|
181
327
|
|
182
328
|
describe ':none?' do
|
183
329
|
it 'returns valid message' do
|
184
|
-
msg = error_compiler.
|
330
|
+
msg = error_compiler.visit(
|
331
|
+
[:input, [:num, [
|
332
|
+
:result, [nil, [:val, [:predicate, [:none?, []]]]]]
|
333
|
+
]]
|
334
|
+
)
|
185
335
|
|
186
|
-
expect(msg).to eql(
|
336
|
+
expect(msg).to eql(num: ['cannot be defined'])
|
187
337
|
end
|
188
338
|
end
|
189
339
|
|
190
340
|
describe ':size?' do
|
191
341
|
it 'returns valid message when val is array and arg is int' do
|
192
|
-
msg = error_compiler.
|
342
|
+
msg = error_compiler.visit(
|
343
|
+
[:input, [:numbers, [
|
344
|
+
:result, [[1], [:val, [:predicate, [:size?, [3]]]]]]
|
345
|
+
]]
|
346
|
+
)
|
193
347
|
|
194
|
-
expect(msg).to eql(
|
348
|
+
expect(msg).to eql(numbers: ['size must be 3'])
|
195
349
|
end
|
196
350
|
|
197
351
|
it 'returns valid message when val is array and arg is range' do
|
198
|
-
msg = error_compiler.
|
352
|
+
msg = error_compiler.visit(
|
353
|
+
[:input, [:numbers, [
|
354
|
+
:result, [[1], [:val, [:predicate, [:size?, [3..4]]]]]]
|
355
|
+
]]
|
356
|
+
)
|
199
357
|
|
200
|
-
expect(msg).to eql(
|
358
|
+
expect(msg).to eql(numbers: ['size must be within 3 - 4'])
|
201
359
|
end
|
202
360
|
|
203
361
|
it 'returns valid message when arg is int' do
|
204
|
-
msg = error_compiler.
|
362
|
+
msg = error_compiler.visit(
|
363
|
+
[:input, [:num, [
|
364
|
+
:result, ['ab', [:val, [:predicate, [:size?, [3]]]]]]
|
365
|
+
]]
|
366
|
+
)
|
205
367
|
|
206
|
-
expect(msg).to eql(
|
368
|
+
expect(msg).to eql(num: ['length must be 3'])
|
207
369
|
end
|
208
370
|
|
209
371
|
it 'returns valid message when arg is range' do
|
210
|
-
msg = error_compiler.
|
372
|
+
msg = error_compiler.visit(
|
373
|
+
[:input, [:num, [
|
374
|
+
:result, ['ab', [:val, [:predicate, [:size?, [3..4]]]]]]
|
375
|
+
]]
|
376
|
+
)
|
211
377
|
|
212
|
-
expect(msg).to eql(
|
378
|
+
expect(msg).to eql(num: ['length must be within 3 - 4'])
|
213
379
|
end
|
214
380
|
end
|
215
381
|
|
216
382
|
describe ':str?' do
|
217
383
|
it 'returns valid message' do
|
218
|
-
msg = error_compiler.
|
384
|
+
msg = error_compiler.visit(
|
385
|
+
[:input, [:num, [
|
386
|
+
:result, [3, [:val, [:predicate, [:str?, []]]]]]
|
387
|
+
]]
|
388
|
+
)
|
219
389
|
|
220
|
-
expect(msg).to eql(
|
390
|
+
expect(msg).to eql(num: ['must be a string'])
|
221
391
|
end
|
222
392
|
end
|
223
393
|
|
224
394
|
describe ':bool?' do
|
225
395
|
it 'returns valid message' do
|
226
|
-
msg = error_compiler.
|
396
|
+
msg = error_compiler.visit(
|
397
|
+
[:input, [:num, [
|
398
|
+
:result, [3, [:val, [:predicate, [:bool?, []]]]]]
|
399
|
+
]]
|
400
|
+
)
|
227
401
|
|
228
|
-
expect(msg).to eql(
|
402
|
+
expect(msg).to eql(num: ['must be boolean'])
|
229
403
|
end
|
230
404
|
end
|
231
405
|
|
232
406
|
describe ':format?' do
|
233
407
|
it 'returns valid message' do
|
234
|
-
msg = error_compiler.
|
408
|
+
msg = error_compiler.visit(
|
409
|
+
[:input, [:str, [
|
410
|
+
:result, ['Bar', [:val, [:predicate, [:format?, [/^F/]]]]]]
|
411
|
+
]]
|
412
|
+
)
|
235
413
|
|
236
|
-
expect(msg).to eql(
|
414
|
+
expect(msg).to eql(str: ['is in invalid format'])
|
237
415
|
end
|
238
416
|
end
|
239
417
|
|
240
418
|
describe ':eql?' do
|
241
419
|
it 'returns valid message' do
|
242
|
-
msg = error_compiler.
|
420
|
+
msg = error_compiler.visit(
|
421
|
+
[:input, [:str, [
|
422
|
+
:result, ['Foo', [:val, [:predicate, [:eql?, ['Bar']]]]]]
|
423
|
+
]]
|
424
|
+
)
|
425
|
+
|
426
|
+
expect(msg).to eql(str: ['must be equal to Bar'])
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
describe ':type??' do
|
431
|
+
it 'returns valid message' do
|
432
|
+
msg = error_compiler.visit(
|
433
|
+
[:input, [:age, [
|
434
|
+
:result, ['1', [:val, [:predicate, [:type?, [Integer]]]]]]
|
435
|
+
]]
|
436
|
+
)
|
243
437
|
|
244
|
-
expect(msg).to eql('
|
438
|
+
expect(msg).to eql(age: ['must be Integer'])
|
245
439
|
end
|
246
440
|
end
|
247
441
|
end
|