shoulda-matchers 5.3.0 → 6.2.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +39 -15
  4. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
  5. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
  6. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
  7. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  8. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
  9. data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +1 -1
  10. data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +16 -6
  11. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +0 -6
  12. data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
  13. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +3 -3
  14. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +24 -11
  15. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +64 -9
  16. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +40 -96
  17. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
  18. data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
  19. data/lib/shoulda/matchers/active_model/validator.rb +4 -0
  20. data/lib/shoulda/matchers/active_model.rb +2 -1
  21. data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
  22. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +9 -1
  23. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -0
  24. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +4 -0
  25. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +23 -19
  26. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
  27. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +0 -8
  28. data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  29. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
  30. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
  31. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
  32. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
  33. data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  34. data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -1
  35. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
  36. data/lib/shoulda/matchers/active_record.rb +2 -0
  37. data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
  38. data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
  39. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
  40. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +7 -5
  41. data/lib/shoulda/matchers/integrations/libraries/routing.rb +5 -3
  42. data/lib/shoulda/matchers/rails_shim.rb +8 -6
  43. data/lib/shoulda/matchers/util/word_wrap.rb +1 -1
  44. data/lib/shoulda/matchers/util.rb +18 -20
  45. data/lib/shoulda/matchers/version.rb +1 -1
  46. data/lib/shoulda/matchers.rb +2 -2
  47. data/shoulda-matchers.gemspec +1 -1
  48. metadata +11 -8
  49. 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 blacklist of values, and inversely, can take values outside of
6
+ # take a blocklist of values, and inversely, can take values outside of
7
7
  # this list.
8
8
  #
9
- # If your blacklist is an array of values, use `in_array`:
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 blacklist is a range of values, use `in_range`:
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 whitelist of values and cannot take values outside of this list.
12
+ # take a allowlist of values and cannot take values outside of this list.
12
13
  #
13
- # If your whitelist is an array of values, use `in_array`:
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 whitelist is a range of values, use `in_range`:
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 = range.first
312
- @maximum = range.max
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?