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
@@ -3,7 +3,7 @@ module Shoulda
|
|
3
3
|
module ActiveModel
|
4
4
|
# The `validate_length_of` matcher tests usage of the
|
5
5
|
# `validates_length_of` matcher. Note that this matcher is intended to be
|
6
|
-
# used against string columns and not integer columns.
|
6
|
+
# used against string columns and associations and not integer columns.
|
7
7
|
#
|
8
8
|
# #### Qualifiers
|
9
9
|
#
|
@@ -36,7 +36,8 @@ module Shoulda
|
|
36
36
|
#
|
37
37
|
# Use `is_at_least` to test usage of the `:minimum` option. This asserts
|
38
38
|
# that the attribute can take a string which is equal to or longer than
|
39
|
-
# the given length and cannot take a string which is shorter.
|
39
|
+
# the given length and cannot take a string which is shorter. This qualifier
|
40
|
+
# also works for associations.
|
40
41
|
#
|
41
42
|
# class User
|
42
43
|
# include ActiveModel::Model
|
@@ -61,7 +62,8 @@ module Shoulda
|
|
61
62
|
#
|
62
63
|
# Use `is_at_most` to test usage of the `:maximum` option. This asserts
|
63
64
|
# that the attribute can take a string which is equal to or shorter than
|
64
|
-
# the given length and cannot take a string which is longer.
|
65
|
+
# the given length and cannot take a string which is longer. This qualifier
|
66
|
+
# also works for associations.
|
65
67
|
#
|
66
68
|
# class User
|
67
69
|
# include ActiveModel::Model
|
@@ -84,7 +86,8 @@ module Shoulda
|
|
84
86
|
#
|
85
87
|
# Use `is_equal_to` to test usage of the `:is` option. This asserts that
|
86
88
|
# the attribute can take a string which is exactly equal to the given
|
87
|
-
# length and cannot take a string which is shorter or longer.
|
89
|
+
# length and cannot take a string which is shorter or longer. This qualifier
|
90
|
+
# also works for associations.
|
88
91
|
#
|
89
92
|
# class User
|
90
93
|
# include ActiveModel::Model
|
@@ -106,7 +109,7 @@ module Shoulda
|
|
106
109
|
# ##### is_at_least + is_at_most
|
107
110
|
#
|
108
111
|
# Use `is_at_least` and `is_at_most` together to test usage of the `:in`
|
109
|
-
# option.
|
112
|
+
# option. This qualifies also works for associations.
|
110
113
|
#
|
111
114
|
# class User
|
112
115
|
# include ActiveModel::Model
|
@@ -260,6 +263,29 @@ module Shoulda
|
|
260
263
|
# should validate_length_of(:bio).is_at_least(15).allow_blank
|
261
264
|
# end
|
262
265
|
#
|
266
|
+
# ##### as_array
|
267
|
+
#
|
268
|
+
# Use `as_array` if you have an ActiveModel model and the attribute being validated
|
269
|
+
# is designed to store an array. (This is not necessary if you have an ActiveRecord
|
270
|
+
# model with an array column, as the matcher will detect this automatically.)
|
271
|
+
#
|
272
|
+
# class User
|
273
|
+
# include ActiveModel::Model
|
274
|
+
# attribute :arr, array: true
|
275
|
+
#
|
276
|
+
# validates_length_of :arr, minimum: 15
|
277
|
+
# end
|
278
|
+
#
|
279
|
+
# # RSpec
|
280
|
+
# describe User do
|
281
|
+
# it { should validate_length_of(:arr).as_array.is_at_least(15) }
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# # Minitest (Shoulda)
|
285
|
+
# class UserTest < ActiveSupport::TestCase
|
286
|
+
# should validate_length_of(:arr).as_array.is_at_least(15)
|
287
|
+
# end
|
288
|
+
#
|
263
289
|
def validate_length_of(attr)
|
264
290
|
ValidateLengthOfMatcher.new(attr)
|
265
291
|
end
|
@@ -275,6 +301,11 @@ module Shoulda
|
|
275
301
|
@long_message = nil
|
276
302
|
end
|
277
303
|
|
304
|
+
def as_array
|
305
|
+
@options[:array] = true
|
306
|
+
self
|
307
|
+
end
|
308
|
+
|
278
309
|
def is_at_least(length)
|
279
310
|
@options[:minimum] = length
|
280
311
|
@short_message ||= :too_short
|
@@ -451,15 +482,39 @@ module Shoulda
|
|
451
482
|
end
|
452
483
|
|
453
484
|
def allows_length_of?(length, message)
|
454
|
-
allows_value_of(
|
485
|
+
allows_value_of(value_of_length(length), message)
|
455
486
|
end
|
456
487
|
|
457
488
|
def disallows_length_of?(length, message)
|
458
|
-
disallows_value_of(
|
489
|
+
disallows_value_of(value_of_length(length), message)
|
490
|
+
end
|
491
|
+
|
492
|
+
def value_of_length(length)
|
493
|
+
if array_column?
|
494
|
+
['x'] * length
|
495
|
+
elsif collection_association?
|
496
|
+
Array.new(length) { association_reflection.klass.new }
|
497
|
+
else
|
498
|
+
'x' * length
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def array_column?
|
503
|
+
@options[:array] || super
|
504
|
+
end
|
505
|
+
|
506
|
+
def collection_association?
|
507
|
+
association? && [:has_many, :has_and_belongs_to_many].include?(
|
508
|
+
association_reflection.macro,
|
509
|
+
)
|
510
|
+
end
|
511
|
+
|
512
|
+
def association?
|
513
|
+
association_reflection.present?
|
459
514
|
end
|
460
515
|
|
461
|
-
def
|
462
|
-
|
516
|
+
def association_reflection
|
517
|
+
model.try(:reflect_on_association, @attribute)
|
463
518
|
end
|
464
519
|
|
465
520
|
def translated_short_message
|
@@ -357,51 +357,33 @@ module Shoulda
|
|
357
357
|
end
|
358
358
|
|
359
359
|
# @private
|
360
|
-
class ValidateNumericalityOfMatcher
|
360
|
+
class ValidateNumericalityOfMatcher < ValidationMatcher
|
361
361
|
NUMERIC_NAME = 'number'.freeze
|
362
362
|
DEFAULT_DIFF_TO_COMPARE = 1
|
363
363
|
|
364
|
-
include Qualifiers::IgnoringInterferenceByWriter
|
365
|
-
|
366
364
|
attr_reader :diff_to_compare
|
367
365
|
|
368
366
|
def initialize(attribute)
|
369
367
|
super
|
370
|
-
@attribute = attribute
|
371
368
|
@submatchers = []
|
372
369
|
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
|
373
|
-
@expects_custom_validation_message = false
|
374
370
|
@expects_to_allow_nil = false
|
375
|
-
@expects_strict = false
|
376
371
|
@allowed_type_adjective = nil
|
377
372
|
@allowed_type_name = 'number'
|
378
|
-
@context = nil
|
379
|
-
@expected_message = nil
|
380
|
-
end
|
381
|
-
|
382
|
-
def strict
|
383
|
-
@expects_strict = true
|
384
|
-
self
|
385
|
-
end
|
386
373
|
|
387
|
-
|
388
|
-
@expects_strict
|
374
|
+
add_disallow_non_numeric_value_matcher
|
389
375
|
end
|
390
376
|
|
391
377
|
def only_integer
|
392
378
|
prepare_submatcher(
|
393
|
-
NumericalityMatchers::OnlyIntegerMatcher.new(self,
|
379
|
+
NumericalityMatchers::OnlyIntegerMatcher.new(self, attribute),
|
394
380
|
)
|
395
381
|
self
|
396
382
|
end
|
397
383
|
|
398
384
|
def allow_nil
|
399
385
|
@expects_to_allow_nil = true
|
400
|
-
prepare_submatcher(
|
401
|
-
AllowValueMatcher.new(nil).
|
402
|
-
for(@attribute).
|
403
|
-
with_message(:not_a_number),
|
404
|
-
)
|
386
|
+
prepare_submatcher(allow_value_matcher(nil, :not_a_number))
|
405
387
|
self
|
406
388
|
end
|
407
389
|
|
@@ -411,70 +393,55 @@ module Shoulda
|
|
411
393
|
|
412
394
|
def odd
|
413
395
|
prepare_submatcher(
|
414
|
-
NumericalityMatchers::OddNumberMatcher.new(self,
|
396
|
+
NumericalityMatchers::OddNumberMatcher.new(self, attribute),
|
415
397
|
)
|
416
398
|
self
|
417
399
|
end
|
418
400
|
|
419
401
|
def even
|
420
402
|
prepare_submatcher(
|
421
|
-
NumericalityMatchers::EvenNumberMatcher.new(self,
|
403
|
+
NumericalityMatchers::EvenNumberMatcher.new(self, attribute),
|
422
404
|
)
|
423
405
|
self
|
424
406
|
end
|
425
407
|
|
426
408
|
def is_greater_than(value)
|
427
|
-
prepare_submatcher(comparison_matcher_for(value, :>).for(
|
409
|
+
prepare_submatcher(comparison_matcher_for(value, :>).for(attribute))
|
428
410
|
self
|
429
411
|
end
|
430
412
|
|
431
413
|
def is_greater_than_or_equal_to(value)
|
432
|
-
prepare_submatcher(comparison_matcher_for(value, :>=).for(
|
414
|
+
prepare_submatcher(comparison_matcher_for(value, :>=).for(attribute))
|
433
415
|
self
|
434
416
|
end
|
435
417
|
|
436
418
|
def is_equal_to(value)
|
437
|
-
prepare_submatcher(comparison_matcher_for(value, :==).for(
|
419
|
+
prepare_submatcher(comparison_matcher_for(value, :==).for(attribute))
|
438
420
|
self
|
439
421
|
end
|
440
422
|
|
441
423
|
def is_less_than(value)
|
442
|
-
prepare_submatcher(comparison_matcher_for(value, :<).for(
|
424
|
+
prepare_submatcher(comparison_matcher_for(value, :<).for(attribute))
|
443
425
|
self
|
444
426
|
end
|
445
427
|
|
446
428
|
def is_less_than_or_equal_to(value)
|
447
|
-
prepare_submatcher(comparison_matcher_for(value, :<=).for(
|
429
|
+
prepare_submatcher(comparison_matcher_for(value, :<=).for(attribute))
|
448
430
|
self
|
449
431
|
end
|
450
432
|
|
451
433
|
def is_other_than(value)
|
452
|
-
prepare_submatcher(comparison_matcher_for(value, :!=).for(
|
434
|
+
prepare_submatcher(comparison_matcher_for(value, :!=).for(attribute))
|
453
435
|
self
|
454
436
|
end
|
455
437
|
|
456
438
|
def is_in(range)
|
457
439
|
prepare_submatcher(
|
458
|
-
NumericalityMatchers::RangeMatcher.new(self,
|
440
|
+
NumericalityMatchers::RangeMatcher.new(self, attribute, range),
|
459
441
|
)
|
460
442
|
self
|
461
443
|
end
|
462
444
|
|
463
|
-
def with_message(message)
|
464
|
-
@expects_custom_validation_message = true
|
465
|
-
@expected_message = message
|
466
|
-
self
|
467
|
-
end
|
468
|
-
|
469
|
-
def expects_custom_validation_message?
|
470
|
-
@expects_custom_validation_message
|
471
|
-
end
|
472
|
-
|
473
|
-
def on(context)
|
474
|
-
@context = context
|
475
|
-
self
|
476
|
-
end
|
477
|
-
|
478
445
|
def matches?(subject)
|
479
446
|
matches_or_does_not_match?(subject)
|
480
447
|
first_submatcher_that_fails_to_match.nil?
|
@@ -486,24 +453,18 @@ module Shoulda
|
|
486
453
|
end
|
487
454
|
|
488
455
|
def simple_description
|
489
|
-
|
456
|
+
String.new.tap do |description|
|
457
|
+
description << "validate that :#{attribute} looks like "
|
458
|
+
description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
|
490
459
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
if range_description.present?
|
495
|
-
description << " #{range_description}"
|
496
|
-
end
|
460
|
+
if range_description.present?
|
461
|
+
description << " #{range_description}"
|
462
|
+
end
|
497
463
|
|
498
|
-
|
499
|
-
|
464
|
+
if comparison_descriptions.present?
|
465
|
+
description << " #{comparison_descriptions}"
|
466
|
+
end
|
500
467
|
end
|
501
|
-
|
502
|
-
description
|
503
|
-
end
|
504
|
-
|
505
|
-
def description
|
506
|
-
ValidationMatcher::BuildDescription.call(self, simple_description)
|
507
468
|
end
|
508
469
|
|
509
470
|
def failure_message
|
@@ -532,44 +493,29 @@ module Shoulda
|
|
532
493
|
@subject = subject
|
533
494
|
@number_of_submatchers = @submatchers.size
|
534
495
|
|
535
|
-
add_disallow_value_matcher
|
536
496
|
qualify_submatchers
|
537
497
|
end
|
538
498
|
|
539
|
-
def overall_failure_message
|
540
|
-
Shoulda::Matchers.word_wrap(
|
541
|
-
"Expected #{model.name} to #{description}, but this could not "\
|
542
|
-
'be proved.',
|
543
|
-
)
|
544
|
-
end
|
545
|
-
|
546
|
-
def overall_failure_message_when_negated
|
547
|
-
Shoulda::Matchers.word_wrap(
|
548
|
-
"Expected #{model.name} not to #{description}, but this could not "\
|
549
|
-
'be proved.',
|
550
|
-
)
|
551
|
-
end
|
552
|
-
|
553
499
|
def attribute_is_active_record_column?
|
554
|
-
columns_hash.key?(
|
500
|
+
columns_hash.key?(attribute.to_s)
|
555
501
|
end
|
556
502
|
|
557
503
|
def column_type
|
558
|
-
columns_hash[
|
504
|
+
columns_hash[attribute.to_s].type
|
559
505
|
end
|
560
506
|
|
561
507
|
def columns_hash
|
562
|
-
if
|
563
|
-
|
508
|
+
if subject.class.respond_to?(:columns_hash)
|
509
|
+
subject.class.columns_hash
|
564
510
|
else
|
565
511
|
{}
|
566
512
|
end
|
567
513
|
end
|
568
514
|
|
569
|
-
def
|
515
|
+
def add_disallow_non_numeric_value_matcher
|
570
516
|
disallow_value_matcher = DisallowValueMatcher.
|
571
517
|
new(non_numeric_value).
|
572
|
-
for(
|
518
|
+
for(attribute).
|
573
519
|
with_message(:not_a_number)
|
574
520
|
|
575
521
|
add_submatcher(disallow_value_matcher)
|
@@ -581,9 +527,9 @@ module Shoulda
|
|
581
527
|
end
|
582
528
|
|
583
529
|
def comparison_matcher_for(value, operator)
|
584
|
-
|
530
|
+
ComparisonMatcher.
|
585
531
|
new(self, value, operator).
|
586
|
-
for(
|
532
|
+
for(attribute)
|
587
533
|
end
|
588
534
|
|
589
535
|
def add_submatcher(submatcher)
|
@@ -615,8 +561,8 @@ module Shoulda
|
|
615
561
|
submatcher.with_message(@expected_message)
|
616
562
|
end
|
617
563
|
|
618
|
-
if
|
619
|
-
submatcher.on(
|
564
|
+
if context
|
565
|
+
submatcher.on(context)
|
620
566
|
end
|
621
567
|
|
622
568
|
submatcher.ignoring_interference_by_writer(
|
@@ -634,23 +580,25 @@ module Shoulda
|
|
634
580
|
end
|
635
581
|
|
636
582
|
def has_been_qualified?
|
637
|
-
@submatchers.any?
|
638
|
-
|
639
|
-
|
640
|
-
|
583
|
+
@submatchers.any? { |submatcher| submatcher_qualified?(submatcher) }
|
584
|
+
end
|
585
|
+
|
586
|
+
def submatcher_qualified?(submatcher)
|
587
|
+
Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
|
588
|
+
NumericalityMatchers || submatcher.instance_of?(ComparisonMatcher)
|
641
589
|
end
|
642
590
|
|
643
591
|
def first_submatcher_that_fails_to_match
|
644
592
|
@_first_submatcher_that_fails_to_match ||=
|
645
593
|
@submatchers.detect do |submatcher|
|
646
|
-
!submatcher.matches?(
|
594
|
+
!submatcher.matches?(subject)
|
647
595
|
end
|
648
596
|
end
|
649
597
|
|
650
598
|
def first_submatcher_that_fails_to_not_match
|
651
599
|
@_first_submatcher_that_fails_to_not_match ||=
|
652
600
|
@submatchers.detect do |submatcher|
|
653
|
-
!submatcher.does_not_match?(
|
601
|
+
!submatcher.does_not_match?(subject)
|
654
602
|
end
|
655
603
|
end
|
656
604
|
|
@@ -719,10 +667,6 @@ module Shoulda
|
|
719
667
|
range_submatcher&.range_description
|
720
668
|
end
|
721
669
|
|
722
|
-
def model
|
723
|
-
@subject.class
|
724
|
-
end
|
725
|
-
|
726
670
|
def non_numeric_value
|
727
671
|
'abcd'
|
728
672
|
end
|
@@ -42,13 +42,12 @@ module Shoulda
|
|
42
42
|
description_clauses = []
|
43
43
|
|
44
44
|
if matcher.try(:expects_strict?)
|
45
|
-
description_clauses <<
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
description_clauses.last << ' on failure'
|
45
|
+
description_clauses <<
|
46
|
+
if matcher.try(:expects_custom_validation_message?)
|
47
|
+
'raising a validation exception with a custom message on failure'
|
48
|
+
else
|
49
|
+
'raising a validation exception on failure'
|
50
|
+
end
|
52
51
|
elsif matcher.try(:expects_custom_validation_message?)
|
53
52
|
description_clauses <<
|
54
53
|
'producing a custom validation error on failure'
|
@@ -186,6 +186,12 @@ module Shoulda
|
|
186
186
|
def blank_values
|
187
187
|
['', ' ', "\n", "\r", "\t", "\f"]
|
188
188
|
end
|
189
|
+
|
190
|
+
def array_column?
|
191
|
+
@subject.class.respond_to?(:columns_hash) &&
|
192
|
+
@subject.class.columns_hash[@attribute.to_s].respond_to?(:array) &&
|
193
|
+
@subject.class.columns_hash[@attribute.to_s].array
|
194
|
+
end
|
189
195
|
end
|
190
196
|
end
|
191
197
|
end
|
@@ -21,8 +21,9 @@ require 'shoulda/matchers/active_model/validate_presence_of_matcher'
|
|
21
21
|
require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
22
22
|
require 'shoulda/matchers/active_model/validate_confirmation_of_matcher'
|
23
23
|
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
24
|
+
require 'shoulda/matchers/active_model/validate_comparison_of_matcher'
|
25
|
+
require 'shoulda/matchers/active_model/comparison_matcher'
|
24
26
|
require 'shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher'
|
25
|
-
require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
|
26
27
|
require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
|
27
28
|
require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
|
28
29
|
require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
|