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.
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?