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
@@ -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
|