dry-validation 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -2
  3. data/Gemfile +4 -0
  4. data/README.md +131 -42
  5. data/config/errors.yml +36 -27
  6. data/examples/basic.rb +2 -4
  7. data/examples/each.rb +2 -2
  8. data/examples/form.rb +1 -2
  9. data/examples/nested.rb +2 -4
  10. data/examples/rule_ast.rb +0 -8
  11. data/lib/dry/validation.rb +0 -5
  12. data/lib/dry/validation/error.rb +2 -6
  13. data/lib/dry/validation/error_compiler.rb +19 -5
  14. data/lib/dry/validation/input_type_compiler.rb +2 -1
  15. data/lib/dry/validation/messages.rb +7 -58
  16. data/lib/dry/validation/messages/abstract.rb +75 -0
  17. data/lib/dry/validation/messages/i18n.rb +24 -0
  18. data/lib/dry/validation/messages/namespaced.rb +27 -0
  19. data/lib/dry/validation/messages/yaml.rb +50 -0
  20. data/lib/dry/validation/result.rb +19 -49
  21. data/lib/dry/validation/rule.rb +2 -2
  22. data/lib/dry/validation/rule/group.rb +21 -0
  23. data/lib/dry/validation/rule/result.rb +73 -0
  24. data/lib/dry/validation/rule_compiler.rb +5 -0
  25. data/lib/dry/validation/schema.rb +33 -14
  26. data/lib/dry/validation/schema/definition.rb +16 -0
  27. data/lib/dry/validation/schema/result.rb +21 -3
  28. data/lib/dry/validation/schema/rule.rb +1 -1
  29. data/lib/dry/validation/schema/value.rb +2 -1
  30. data/lib/dry/validation/version.rb +1 -1
  31. data/spec/fixtures/locales/en.yml +5 -0
  32. data/spec/fixtures/locales/pl.yml +14 -0
  33. data/spec/integration/custom_error_messages_spec.rb +4 -16
  34. data/spec/{unit → integration}/error_compiler_spec.rb +81 -39
  35. data/spec/integration/localized_error_messages_spec.rb +52 -0
  36. data/spec/integration/messages/i18n_spec.rb +71 -0
  37. data/spec/integration/rule_groups_spec.rb +35 -0
  38. data/spec/integration/schema_form_spec.rb +9 -9
  39. data/spec/integration/schema_spec.rb +2 -2
  40. data/spec/shared/predicates.rb +2 -0
  41. data/spec/spec_helper.rb +1 -0
  42. data/spec/unit/rule/group_spec.rb +12 -0
  43. data/spec/unit/schema_spec.rb +35 -0
  44. metadata +24 -6
  45. data/spec/fixtures/errors.yml +0 -4
@@ -1,5 +1,3 @@
1
- require 'dry/validation/result'
2
-
3
1
  module Dry
4
2
  module Validation
5
3
  class Rule
@@ -44,3 +42,5 @@ require 'dry/validation/rule/value'
44
42
  require 'dry/validation/rule/each'
45
43
  require 'dry/validation/rule/set'
46
44
  require 'dry/validation/rule/composite'
45
+ require 'dry/validation/rule/group'
46
+ require 'dry/validation/rule/result'
@@ -0,0 +1,21 @@
1
+ module Dry
2
+ module Validation
3
+ class Rule::Group < Rule
4
+ attr_reader :rules
5
+
6
+ def initialize(identifier, predicate)
7
+ name, rules = identifier.to_a.first
8
+ @rules = rules
9
+ super(name, predicate)
10
+ end
11
+
12
+ def call(*input)
13
+ Validation.Result(input, predicate.(*input), self)
14
+ end
15
+
16
+ def type
17
+ :group
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,73 @@
1
+ module Dry
2
+ module Validation
3
+ def self.Result(input, value, rule)
4
+ case value
5
+ when Array then Rule::Result::Set.new(input, value, rule)
6
+ else Rule::Result::Value.new(input, value, rule)
7
+ end
8
+ end
9
+
10
+ class Rule::Result
11
+ include Dry::Equalizer(:success?, :input, :rule)
12
+
13
+ attr_reader :input, :value, :rule, :name
14
+
15
+ class Rule::Result::Set < Rule::Result
16
+ def success?
17
+ value.all?(&:success?)
18
+ end
19
+
20
+ def to_ary
21
+ indices = value.map { |v| v.failure? ? value.index(v) : nil }.compact
22
+ [:input, [rule.name, input, value.values_at(*indices).map(&:to_ary)]]
23
+ end
24
+ end
25
+
26
+ class Rule::Result::Value < Rule::Result
27
+ def to_ary
28
+ [:input, [rule.name, input, [rule.to_ary]]]
29
+ end
30
+ alias_method :to_a, :to_ary
31
+ end
32
+
33
+ def initialize(input, value, rule)
34
+ @input = input
35
+ @value = value
36
+ @rule = rule
37
+ @name = rule.name
38
+ end
39
+
40
+ def >(other)
41
+ if success?
42
+ other.(input)
43
+ else
44
+ Validation.Result(input, true, rule)
45
+ end
46
+ end
47
+
48
+ def and(other)
49
+ if success?
50
+ other.(input)
51
+ else
52
+ self
53
+ end
54
+ end
55
+
56
+ def or(other)
57
+ if success?
58
+ self
59
+ else
60
+ other.(input)
61
+ end
62
+ end
63
+
64
+ def success?
65
+ @value
66
+ end
67
+
68
+ def failure?
69
+ ! success?
70
+ end
71
+ end
72
+ end
73
+ end
@@ -57,6 +57,11 @@ module Dry
57
57
  left, right = node
58
58
  visit(left) > visit(right)
59
59
  end
60
+
61
+ def visit_group(node)
62
+ identifier, predicate = node
63
+ Rule::Group.new(identifier, visit(predicate))
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -4,6 +4,7 @@ require 'dry/validation/error'
4
4
  require 'dry/validation/rule_compiler'
5
5
  require 'dry/validation/messages'
6
6
  require 'dry/validation/error_compiler'
7
+ require 'dry/validation/result'
7
8
  require 'dry/validation/schema/result'
8
9
 
9
10
  module Dry
@@ -13,7 +14,7 @@ module Dry
13
14
  extend Definition
14
15
 
15
16
  setting :predicates, Predicates
16
- setting :messages, Messages.default
17
+ setting :messages, :yaml
17
18
  setting :messages_file
18
19
  setting :namespace
19
20
 
@@ -26,7 +27,13 @@ module Dry
26
27
  end
27
28
 
28
29
  def self.messages
29
- default = config.messages
30
+ default =
31
+ case config.messages
32
+ when :yaml then Messages.default
33
+ when :i18n then Messages::I18n.new
34
+ else
35
+ raise RuntimeError, "+#{config.messages}+ is not a valid messages identifier"
36
+ end
30
37
 
31
38
  if config.messages_file && config.namespace
32
39
  default.merge(config.messages_file).namespaced(config.namespace)
@@ -43,36 +50,48 @@ module Dry
43
50
  @__rules__ ||= []
44
51
  end
45
52
 
46
- attr_reader :rules
53
+ def self.groups
54
+ @__groups__ ||= []
55
+ end
56
+
57
+ attr_reader :rules, :groups
47
58
 
48
59
  attr_reader :error_compiler
49
60
 
50
61
  def initialize(error_compiler = self.class.error_compiler)
51
- @rules = RuleCompiler.new(self).(self.class.rules.map(&:to_ary))
62
+ compiler = RuleCompiler.new(self)
63
+ @rules = compiler.(self.class.rules.map(&:to_ary))
64
+ @groups = compiler.(self.class.groups.map(&:to_ary))
52
65
  @error_compiler = error_compiler
53
66
  end
54
67
 
55
68
  def call(input)
56
- error_set = rules.each_with_object(Error::Set.new) do |rule, errors|
57
- result = rule.(input)
58
- errors << Error.new(result) if result.failure?
69
+ result = Validation::Result.new(rules.map { |rule| rule.(input) })
70
+
71
+ groups.each do |group|
72
+ result.with_values(group.rules) do |values|
73
+ result << group.(*values)
74
+ end
59
75
  end
60
76
 
61
- Result.new(input, error_set)
62
- end
77
+ errors = Error::Set.new(result.failures.map { |failure| Error.new(failure) })
63
78
 
64
- def messages(input)
65
- result = call(input)
66
- Result.new(result.params, error_compiler.(result.to_ary))
79
+ Schema::Result.new(input, result, errors, error_compiler)
67
80
  end
68
81
 
69
82
  def [](name)
70
- if methods.include?(name)
83
+ if predicates.key?(name)
84
+ predicates[name]
85
+ elsif respond_to?(name)
71
86
  Predicate.new(name, &method(name))
72
87
  else
73
- self.class.predicates[name]
88
+ raise ArgumentError, "+#{name}+ is not a valid predicate name"
74
89
  end
75
90
  end
91
+
92
+ def predicates
93
+ self.class.predicates
94
+ end
76
95
  end
77
96
  end
78
97
  end
@@ -9,6 +9,22 @@ module Dry
9
9
  def optional(name, &block)
10
10
  Key.new(name, rules).optional(&block)
11
11
  end
12
+
13
+ def rule(name, **options)
14
+ predicate, rules = options.to_a.first
15
+ identifier = { name => rules }
16
+
17
+ groups << [:group, [identifier, [:predicate, predicate]]]
18
+ end
19
+
20
+ def confirmation(name)
21
+ identifier = :"#{name}_confirmation"
22
+
23
+ key(name, &:filled?)
24
+ key(identifier, &:filled?)
25
+
26
+ rule(identifier, eql?: [name, identifier])
27
+ end
12
28
  end
13
29
  end
14
30
  end
@@ -1,20 +1,26 @@
1
1
  module Dry
2
2
  module Validation
3
3
  class Schema::Result
4
- include Dry::Equalizer(:params, :errors)
4
+ include Dry::Equalizer(:params, :messages)
5
5
  include Enumerable
6
6
 
7
7
  attr_reader :params
8
8
 
9
+ attr_reader :result
10
+
9
11
  attr_reader :errors
10
12
 
11
- def initialize(params, errors)
13
+ attr_reader :error_compiler
14
+
15
+ def initialize(params, result, errors, error_compiler)
12
16
  @params = params
17
+ @result = result
13
18
  @errors = errors
19
+ @error_compiler = error_compiler
14
20
  end
15
21
 
16
22
  def each(&block)
17
- errors.each(&block)
23
+ failures.each(&block)
18
24
  end
19
25
 
20
26
  def empty?
@@ -24,6 +30,18 @@ module Dry
24
30
  def to_ary
25
31
  errors.map(&:to_ary)
26
32
  end
33
+
34
+ def messages(options = {})
35
+ @messages ||= error_compiler.with(options).(errors.map(&:to_ary))
36
+ end
37
+
38
+ def successes
39
+ result.successes
40
+ end
41
+
42
+ def failures
43
+ result.failures
44
+ end
27
45
  end
28
46
  end
29
47
  end
@@ -2,7 +2,7 @@ module Dry
2
2
  module Validation
3
3
  class Schema
4
4
  class Rule
5
- attr_reader :node
5
+ attr_reader :name, :node
6
6
 
7
7
  def initialize(node)
8
8
  @node = node
@@ -4,11 +4,12 @@ module Dry
4
4
  class Value
5
5
  include Schema::Definition
6
6
 
7
- attr_reader :name, :rules
7
+ attr_reader :name, :rules, :groups
8
8
 
9
9
  def initialize(name)
10
10
  @name = name
11
11
  @rules = []
12
+ @groups = []
12
13
  end
13
14
 
14
15
  def each(&block)
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Validation
3
- VERSION = '0.2.0'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,5 @@
1
+ en:
2
+ errors:
3
+ rules:
4
+ email:
5
+ filled?: "Please provide your email"
@@ -0,0 +1,14 @@
1
+ pl:
2
+ errors:
3
+ filled?: "%{name} nie może być pusty"
4
+ size?:
5
+ arg:
6
+ default: "wielkość musi być równa %{num}"
7
+ range: "wielkość musi być między %{left} a %{right}"
8
+ rules:
9
+ email:
10
+ filled?: "Proszę podać adres email"
11
+ user:
12
+ rules:
13
+ email:
14
+ filled?: "Hej user! Dawaj ten email no!"
@@ -5,29 +5,17 @@ RSpec.describe Dry::Validation, 'with custom messages' do
5
5
  let(:schema) do
6
6
  Class.new(Dry::Validation::Schema) do
7
7
  configure do |config|
8
- config.messages_file = SPEC_ROOT.join('fixtures/errors.yml')
9
- config.namespace = :user
8
+ config.messages_file = SPEC_ROOT.join('fixtures/locales/en.yml')
10
9
  end
11
10
 
12
- key(:email) { |email| email.filled? }
11
+ key(:email, &:filled?)
13
12
  end
14
13
  end
15
14
 
16
- let(:attrs) do
17
- {
18
- email: 'jane@doe.org',
19
- age: 19,
20
- address: { city: 'NYC', street: 'Street 1/2', country: { code: 'US', name: 'USA' } },
21
- phone_numbers: [
22
- '123456', '234567'
23
- ]
24
- }.freeze
25
- end
26
-
27
15
  describe '#messages' do
28
16
  it 'returns compiled error messages' do
29
- expect(validation.messages(attrs.merge(email: ''))).to match_array([
30
- [:email, ["email can't be blank"]]
17
+ expect(validation.(email: '').messages).to match_array([
18
+ [:email, [['Please provide your email', '']]]
31
19
  ])
32
20
  end
33
21
  end
@@ -6,10 +6,14 @@ RSpec.describe Dry::Validation::ErrorCompiler do
6
6
 
7
7
  let(:messages) do
8
8
  Messages.default.merge(
9
- key?: '+%{name}+ key is missing in the hash',
10
- attributes: {
11
- address: {
12
- filled?: 'Please provide your address'
9
+ en: {
10
+ errors: {
11
+ key?: '+%{name}+ key is missing in the hash',
12
+ rules: {
13
+ address: {
14
+ filled?: 'Please provide your address'
15
+ }
16
+ }
13
17
  }
14
18
  }
15
19
  )
@@ -26,202 +30,240 @@ RSpec.describe Dry::Validation::ErrorCompiler do
26
30
  end
27
31
 
28
32
  it 'converts error ast into another format' do
29
- expect(error_compiler.(ast)).to eql([
30
- [:name, ["+name+ key is missing in the hash"]],
31
- [:age, ["age must be greater than 18 (18 was given)"]],
32
- [:email, ["email must be filled"]],
33
- [:address, ["Please provide your address"]]
34
- ])
33
+ expect(error_compiler.(ast)).to eql(
34
+ name: [["+name+ key is missing in the hash", nil]],
35
+ age: [["age must be greater than 18", 18]],
36
+ email: [["email must be filled", '']],
37
+ address: [["Please provide your address", '']]
38
+ )
35
39
  end
36
40
  end
37
41
 
38
42
  describe '#visit_predicate' do
39
43
  describe ':empty?' do
40
44
  it 'returns valid message' do
41
- msg = error_compiler.visit_predicate([:empty?, []], [], :tags)
45
+ msg, val = error_compiler.visit_predicate([:empty?, []], [], :tags)
42
46
 
47
+ expect(val).to eql([])
43
48
  expect(msg).to eql('tags cannot be empty')
44
49
  end
45
50
  end
46
51
 
47
52
  describe ':exclusion?' do
48
53
  it 'returns valid message' do
49
- msg = error_compiler.visit_predicate([:exclusion?, [[1, 2, 3]]], 2, :num)
54
+ msg, val = error_compiler.visit_predicate([:exclusion?, [[1, 2, 3]]], 2, :num)
50
55
 
56
+ expect(val).to be(2)
51
57
  expect(msg).to eql('num must not be one of: 1, 2, 3')
52
58
  end
53
59
  end
54
60
 
55
61
  describe ':inclusion?' do
56
62
  it 'returns valid message' do
57
- msg = error_compiler.visit_predicate([:inclusion?, [[1, 2, 3]]], 2, :num)
63
+ msg, val = error_compiler.visit_predicate([:inclusion?, [[1, 2, 3]]], 2, :num)
58
64
 
65
+ expect(val).to be(2)
59
66
  expect(msg).to eql('num must be one of: 1, 2, 3')
60
67
  end
61
68
  end
62
69
 
63
70
  describe ':gt?' do
64
71
  it 'returns valid message' do
65
- msg = error_compiler.visit_predicate([:gt?, [3]], 2, :num)
72
+ msg, val = error_compiler.visit_predicate([:gt?, [3]], 2, :num)
66
73
 
67
- expect(msg).to eql('num must be greater than 3 (2 was given)')
74
+ expect(val).to be(2)
75
+ expect(msg).to eql('num must be greater than 3')
68
76
  end
69
77
  end
70
78
 
71
79
  describe ':gteq?' do
72
80
  it 'returns valid message' do
73
- msg = error_compiler.visit_predicate([:gteq?, [3]], 2, :num)
81
+ msg, val = error_compiler.visit_predicate([:gteq?, [3]], 2, :num)
74
82
 
83
+ expect(val).to be(2)
75
84
  expect(msg).to eql('num must be greater than or equal to 3')
76
85
  end
77
86
  end
78
87
 
79
88
  describe ':lt?' do
80
89
  it 'returns valid message' do
81
- msg = error_compiler.visit_predicate([:lt?, [3]], 2, :num)
90
+ msg, val = error_compiler.visit_predicate([:lt?, [3]], 2, :num)
82
91
 
92
+ expect(val).to be(2)
83
93
  expect(msg).to eql('num must be less than 3 (2 was given)')
84
94
  end
85
95
  end
86
96
 
87
97
  describe ':lteq?' do
88
98
  it 'returns valid message' do
89
- msg = error_compiler.visit_predicate([:lteq?, [3]], 2, :num)
99
+ msg, val = error_compiler.visit_predicate([:lteq?, [3]], 2, :num)
90
100
 
101
+ expect(val).to be(2)
91
102
  expect(msg).to eql('num must be less than or equal to 3')
92
103
  end
93
104
  end
94
105
 
95
106
  describe ':hash?' do
96
107
  it 'returns valid message' do
97
- msg = error_compiler.visit_predicate([:hash?, []], '', :address)
108
+ msg, val = error_compiler.visit_predicate([:hash?, []], '', :address)
98
109
 
110
+ expect(val).to eql('')
99
111
  expect(msg).to eql('address must be a hash')
100
112
  end
101
113
  end
102
114
 
103
115
  describe ':array?' do
104
116
  it 'returns valid message' do
105
- msg = error_compiler.visit_predicate([:array?, []], '', :phone_numbers)
117
+ msg, val = error_compiler.visit_predicate([:array?, []], '', :phone_numbers)
106
118
 
119
+ expect(val).to eql('')
107
120
  expect(msg).to eql('phone_numbers must be an array')
108
121
  end
109
122
  end
110
123
 
111
124
  describe ':int?' do
112
125
  it 'returns valid message' do
113
- msg = error_compiler.visit_predicate([:int?, []], '2', :num)
126
+ msg, val = error_compiler.visit_predicate([:int?, []], '2', :num)
114
127
 
128
+ expect(val).to eql('2')
115
129
  expect(msg).to eql('num must be an integer')
116
130
  end
117
131
  end
118
132
 
119
133
  describe ':float?' do
120
134
  it 'returns valid message' do
121
- msg = error_compiler.visit_predicate([:float?, []], '2', :num)
135
+ msg, val = error_compiler.visit_predicate([:float?, []], '2', :num)
122
136
 
137
+ expect(val).to eql('2')
123
138
  expect(msg).to eql('num must be a float')
124
139
  end
125
140
  end
126
141
 
127
142
  describe ':decimal?' do
128
143
  it 'returns valid message' do
129
- msg = error_compiler.visit_predicate([:decimal?, []], '2', :num)
144
+ msg, val = error_compiler.visit_predicate([:decimal?, []], '2', :num)
130
145
 
146
+ expect(val).to eql('2')
131
147
  expect(msg).to eql('num must be a decimal')
132
148
  end
133
149
  end
134
150
 
135
151
  describe ':date?' do
136
152
  it 'returns valid message' do
137
- msg = error_compiler.visit_predicate([:date?, []], '2', :num)
153
+ msg, val = error_compiler.visit_predicate([:date?, []], '2', :num)
138
154
 
155
+ expect(val).to eql('2')
139
156
  expect(msg).to eql('num must be a date')
140
157
  end
141
158
  end
142
159
 
143
160
  describe ':date_time?' do
144
161
  it 'returns valid message' do
145
- msg = error_compiler.visit_predicate([:date_time?, []], '2', :num)
162
+ msg, val = error_compiler.visit_predicate([:date_time?, []], '2', :num)
146
163
 
164
+ expect(val).to eql('2')
147
165
  expect(msg).to eql('num must be a date time')
148
166
  end
149
167
  end
150
168
 
151
169
  describe ':time?' do
152
170
  it 'returns valid message' do
153
- msg = error_compiler.visit_predicate([:time?, []], '2', :num)
171
+ msg, val = error_compiler.visit_predicate([:time?, []], '2', :num)
154
172
 
173
+ expect(val).to eql('2')
155
174
  expect(msg).to eql('num must be a time')
156
175
  end
157
176
  end
158
177
 
159
178
  describe ':max_size?' do
160
179
  it 'returns valid message' do
161
- msg = error_compiler.visit_predicate([:max_size?, [3]], 'abcd', :num)
180
+ msg, val = error_compiler.visit_predicate([:max_size?, [3]], 'abcd', :num)
162
181
 
182
+ expect(val).to eql('abcd')
163
183
  expect(msg).to eql('num size cannot be greater than 3')
164
184
  end
165
185
  end
166
186
 
167
187
  describe ':min_size?' do
168
188
  it 'returns valid message' do
169
- msg = error_compiler.visit_predicate([:min_size?, [3]], 'ab', :num)
189
+ msg, val = error_compiler.visit_predicate([:min_size?, [3]], 'ab', :num)
170
190
 
191
+ expect(val).to eql('ab')
171
192
  expect(msg).to eql('num size cannot be less than 3')
172
193
  end
173
194
  end
174
195
 
175
- describe ':nil?' do
196
+ describe ':none?' do
176
197
  it 'returns valid message' do
177
- msg = error_compiler.visit_predicate([:nil?, []], nil, :num)
198
+ msg, val = error_compiler.visit_predicate([:none?, []], nil, :num)
178
199
 
179
- expect(msg).to eql('num cannot be nil')
200
+ expect(val).to be(nil)
201
+ expect(msg).to eql('num cannot be defined')
180
202
  end
181
203
  end
182
204
 
183
205
  describe ':size?' do
206
+ it 'returns valid message when val is array and arg is int' do
207
+ msg, val = error_compiler.visit_predicate([:size?, [3]], [1], :numbers)
208
+
209
+ expect(val).to eql([1])
210
+ expect(msg).to eql('numbers size must be 3')
211
+ end
212
+
213
+ it 'returns valid message when val is array and arg is range' do
214
+ msg, val = error_compiler.visit_predicate([:size?, [3..4]], [1], :numbers)
215
+
216
+ expect(val).to eql([1])
217
+ expect(msg).to eql('numbers size must be within 3 - 4')
218
+ end
219
+
184
220
  it 'returns valid message when arg is int' do
185
- msg = error_compiler.visit_predicate([:size?, [3]], 'ab', :num)
221
+ msg, val = error_compiler.visit_predicate([:size?, [3]], 'ab', :num)
186
222
 
187
- expect(msg).to eql('num size must be 3')
223
+ expect(val).to eql('ab')
224
+ expect(msg).to eql('num length must be 3')
188
225
  end
189
226
 
190
227
  it 'returns valid message when arg is range' do
191
- msg = error_compiler.visit_predicate([:size?, [3..4]], 'ab', :num)
228
+ msg, val = error_compiler.visit_predicate([:size?, [3..4]], 'ab', :num)
192
229
 
193
- expect(msg).to eql('num size must be within 3 - 4')
230
+ expect(val).to eql('ab')
231
+ expect(msg).to eql('num length must be within 3 - 4')
194
232
  end
195
233
  end
196
234
 
197
235
  describe ':str?' do
198
236
  it 'returns valid message' do
199
- msg = error_compiler.visit_predicate([:str?, []], 3, :num)
237
+ msg, val = error_compiler.visit_predicate([:str?, []], 3, :num)
200
238
 
239
+ expect(val).to be(3)
201
240
  expect(msg).to eql('num must be a string')
202
241
  end
203
242
  end
204
243
 
205
244
  describe ':bool?' do
206
245
  it 'returns valid message' do
207
- msg = error_compiler.visit_predicate([:bool?, []], 3, :num)
246
+ msg, val = error_compiler.visit_predicate([:bool?, []], 3, :num)
208
247
 
248
+ expect(val).to be(3)
209
249
  expect(msg).to eql('num must be boolean')
210
250
  end
211
251
  end
212
252
 
213
253
  describe ':format?' do
214
254
  it 'returns valid message' do
215
- msg = error_compiler.visit_predicate([:format?, [/^F/]], 'Bar', :str)
255
+ msg, val = error_compiler.visit_predicate([:format?, [/^F/]], 'Bar', :str)
216
256
 
257
+ expect(val).to eql('Bar')
217
258
  expect(msg).to eql('str is in invalid format')
218
259
  end
219
260
  end
220
261
 
221
262
  describe ':eql?' do
222
263
  it 'returns valid message' do
223
- msg = error_compiler.visit_predicate([:eql?, ['Bar']], 'Foo', :str)
264
+ msg, val = error_compiler.visit_predicate([:eql?, ['Bar']], 'Foo', :str)
224
265
 
266
+ expect(val).to eql('Foo')
225
267
  expect(msg).to eql('str must be equal to Bar')
226
268
  end
227
269
  end