dry-validation 0.2.0 → 0.3.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.
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