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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.travis.yml +3 -2
  4. data/CHANGELOG.md +42 -0
  5. data/Gemfile +8 -1
  6. data/README.md +13 -89
  7. data/config/errors.yml +35 -29
  8. data/dry-validation.gemspec +2 -2
  9. data/examples/basic.rb +3 -7
  10. data/examples/each.rb +3 -8
  11. data/examples/form.rb +3 -6
  12. data/examples/nested.rb +7 -15
  13. data/lib/dry/validation.rb +33 -5
  14. data/lib/dry/validation/error.rb +10 -26
  15. data/lib/dry/validation/error_compiler.rb +69 -99
  16. data/lib/dry/validation/error_compiler/input.rb +148 -0
  17. data/lib/dry/validation/hint_compiler.rb +83 -33
  18. data/lib/dry/validation/input_processor_compiler.rb +98 -0
  19. data/lib/dry/validation/input_processor_compiler/form.rb +46 -0
  20. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +46 -0
  21. data/lib/dry/validation/messages/abstract.rb +30 -10
  22. data/lib/dry/validation/messages/i18n.rb +2 -1
  23. data/lib/dry/validation/messages/namespaced.rb +1 -0
  24. data/lib/dry/validation/messages/yaml.rb +8 -5
  25. data/lib/dry/validation/result.rb +33 -25
  26. data/lib/dry/validation/schema.rb +168 -61
  27. data/lib/dry/validation/schema/attr.rb +5 -27
  28. data/lib/dry/validation/schema/check.rb +24 -0
  29. data/lib/dry/validation/schema/dsl.rb +97 -0
  30. data/lib/dry/validation/schema/form.rb +2 -26
  31. data/lib/dry/validation/schema/key.rb +32 -28
  32. data/lib/dry/validation/schema/rule.rb +88 -32
  33. data/lib/dry/validation/schema/value.rb +77 -27
  34. data/lib/dry/validation/schema_compiler.rb +38 -0
  35. data/lib/dry/validation/version.rb +1 -1
  36. data/spec/fixtures/locales/pl.yml +1 -1
  37. data/spec/integration/attr_spec.rb +122 -0
  38. data/spec/integration/custom_error_messages_spec.rb +9 -11
  39. data/spec/integration/custom_predicates_spec.rb +68 -18
  40. data/spec/integration/error_compiler_spec.rb +259 -65
  41. data/spec/integration/hints_spec.rb +28 -9
  42. data/spec/integration/injecting_rules_spec.rb +11 -12
  43. data/spec/integration/localized_error_messages_spec.rb +16 -16
  44. data/spec/integration/messages/i18n_spec.rb +9 -5
  45. data/spec/integration/optional_keys_spec.rb +9 -11
  46. data/spec/integration/schema/array_schema_spec.rb +23 -0
  47. data/spec/integration/schema/check_rules_spec.rb +39 -31
  48. data/spec/integration/schema/check_with_nth_el_spec.rb +25 -0
  49. data/spec/integration/schema/each_with_set_spec.rb +23 -24
  50. data/spec/integration/schema/form_spec.rb +122 -0
  51. data/spec/integration/schema/inheriting_schema_spec.rb +31 -0
  52. data/spec/integration/schema/input_processor_spec.rb +46 -0
  53. data/spec/integration/schema/macros/confirmation_spec.rb +33 -0
  54. data/spec/integration/schema/macros/maybe_spec.rb +32 -0
  55. data/spec/integration/schema/macros/required_spec.rb +59 -0
  56. data/spec/integration/schema/macros/when_spec.rb +65 -0
  57. data/spec/integration/schema/nested_values_spec.rb +41 -0
  58. data/spec/integration/schema/not_spec.rb +14 -14
  59. data/spec/integration/schema/option_with_default_spec.rb +30 -0
  60. data/spec/integration/schema/reusing_schema_spec.rb +33 -0
  61. data/spec/integration/schema/using_types_spec.rb +29 -0
  62. data/spec/integration/schema/xor_spec.rb +17 -14
  63. data/spec/integration/schema_spec.rb +75 -245
  64. data/spec/shared/rule_compiler.rb +8 -0
  65. data/spec/spec_helper.rb +13 -0
  66. data/spec/unit/hint_compiler_spec.rb +10 -10
  67. data/spec/unit/{input_type_compiler_spec.rb → input_processor_compiler/form_spec.rb} +88 -73
  68. data/spec/unit/schema/key_spec.rb +33 -0
  69. data/spec/unit/schema/rule_spec.rb +7 -6
  70. data/spec/unit/schema/value_spec.rb +187 -54
  71. metadata +53 -31
  72. data/.rubocop.yml +0 -16
  73. data/.rubocop_todo.yml +0 -7
  74. data/lib/dry/validation/input_type_compiler.rb +0 -83
  75. data/lib/dry/validation/schema/definition.rb +0 -74
  76. data/lib/dry/validation/schema/result.rb +0 -68
  77. data/rakelib/rubocop.rake +0 -18
  78. data/spec/integration/rule_groups_spec.rb +0 -94
  79. data/spec/integration/schema/attrs_spec.rb +0 -38
  80. data/spec/integration/schema/default_key_behavior_spec.rb +0 -23
  81. data/spec/integration/schema/grouped_rules_spec.rb +0 -57
  82. data/spec/integration/schema/nested_spec.rb +0 -31
  83. 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
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Validation
3
- VERSION = '0.6.0'.freeze
3
+ VERSION = '0.7.0'.freeze
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  pl:
2
2
  errors:
3
- filled?: "%{name} nie może być pusty"
3
+ filled?: "nie może być pusty"
4
4
  size?:
5
5
  arg:
6
6
  default: "wielkość musi być równa %{num}"
@@ -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(validation.(email: '').messages).to match_array([
10
- [:email, [['Please provide your email'], '']]
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
- let(:schema) do
18
- Class.new(Dry::Validation::Schema) do
19
- configure do |config|
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
- let(:schema) do
38
- Class.new(Dry::Validation::Schema) do
39
- configure do |config|
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(validation.(email: 'jane@doe')).to be_empty
4
+ expect(schema.(email: 'jane@doe')).to be_success
7
5
 
8
- expect(validation.(email: nil)).to match_array([
9
- [:error, [:input, [:email, nil, [[:val, [:email, [:predicate, [:filled?, []]]]]]]]]
10
- ])
6
+ expect(schema.(email: nil).messages).to eql(
7
+ email: ['must be filled', 'must be a valid email']
8
+ )
11
9
 
12
- expect(validation.(email: 'jane')).to match_array([
13
- [:error, [:input, [:email, 'jane', [[:val, [:email, [:predicate, [:email?, []]]]]]]]]
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
- let(:schema) do
20
- Class.new(Dry::Validation::Schema) do
21
- configure do |config|
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) { |value| value.filled? & value.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
- let(:schema) do
46
- Class.new(Dry::Validation::Schema) do
47
- key(:email) { |value| value.filled? & value.email? }
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
- value.include?('@')
96
+ false
51
97
  end
52
98
  end
99
+
100
+ key(:email).required(:email?)
53
101
  end
54
102
 
55
- include_context 'uses custom predicates'
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, nil, [[:key, [:name, [:predicate, [:key?, []]]]]]]]],
27
- [:error, [:input, [:phone, nil, [[:attr, [:phone, [:predicate, [:attr?, []]]]]]]]],
28
- [:error, [:input, [:age, 18, [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]],
29
- [:error, [:input, [:email, "", [[:val, [:email, [:predicate, [:filled?, []]]]]]]]],
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: [["+name+ key is missing in the hash"], nil],
37
- phone: [["Object does not respond to the +phone+ attr"], nil],
38
- age: [["age must be greater than 18"], 18],
39
- email: [["email must be filled"], ''],
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 '#visit_predicate' do
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.visit_predicate([:empty?, []], [], :tags)
128
+ msg = error_compiler.visit(
129
+ [:input, [:tags, [:result, [nil, [:val, [:predicate, [:empty?, []]]]]]]]
130
+ )
49
131
 
50
- expect(msg).to eql('tags cannot be empty')
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.visit_predicate([:exclusion?, [[1, 2, 3]]], 2, :num)
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('num must not be one of: 1, 2, 3')
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.visit_predicate([:inclusion?, [[1, 2, 3]]], 2, :num)
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('num must be one of: 1, 2, 3')
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.visit_predicate([:gt?, [3]], 2, :num)
162
+ msg = error_compiler.visit(
163
+ [:input, [:num, [
164
+ :result, [2, [:val, [:predicate, [:gt?, [3]]]]]]
165
+ ]]
166
+ )
73
167
 
74
- expect(msg).to eql('num must be greater than 3')
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.visit_predicate([:gteq?, [3]], 2, :num)
174
+ msg = error_compiler.visit(
175
+ [:input, [:num, [
176
+ :result, [2, [:val, [:predicate, [:gteq?, [3]]]]]]
177
+ ]]
178
+ )
81
179
 
82
- expect(msg).to eql('num must be greater than or equal to 3')
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.visit_predicate([:lt?, [3]], 2, :num)
186
+ msg = error_compiler.visit(
187
+ [:input, [:num, [
188
+ :result, [2, [:val, [:predicate, [:lt?, [3]]]]]]
189
+ ]]
190
+ )
89
191
 
90
- expect(msg).to eql('num must be less than 3 (2 was given)')
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.visit_predicate([:lteq?, [3]], 2, :num)
198
+ msg = error_compiler.visit(
199
+ [:input, [:num, [
200
+ :result, [2, [:val, [:predicate, [:lteq?, [3]]]]]]
201
+ ]]
202
+ )
97
203
 
98
- expect(msg).to eql('num must be less than or equal to 3')
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.visit_predicate([:hash?, []], '', :address)
210
+ msg = error_compiler.visit(
211
+ [:input, [:address, [
212
+ :result, ['', [:val, [:predicate, [:hash?, []]]]]]
213
+ ]]
214
+ )
105
215
 
106
- expect(msg).to eql('address must be a hash')
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.visit_predicate([:array?, []], '', :phone_numbers)
222
+ msg = error_compiler.visit(
223
+ [:input, [:phone_numbers, [
224
+ :result, ['', [:val, [:predicate, [:array?, []]]]]]
225
+ ]]
226
+ )
113
227
 
114
- expect(msg).to eql('phone_numbers must be an array')
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.visit_predicate([:int?, []], '2', :num)
234
+ msg = error_compiler.visit(
235
+ [:input, [:num, [
236
+ :result, ['2', [:val, [:predicate, [:int?, []]]]]]
237
+ ]]
238
+ )
121
239
 
122
- expect(msg).to eql('num must be an integer')
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.visit_predicate([:float?, []], '2', :num)
246
+ msg = error_compiler.visit(
247
+ [:input, [:num, [
248
+ :result, ['2', [:val, [:predicate, [:float?, []]]]]]
249
+ ]]
250
+ )
129
251
 
130
- expect(msg).to eql('num must be a float')
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.visit_predicate([:decimal?, []], '2', :num)
258
+ msg = error_compiler.visit(
259
+ [:input, [:num, [
260
+ :result, ['2', [:val, [:predicate, [:decimal?, []]]]]]
261
+ ]]
262
+ )
137
263
 
138
- expect(msg).to eql('num must be a decimal')
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.visit_predicate([:date?, []], '2', :num)
270
+ msg = error_compiler.visit(
271
+ [:input, [:num, [
272
+ :result, ['2', [:val, [:predicate, [:date?, []]]]]]
273
+ ]]
274
+ )
145
275
 
146
- expect(msg).to eql('num must be a date')
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.visit_predicate([:date_time?, []], '2', :num)
282
+ msg = error_compiler.visit(
283
+ [:input, [:num, [
284
+ :result, ['2', [:val, [:predicate, [:date_time?, []]]]]]
285
+ ]]
286
+ )
153
287
 
154
- expect(msg).to eql('num must be a date time')
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.visit_predicate([:time?, []], '2', :num)
294
+ msg = error_compiler.visit(
295
+ [:input, [:num, [
296
+ :result, ['2', [:val, [:predicate, [:time?, []]]]]]
297
+ ]]
298
+ )
161
299
 
162
- expect(msg).to eql('num must be a time')
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.visit_predicate([:max_size?, [3]], 'abcd', :num)
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('num size cannot be greater than 3')
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.visit_predicate([:min_size?, [3]], 'ab', :num)
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('num size cannot be less than 3')
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.visit_predicate([:none?, []], nil, :num)
330
+ msg = error_compiler.visit(
331
+ [:input, [:num, [
332
+ :result, [nil, [:val, [:predicate, [:none?, []]]]]]
333
+ ]]
334
+ )
185
335
 
186
- expect(msg).to eql('num cannot be defined')
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.visit_predicate([:size?, [3]], [1], :numbers)
342
+ msg = error_compiler.visit(
343
+ [:input, [:numbers, [
344
+ :result, [[1], [:val, [:predicate, [:size?, [3]]]]]]
345
+ ]]
346
+ )
193
347
 
194
- expect(msg).to eql('numbers size must be 3')
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.visit_predicate([:size?, [3..4]], [1], :numbers)
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('numbers size must be within 3 - 4')
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.visit_predicate([:size?, [3]], 'ab', :num)
362
+ msg = error_compiler.visit(
363
+ [:input, [:num, [
364
+ :result, ['ab', [:val, [:predicate, [:size?, [3]]]]]]
365
+ ]]
366
+ )
205
367
 
206
- expect(msg).to eql('num length must be 3')
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.visit_predicate([:size?, [3..4]], 'ab', :num)
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('num length must be within 3 - 4')
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.visit_predicate([:str?, []], 3, :num)
384
+ msg = error_compiler.visit(
385
+ [:input, [:num, [
386
+ :result, [3, [:val, [:predicate, [:str?, []]]]]]
387
+ ]]
388
+ )
219
389
 
220
- expect(msg).to eql('num must be a string')
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.visit_predicate([:bool?, []], 3, :num)
396
+ msg = error_compiler.visit(
397
+ [:input, [:num, [
398
+ :result, [3, [:val, [:predicate, [:bool?, []]]]]]
399
+ ]]
400
+ )
227
401
 
228
- expect(msg).to eql('num must be boolean')
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.visit_predicate([:format?, [/^F/]], 'Bar', :str)
408
+ msg = error_compiler.visit(
409
+ [:input, [:str, [
410
+ :result, ['Bar', [:val, [:predicate, [:format?, [/^F/]]]]]]
411
+ ]]
412
+ )
235
413
 
236
- expect(msg).to eql('str is in invalid format')
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.visit_predicate([:eql?, ['Bar']], 'Foo', :str)
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('str must be equal to Bar')
438
+ expect(msg).to eql(age: ['must be Integer'])
245
439
  end
246
440
  end
247
441
  end