shoulda-matchers 5.3.0 → 6.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +39 -15
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
- data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +16 -6
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +0 -6
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +24 -11
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +64 -9
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +40 -96
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
- data/lib/shoulda/matchers/active_model/validator.rb +4 -0
- data/lib/shoulda/matchers/active_model.rb +2 -1
- data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +9 -1
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -0
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +4 -0
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +23 -19
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +0 -8
- data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -1
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
- data/lib/shoulda/matchers/active_record.rb +2 -0
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +7 -5
- data/lib/shoulda/matchers/integrations/libraries/routing.rb +5 -3
- data/lib/shoulda/matchers/rails_shim.rb +8 -6
- data/lib/shoulda/matchers/util/word_wrap.rb +1 -1
- data/lib/shoulda/matchers/util.rb +18 -20
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +2 -2
- data/shoulda-matchers.gemspec +1 -1
- metadata +11 -8
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -136
@@ -0,0 +1,532 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# The `validate_comparison_of` matcher tests usage of the
|
5
|
+
# `validates_comparison_of` validation.
|
6
|
+
#
|
7
|
+
# class Person
|
8
|
+
# include ActiveModel::Model
|
9
|
+
# attr_accessor :gpa
|
10
|
+
#
|
11
|
+
# validates_comparison_of :gpa, greater_than: 10
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # RSpec
|
15
|
+
# RSpec.describe Person, type: :model do
|
16
|
+
# it { should validate_comparison_of(:gpa).is_greater_than(10) }
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # Minitest (Shoulda)
|
20
|
+
# class PersonTest < ActiveSupport::TestCase
|
21
|
+
# should validate_comparison_of(:gpa).is_greater_than(10)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# #### Qualifiers
|
25
|
+
#
|
26
|
+
# ##### on
|
27
|
+
#
|
28
|
+
# Use `on` if your validation applies only under a certain context.
|
29
|
+
#
|
30
|
+
# class Person
|
31
|
+
# include ActiveModel::Model
|
32
|
+
# attribute :number_of_dependents, :integer
|
33
|
+
# attr_accessor :number_of_dependents
|
34
|
+
#
|
35
|
+
# validates_comparison_of :number_of_dependents, on: :create, greater_than: 0
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # RSpec
|
39
|
+
# RSpec.describe Person, type: :model do
|
40
|
+
# it do
|
41
|
+
# should validate_comparison_of(:number_of_dependents).
|
42
|
+
# is_greater_than(0).
|
43
|
+
# on(:create)
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # Minitest (Shoulda)
|
48
|
+
# class PersonTest < ActiveSupport::TestCase
|
49
|
+
# should validate_comparison_of(:number_of_dependents).is_greater_than(0).on(:create)
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# ##### is_less_than
|
53
|
+
#
|
54
|
+
# Use `is_less_than` to test usage of the the `:less_than` option. This
|
55
|
+
# asserts that the attribute can take a value which is less than the
|
56
|
+
# given value and cannot take a value which is greater than or equal to
|
57
|
+
# it. It can also accept methods or procs that returns a given value.
|
58
|
+
#
|
59
|
+
# class Person
|
60
|
+
# include ActiveModel::Model
|
61
|
+
# attribute :number_of_cars, :integer
|
62
|
+
# attr_accessor :number_of_cars
|
63
|
+
#
|
64
|
+
# validates_comparison_of :number_of_cars, less_than: :current_number_of_cars
|
65
|
+
#
|
66
|
+
# def current_number_of_cars
|
67
|
+
# 10
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# # RSpec
|
72
|
+
# RSpec.describe Person, type: :model do
|
73
|
+
# it do
|
74
|
+
# should validate_comparison_of(:number_of_cars).
|
75
|
+
# is_less_than(:current_number_of_cars)
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# # Minitest (Shoulda)
|
80
|
+
# class PersonTest < ActiveSupport::TestCase
|
81
|
+
# should validate_comparison_of(:number_of_cars).
|
82
|
+
# is_less_than(:current_number_of_cars)
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# ##### is_less_than_or_equal_to
|
86
|
+
#
|
87
|
+
# Use `is_less_than_or_equal_to` to test usage of the
|
88
|
+
# `:less_than_or_equal_to` option. This asserts that the attribute can
|
89
|
+
# take a value which is less than or equal to the given value and cannot
|
90
|
+
# take a value which is greater than it. It can also accept methods or
|
91
|
+
# procs that returns a given value.
|
92
|
+
#
|
93
|
+
# class Person
|
94
|
+
# include ActiveModel::Model
|
95
|
+
# attr_accessor :birth_date
|
96
|
+
#
|
97
|
+
# validates_comparison_of :birth_date, less_than_or_equal_to: Date.new(1987, 12, 31)
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# # RSpec
|
101
|
+
# RSpec.describe Person, type: :model do
|
102
|
+
# it do
|
103
|
+
# should validate_comparison_of(:birth_date).
|
104
|
+
# is_less_than_or_equal_to(Date.new(1987, 12, 31))
|
105
|
+
# end
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# # Minitest (Shoulda)
|
109
|
+
# class PersonTest < ActiveSupport::TestCase
|
110
|
+
# should validate_comparison_of(:birth_date).
|
111
|
+
# is_less_than_or_equal_to(Date.new(1987, 12, 31))
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# ##### is_greater_than_or_equal_to
|
115
|
+
#
|
116
|
+
# Use `is_greater_than_or_equal_to` to test usage of the
|
117
|
+
# `:greater_than_or_equal_to` option. This asserts that the attribute can
|
118
|
+
# take a value which is greater than or equal to the given value and
|
119
|
+
# cannot take a value which is less than it.
|
120
|
+
#
|
121
|
+
# class Person
|
122
|
+
# include ActiveModel::Model
|
123
|
+
# attribute :birth_date, :date
|
124
|
+
# attr_accessor :birth_date
|
125
|
+
#
|
126
|
+
# validates_comparison_of :birth_date,
|
127
|
+
# greater_than_or_equal_to: -> { 18.years.ago.to_date }
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# # RSpec
|
131
|
+
# RSpec.describe Person, type: :model do
|
132
|
+
# it do
|
133
|
+
# should validate_comparison_of(:birth_date).
|
134
|
+
# is_greater_than_or_equal_to(-> { 18.years.ago.to_date })
|
135
|
+
# end
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# # Minitest (Shoulda)
|
139
|
+
# class PersonTest < ActiveSupport::TestCase
|
140
|
+
# should validate_comparison_of(:birth_date).
|
141
|
+
# is_greater_than_or_equal_to(-> { 18.years.ago.to_date })
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# ##### is_greater_than
|
145
|
+
#
|
146
|
+
# Use `is_greater_than` to test usage of the `:greater_than` option.
|
147
|
+
# This asserts that the attribute can take a value which is greater than
|
148
|
+
# the given value and cannot take a value less than or equal to it.
|
149
|
+
# It can also accept methods or procs that returns a given value.
|
150
|
+
#
|
151
|
+
# class Person
|
152
|
+
# include ActiveModel::Model
|
153
|
+
# attribute :legal_age, :integer
|
154
|
+
# attr_accessor :legal_age
|
155
|
+
#
|
156
|
+
# validates_comparison_of :legal_age, greater_than: 21
|
157
|
+
# end
|
158
|
+
#
|
159
|
+
# # RSpec
|
160
|
+
# RSpec.describe Person, type: :model do
|
161
|
+
# it do
|
162
|
+
# should validate_comparison_of(:legal_age).
|
163
|
+
# is_greater_than(21)
|
164
|
+
# end
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# # Minitest (Shoulda)
|
168
|
+
# class PersonTest < ActiveSupport::TestCase
|
169
|
+
# should validate_comparison_of(:legal_age).
|
170
|
+
# is_greater_than(21)
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# ##### is_equal_to
|
174
|
+
#
|
175
|
+
# Use `is_equal_to` to test usage of the `:equal_to` option. This asserts
|
176
|
+
# that the attribute can take a value which is equal to the given value
|
177
|
+
# and cannot take a value which is not equal. It can also accept methods or
|
178
|
+
# procs that returns a given value.
|
179
|
+
#
|
180
|
+
# class Person
|
181
|
+
# include ActiveModel::Model
|
182
|
+
# attribute :favorite_color, :string
|
183
|
+
# attr_accessor :favorite_color
|
184
|
+
#
|
185
|
+
# validates_comparison_of :favorite_color, equal_to: "blue"
|
186
|
+
# end
|
187
|
+
#
|
188
|
+
# # RSpec
|
189
|
+
# RSpec.describe Person, type: :model do
|
190
|
+
# it { should validate_comparison_of(:favorite_color).is_equal_to("blue") }
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# # Minitest (Shoulda)
|
194
|
+
# class PersonTest < ActiveSupport::TestCase
|
195
|
+
# should validate_comparison_of(:favorite_color).is_equal_to("blue")
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
#
|
199
|
+
# ##### is_other_than
|
200
|
+
#
|
201
|
+
# Use `is_other_than` to test usage of the `:other_than` option.
|
202
|
+
# This asserts that the attribute can take a number which is not equal to
|
203
|
+
# the given value.
|
204
|
+
#
|
205
|
+
# class Person
|
206
|
+
# include ActiveModel::Model
|
207
|
+
# attr_accessor :legal_age
|
208
|
+
#
|
209
|
+
# validates_comparison_of :legal_age, other_than: 21
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# # RSpec
|
213
|
+
# RSpec.describe Person, type: :model do
|
214
|
+
# it do
|
215
|
+
# should validate_comparison_of(:legal_age).
|
216
|
+
# is_other_than(21)
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# # Minitest (Shoulda)
|
221
|
+
# class PersonTest < ActiveSupport::TestCase
|
222
|
+
# should validate_comparison_of(:legal_age).
|
223
|
+
# is_other_than(21)
|
224
|
+
# end
|
225
|
+
#
|
226
|
+
# ##### with_message
|
227
|
+
#
|
228
|
+
# Use `with_message` if you are using a custom validation message.
|
229
|
+
#
|
230
|
+
# class Person
|
231
|
+
# include ActiveModel::Model
|
232
|
+
# attr_accessor :number_of_dependents
|
233
|
+
#
|
234
|
+
# validates_comparison_of :number_of_dependents, greater_than: 0
|
235
|
+
# message: 'Number of dependents must be a number'
|
236
|
+
# end
|
237
|
+
#
|
238
|
+
# # RSpec
|
239
|
+
# RSpec.describe Person, type: :model do
|
240
|
+
# it do
|
241
|
+
# should validate_comparison_of(:number_of_dependents).
|
242
|
+
# is_greater_than(0).
|
243
|
+
# with_message('Number of dependents must be a number')
|
244
|
+
# end
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# # Minitest (Shoulda)
|
248
|
+
# class PersonTest < ActiveSupport::TestCase
|
249
|
+
# should validate_comparison_of(:number_of_dependents).
|
250
|
+
# is_greater_than(0).
|
251
|
+
# with_message('Number of dependents must be a number')
|
252
|
+
# end
|
253
|
+
#
|
254
|
+
# ##### allow_nil
|
255
|
+
#
|
256
|
+
# Use `allow_nil` to assert that the attribute allows nil.
|
257
|
+
#
|
258
|
+
# class Post
|
259
|
+
# include ActiveModel::Model
|
260
|
+
# attr_accessor :age
|
261
|
+
#
|
262
|
+
# validates_comparison_of :age, greater_than: 0, allow_nil: true
|
263
|
+
# end
|
264
|
+
#
|
265
|
+
# # RSpec
|
266
|
+
# RSpec.describe Post, type: :model do
|
267
|
+
# it { should validate_comparison_of(:age).is_greater_than(0).allow_nil }
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
# # Minitest (Shoulda)
|
271
|
+
# class PostTest < ActiveSupport::TestCase
|
272
|
+
# should validate_comparison_of(:age).is_greater_than(0).allow_nil
|
273
|
+
# end
|
274
|
+
#
|
275
|
+
# @return [ValidateComparisonOfMatcher]
|
276
|
+
#
|
277
|
+
def validate_comparison_of(attr)
|
278
|
+
ValidateComparisonOfMatcher.new(attr)
|
279
|
+
end
|
280
|
+
|
281
|
+
# @private
|
282
|
+
class ValidateComparisonOfMatcher < ValidationMatcher
|
283
|
+
NUMERIC_NAME = 'number'.freeze
|
284
|
+
DEFAULT_DIFF_TO_COMPARE = 1
|
285
|
+
|
286
|
+
attr_reader :diff_to_compare, :number_of_submatchers
|
287
|
+
|
288
|
+
def initialize(attribute)
|
289
|
+
super
|
290
|
+
@submatchers = []
|
291
|
+
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
|
292
|
+
@expects_to_allow_nil = false
|
293
|
+
@comparison_submatcher = false
|
294
|
+
end
|
295
|
+
|
296
|
+
def allow_nil
|
297
|
+
@expects_to_allow_nil = true
|
298
|
+
prepare_submatcher(allow_value_matcher(nil))
|
299
|
+
self
|
300
|
+
end
|
301
|
+
|
302
|
+
def expects_to_allow_nil?
|
303
|
+
@expects_to_allow_nil
|
304
|
+
end
|
305
|
+
|
306
|
+
def is_greater_than(value)
|
307
|
+
prepare_submatcher(comparison_matcher_for(value, :>).for(attribute))
|
308
|
+
self
|
309
|
+
end
|
310
|
+
|
311
|
+
def is_greater_than_or_equal_to(value)
|
312
|
+
prepare_submatcher(comparison_matcher_for(value, :>=).for(attribute))
|
313
|
+
self
|
314
|
+
end
|
315
|
+
|
316
|
+
def is_equal_to(value)
|
317
|
+
prepare_submatcher(comparison_matcher_for(value, :==).for(attribute))
|
318
|
+
self
|
319
|
+
end
|
320
|
+
|
321
|
+
def is_less_than(value)
|
322
|
+
prepare_submatcher(comparison_matcher_for(value, :<).for(attribute))
|
323
|
+
self
|
324
|
+
end
|
325
|
+
|
326
|
+
def is_less_than_or_equal_to(value)
|
327
|
+
prepare_submatcher(comparison_matcher_for(value, :<=).for(attribute))
|
328
|
+
self
|
329
|
+
end
|
330
|
+
|
331
|
+
def is_other_than(value)
|
332
|
+
prepare_submatcher(comparison_matcher_for(value, :!=).for(attribute))
|
333
|
+
self
|
334
|
+
end
|
335
|
+
|
336
|
+
def matches?(subject)
|
337
|
+
@subject = subject
|
338
|
+
@number_of_submatchers = @submatchers.size
|
339
|
+
unless @comparison_matcher
|
340
|
+
raise(ArgumentError, "matcher isn't qualified with any comparison matcher")
|
341
|
+
end
|
342
|
+
|
343
|
+
qualify_submatchers
|
344
|
+
first_submatcher_that_fails_to_match.nil?
|
345
|
+
end
|
346
|
+
|
347
|
+
def does_not_match?(subject)
|
348
|
+
@subject = subject
|
349
|
+
@number_of_submatchers = @submatchers.size
|
350
|
+
|
351
|
+
qualify_submatchers
|
352
|
+
first_submatcher_that_fails_to_not_match.nil?
|
353
|
+
end
|
354
|
+
|
355
|
+
def simple_description
|
356
|
+
String.new.tap do |description|
|
357
|
+
description << "validate that :#{attribute} looks like "
|
358
|
+
description << Shoulda::Matchers::Util.a_or_an(allowed_type_name)
|
359
|
+
|
360
|
+
if comparison_descriptions.present?
|
361
|
+
description << " #{comparison_descriptions}"
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def failure_message
|
367
|
+
overall_failure_message.dup.tap do |message|
|
368
|
+
message << "\n"
|
369
|
+
message << failure_message_for_first_submatcher_that_fails_to_match
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
def failure_message_when_negated
|
374
|
+
overall_failure_message_when_negated.dup.tap do |message|
|
375
|
+
message << "\n"
|
376
|
+
message <<
|
377
|
+
failure_message_for_first_submatcher_that_fails_to_not_match
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def given_numeric_column?
|
382
|
+
attribute_is_active_record_column? &&
|
383
|
+
[:integer, :float, :decimal].include?(column_type)
|
384
|
+
end
|
385
|
+
|
386
|
+
private
|
387
|
+
|
388
|
+
def attribute_is_active_record_column?
|
389
|
+
columns_hash.key?(attribute.to_s)
|
390
|
+
end
|
391
|
+
|
392
|
+
def column_type
|
393
|
+
columns_hash[attribute.to_s].type
|
394
|
+
end
|
395
|
+
|
396
|
+
def columns_hash
|
397
|
+
if subject.class.respond_to?(:columns_hash)
|
398
|
+
subject.class.columns_hash
|
399
|
+
else
|
400
|
+
{}
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def prepare_submatcher(submatcher)
|
405
|
+
add_submatcher(submatcher)
|
406
|
+
submatcher
|
407
|
+
end
|
408
|
+
|
409
|
+
def comparison_matcher_for(value, operator)
|
410
|
+
@comparison_matcher = true
|
411
|
+
ComparisonMatcher.
|
412
|
+
new(self, value, operator).
|
413
|
+
for(attribute)
|
414
|
+
end
|
415
|
+
|
416
|
+
def add_submatcher(submatcher)
|
417
|
+
@submatchers << submatcher
|
418
|
+
end
|
419
|
+
|
420
|
+
def qualify_submatchers
|
421
|
+
@submatchers.each do |submatcher|
|
422
|
+
if @expects_strict
|
423
|
+
submatcher.strict
|
424
|
+
end
|
425
|
+
|
426
|
+
if @expected_message.present?
|
427
|
+
submatcher.with_message(@expected_message)
|
428
|
+
end
|
429
|
+
|
430
|
+
if @context
|
431
|
+
submatcher.on(@context)
|
432
|
+
end
|
433
|
+
|
434
|
+
submatcher.ignoring_interference_by_writer(
|
435
|
+
ignore_interference_by_writer,
|
436
|
+
)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def number_of_submatchers_for_failure_message
|
441
|
+
if has_been_qualified?
|
442
|
+
number_of_submatchers - 1
|
443
|
+
else
|
444
|
+
number_of_submatchers
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
def has_been_qualified?
|
449
|
+
@submatchers.any? { |submatcher| submatcher_qualified?(submatcher) }
|
450
|
+
end
|
451
|
+
|
452
|
+
def submatcher_qualified?(submatcher)
|
453
|
+
submatcher.instance_of?(ComparisonMatcher)
|
454
|
+
end
|
455
|
+
|
456
|
+
def first_submatcher_that_fails_to_match
|
457
|
+
@_first_submatcher_that_fails_to_match ||=
|
458
|
+
@submatchers.detect do |submatcher|
|
459
|
+
!submatcher.matches?(subject)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def first_submatcher_that_fails_to_not_match
|
464
|
+
@_first_submatcher_that_fails_to_not_match ||=
|
465
|
+
@submatchers.detect do |submatcher|
|
466
|
+
submatcher.matches?(subject)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def failure_message_for_first_submatcher_that_fails_to_match
|
471
|
+
build_submatcher_failure_message_for(
|
472
|
+
first_submatcher_that_fails_to_match,
|
473
|
+
:failure_message,
|
474
|
+
)
|
475
|
+
end
|
476
|
+
|
477
|
+
def failure_message_for_first_submatcher_that_fails_to_not_match
|
478
|
+
build_submatcher_failure_message_for(
|
479
|
+
first_submatcher_that_fails_to_not_match,
|
480
|
+
:failure_message_when_negated,
|
481
|
+
)
|
482
|
+
end
|
483
|
+
|
484
|
+
def build_submatcher_failure_message_for(
|
485
|
+
submatcher,
|
486
|
+
failure_message_method
|
487
|
+
)
|
488
|
+
failure_message = submatcher.public_send(failure_message_method)
|
489
|
+
submatcher_description = submatcher.simple_description.
|
490
|
+
sub(/\bvalidate that\b/, 'validates').
|
491
|
+
sub(/\bdisallow\b/, 'disallows').
|
492
|
+
sub(/\ballow\b/, 'allows')
|
493
|
+
submatcher_message =
|
494
|
+
if number_of_submatchers_for_failure_message > 1
|
495
|
+
"In checking that #{model.name} #{submatcher_description}, " +
|
496
|
+
failure_message[0].downcase +
|
497
|
+
failure_message[1..]
|
498
|
+
else
|
499
|
+
failure_message
|
500
|
+
end
|
501
|
+
|
502
|
+
Shoulda::Matchers.word_wrap(submatcher_message, indent: 2)
|
503
|
+
end
|
504
|
+
|
505
|
+
def comparison_descriptions
|
506
|
+
description_array = submatcher_comparison_descriptions
|
507
|
+
if description_array.empty?
|
508
|
+
''
|
509
|
+
else
|
510
|
+
submatcher_comparison_descriptions.join(' and ')
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def submatcher_comparison_descriptions
|
515
|
+
@submatchers.inject([]) do |arr, submatcher|
|
516
|
+
arr << if submatcher.respond_to? :comparison_description
|
517
|
+
submatcher.comparison_description
|
518
|
+
end
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def allowed_type_name
|
523
|
+
'value'
|
524
|
+
end
|
525
|
+
|
526
|
+
def non_numeric_value
|
527
|
+
'abcd'
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
@@ -3,10 +3,10 @@ module Shoulda
|
|
3
3
|
module ActiveModel
|
4
4
|
# The `validate_exclusion_of` matcher tests usage of the
|
5
5
|
# `validates_exclusion_of` validation, asserting that an attribute cannot
|
6
|
-
# take a
|
6
|
+
# take a blocklist of values, and inversely, can take values outside of
|
7
7
|
# this list.
|
8
8
|
#
|
9
|
-
# If your
|
9
|
+
# If your blocklist an array of values, use `in_array`:
|
10
10
|
#
|
11
11
|
# class Game
|
12
12
|
# include ActiveModel::Model
|
@@ -29,7 +29,7 @@ module Shoulda
|
|
29
29
|
# in_array(['Mac', 'Linux'])
|
30
30
|
# end
|
31
31
|
#
|
32
|
-
# If your
|
32
|
+
# If your blocklist is a range of values, use `in_range`:
|
33
33
|
#
|
34
34
|
# class Game
|
35
35
|
# include ActiveModel::Model
|
@@ -3,14 +3,15 @@ require 'date'
|
|
3
3
|
|
4
4
|
module Shoulda
|
5
5
|
module Matchers
|
6
|
+
# @private
|
6
7
|
class ExampleClass; end
|
7
8
|
|
8
9
|
module ActiveModel
|
9
10
|
# The `validate_inclusion_of` matcher tests usage of the
|
10
11
|
# `validates_inclusion_of` validation, asserting that an attribute can
|
11
|
-
# take a
|
12
|
+
# take a allowlist of values and cannot take values outside of this list.
|
12
13
|
#
|
13
|
-
# If your
|
14
|
+
# If your allowlist is an array of values, use `in_array`:
|
14
15
|
#
|
15
16
|
# class Issue
|
16
17
|
# include ActiveModel::Model
|
@@ -34,7 +35,7 @@ module Shoulda
|
|
34
35
|
# in_array(['open', 'resolved', 'unresolved'])
|
35
36
|
# end
|
36
37
|
#
|
37
|
-
# If your
|
38
|
+
# If your allowlist is a range of values, use `in_range`:
|
38
39
|
#
|
39
40
|
# class Issue
|
40
41
|
# include ActiveModel::Model
|
@@ -308,8 +309,8 @@ EOT
|
|
308
309
|
|
309
310
|
def in_range(range)
|
310
311
|
@range = range
|
311
|
-
@minimum =
|
312
|
-
@maximum =
|
312
|
+
@minimum = minimum_range_value
|
313
|
+
@maximum = maximum_range_value
|
313
314
|
self
|
314
315
|
end
|
315
316
|
|
@@ -399,6 +400,18 @@ EOT
|
|
399
400
|
|
400
401
|
private
|
401
402
|
|
403
|
+
def minimum_range_value
|
404
|
+
@range.begin
|
405
|
+
end
|
406
|
+
|
407
|
+
def maximum_range_value
|
408
|
+
if @range.exclude_end?
|
409
|
+
@range.end ? (@range.end - 1) : nil
|
410
|
+
else
|
411
|
+
@range.end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
402
415
|
def matches_for_range?
|
403
416
|
disallows_lower_value &&
|
404
417
|
allows_minimum_value &&
|
@@ -440,27 +453,27 @@ EOT
|
|
440
453
|
end
|
441
454
|
|
442
455
|
def allows_minimum_value
|
443
|
-
allows_value_of(@minimum, @low_message)
|
456
|
+
@minimum.nil? || allows_value_of(@minimum, @low_message)
|
444
457
|
end
|
445
458
|
|
446
459
|
def disallows_minimum_value
|
447
|
-
disallows_value_of(@minimum, @low_message)
|
460
|
+
@minimum.nil? || disallows_value_of(@minimum, @low_message)
|
448
461
|
end
|
449
462
|
|
450
463
|
def allows_maximum_value
|
451
|
-
allows_value_of(@maximum, @high_message)
|
464
|
+
@maximum.nil? || allows_value_of(@maximum, @high_message)
|
452
465
|
end
|
453
466
|
|
454
467
|
def disallows_maximum_value
|
455
|
-
disallows_value_of(@maximum, @high_message)
|
468
|
+
@maximum.nil? || disallows_value_of(@maximum, @high_message)
|
456
469
|
end
|
457
470
|
|
458
471
|
def allows_higher_value
|
459
|
-
allows_value_of(@maximum + 1, @high_message)
|
472
|
+
@maximum.nil? || allows_value_of(@maximum + 1, @high_message)
|
460
473
|
end
|
461
474
|
|
462
475
|
def disallows_higher_value
|
463
|
-
disallows_value_of(@maximum + 1, @high_message)
|
476
|
+
@maximum.nil? || disallows_value_of(@maximum + 1, @high_message)
|
464
477
|
end
|
465
478
|
|
466
479
|
def allows_all_values_in_array?
|