/
comparison_matcher.rb
159 lines (136 loc) · 4.28 KB
/
comparison_matcher.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
module Shoulda
module Matchers
module ActiveModel
module NumericalityMatchers
# @private
class ComparisonMatcher < ValidationMatcher
ERROR_MESSAGES = {
:> => :greater_than,
:>= => :greater_than_or_equal_to,
:< => :less_than,
:<= => :less_than_or_equal_to,
:== => :equal_to,
:!= => :other_than,
}
def initialize(numericality_matcher, value, operator)
super(nil)
unless numericality_matcher.respond_to? :diff_to_compare
raise ArgumentError, 'numericality_matcher is invalid'
end
@numericality_matcher = numericality_matcher
@value = value
@operator = operator
@message = ERROR_MESSAGES[operator]
end
def simple_description
description = ''
if expects_strict?
description << ' strictly'
end
description +
"disallow :#{attribute} from being a number that is not " +
"#{comparison_expectation} #{@value}"
end
def for(attribute)
@attribute = attribute
self
end
def with_message(message)
@expects_custom_validation_message = true
@message = message
self
end
def expects_custom_validation_message?
@expects_custom_validation_message
end
def matches?(subject)
@subject = subject
all_bounds_correct?
end
def failure_message
last_failing_submatcher.failure_message
end
def failure_message_when_negated
last_failing_submatcher.failure_message_when_negated
end
def comparison_description
"#{comparison_expectation} #{@value}"
end
private
def all_bounds_correct?
failing_submatchers.empty?
end
def failing_submatchers
submatchers_and_results.
select { |x| !x[:matched] }.
map { |x| x[:matcher] }
end
def last_failing_submatcher
failing_submatchers.last
end
def submatchers
@_submatchers ||=
comparison_combos.map do |diff, submatcher_method_name|
matcher = __send__(submatcher_method_name, diff, nil)
matcher.with_message(@message, values: { count: @value })
matcher
end
end
def submatchers_and_results
@_submatchers_and_results ||=
submatchers.map do |matcher|
{ matcher: matcher, matched: matcher.matches?(@subject) }
end
end
def comparison_combos
diffs_to_compare.zip(submatcher_method_names)
end
def submatcher_method_names
assertions.map do |value|
if value
:allow_value_matcher
else
:disallow_value_matcher
end
end
end
def assertions
case @operator
when :>
[false, false, true]
when :>=
[false, true, true]
when :==
[false, true, false]
when :<
[true, false, false]
when :<=
[true, true, false]
when :!=
[true, false, true]
end
end
def diffs_to_compare
diff_to_compare = @numericality_matcher.diff_to_compare
values = [-1, 0, 1].map { |sign| @value + (diff_to_compare * sign) }
if @numericality_matcher.given_numeric_column?
values
else
values.map(&:to_s)
end
end
def comparison_expectation
case @operator
when :> then "greater than"
when :>= then "greater than or equal to"
when :== then "equal to"
when :< then "less than"
when :<= then "less than or equal to"
when :!= then 'other than'
end
end
end
end
end
end
end