shoulda-matchers 3.1.3 → 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. checksums.yaml +4 -4
  2. data/.hound/ruby.yml +336 -316
  3. data/.python-version +1 -0
  4. data/.rubocop.yml +3 -1
  5. data/.travis.yml +7 -6
  6. data/Appraisals +76 -44
  7. data/CONTRIBUTING.md +137 -66
  8. data/Gemfile +5 -5
  9. data/Gemfile.lock +30 -35
  10. data/MAINTAINING.md +250 -0
  11. data/MIT-LICENSE +1 -1
  12. data/NEWS.md +176 -4
  13. data/README.md +138 -200
  14. data/Rakefile +7 -0
  15. data/bin/setup +190 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +4 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +0 -6
  18. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +0 -17
  19. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +27 -0
  20. data/gemfiles/4.2.gemfile +21 -20
  21. data/gemfiles/4.2.gemfile.lock +143 -140
  22. data/gemfiles/5.0.gemfile +37 -0
  23. data/gemfiles/5.0.gemfile.lock +238 -0
  24. data/gemfiles/5.1.gemfile +38 -0
  25. data/gemfiles/5.1.gemfile.lock +254 -0
  26. data/gemfiles/5.2.gemfile +40 -0
  27. data/gemfiles/5.2.gemfile.lock +273 -0
  28. data/lib/shoulda/matchers/action_controller/callback_matcher.rb +18 -6
  29. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -1
  30. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +1 -1
  31. data/lib/shoulda/matchers/action_controller/route_matcher.rb +87 -27
  32. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +1 -0
  33. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +0 -4
  34. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +5 -0
  35. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -0
  36. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +26 -11
  37. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +39 -4
  38. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +116 -47
  39. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +127 -38
  40. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +55 -37
  41. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +30 -1
  42. data/lib/shoulda/matchers/active_model/validation_matcher.rb +11 -4
  43. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +11 -6
  44. data/lib/shoulda/matchers/active_record.rb +3 -0
  45. data/lib/shoulda/matchers/active_record/association_matcher.rb +172 -22
  46. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +1 -1
  47. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +11 -6
  48. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +46 -0
  49. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +51 -0
  50. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +268 -38
  51. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +1 -1
  52. data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +111 -0
  53. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +207 -79
  54. data/lib/shoulda/matchers/doublespeak/object_double.rb +5 -1
  55. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +100 -21
  56. data/lib/shoulda/matchers/rails_shim.rb +133 -52
  57. data/lib/shoulda/matchers/routing.rb +2 -2
  58. data/lib/shoulda/matchers/util.rb +23 -1
  59. data/lib/shoulda/matchers/util/word_wrap.rb +6 -2
  60. data/lib/shoulda/matchers/version.rb +1 -1
  61. data/script/install_gems_in_all_appraisals +3 -1
  62. data/script/run_all_tests +3 -1
  63. data/script/supported_ruby_versions +7 -0
  64. data/script/update_gem_in_all_appraisals +3 -1
  65. data/script/update_gems_in_all_appraisals +3 -1
  66. data/shoulda-matchers.gemspec +3 -3
  67. data/spec/acceptance/independent_matchers_spec.rb +2 -2
  68. data/spec/acceptance/multiple_libraries_integration_spec.rb +1 -1
  69. data/spec/acceptance/rails_integration_spec.rb +2 -2
  70. data/spec/spec_helper.rb +2 -3
  71. data/spec/support/acceptance/helpers.rb +2 -0
  72. data/spec/support/acceptance/helpers/command_helpers.rb +17 -4
  73. data/spec/support/acceptance/helpers/rails_migration_helpers.rb +21 -0
  74. data/spec/support/acceptance/helpers/step_helpers.rb +1 -1
  75. data/spec/support/tests/current_bundle.rb +3 -9
  76. data/spec/support/tests/filesystem.rb +2 -2
  77. data/spec/support/unit/attribute.rb +0 -2
  78. data/spec/support/unit/capture.rb +9 -3
  79. data/spec/support/unit/helpers/action_pack_versions.rb +22 -0
  80. data/spec/support/unit/helpers/active_model_versions.rb +4 -0
  81. data/spec/support/unit/helpers/active_record_versions.rb +22 -2
  82. data/spec/support/unit/helpers/active_resource_builder.rb +2 -2
  83. data/spec/support/unit/helpers/controller_builder.rb +1 -1
  84. data/spec/support/unit/helpers/message_helpers.rb +19 -0
  85. data/spec/support/unit/helpers/rails_versions.rb +14 -0
  86. data/spec/support/unit/matchers/fail_with_message_matcher.rb +7 -5
  87. data/spec/support/unit/matchers/print_warning_including.rb +21 -13
  88. data/spec/support/unit/model_creation_strategies/active_record.rb +1 -1
  89. data/spec/support/unit/model_creators/active_record.rb +0 -1
  90. data/spec/support/unit/model_creators/basic.rb +7 -2
  91. data/spec/support/unit/rails_application.rb +25 -0
  92. data/spec/support/unit/record_validating_confirmation_builder.rb +5 -2
  93. data/spec/support/unit/validation_matcher_scenario.rb +0 -2
  94. data/spec/unit/shoulda/matchers/action_controller/callback_matcher_spec.rb +18 -18
  95. data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +33 -5
  96. data/spec/unit/shoulda/matchers/action_controller/render_template_matcher_spec.rb +1 -1
  97. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +80 -78
  98. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +7 -9
  99. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +28 -4
  100. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +19 -1
  101. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +27 -4
  102. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +62 -5
  103. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +52 -18
  104. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +51 -4
  105. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +99 -71
  106. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +41 -15
  107. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +445 -15
  108. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +615 -93
  109. data/spec/unit/shoulda/matchers/active_record/have_secure_token_matcher_spec.rb +169 -0
  110. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +167 -97
  111. data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +2 -4
  112. data/spec/unit/shoulda/matchers/independent/delegate_method_matcher_spec.rb +152 -19
  113. data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +258 -94
  114. data/spec/unit_spec_helper.rb +9 -1
  115. data/zeus.json +1 -1
  116. metadata +31 -16
  117. data/gemfiles/4.0.0.gemfile +0 -38
  118. data/gemfiles/4.0.0.gemfile.lock +0 -223
  119. data/gemfiles/4.0.1.gemfile +0 -38
  120. data/gemfiles/4.0.1.gemfile.lock +0 -225
  121. data/gemfiles/4.1.gemfile +0 -38
  122. data/gemfiles/4.1.gemfile.lock +0 -220
  123. data/script/SUPPORTED_VERSIONS +0 -1
@@ -10,7 +10,7 @@ module Shoulda
10
10
  # pre-existing record (thereby failing the uniqueness check).
11
11
  #
12
12
  # class Post < ActiveRecord::Base
13
- # validates_uniqueness_of :permalink
13
+ # validates :permalink, uniqueness: true
14
14
  # end
15
15
  #
16
16
  # # RSpec
@@ -80,19 +80,19 @@ module Shoulda
80
80
  #
81
81
  # RSpec.describe Post, type: :model do
82
82
  # describe "validations" do
83
- # subject { Post.new(content: "Here is the content") }
83
+ # subject { Post.create(content: "Here is the content") }
84
84
  # it { should validate_uniqueness_of(:title) }
85
85
  # end
86
86
  # end
87
87
  #
88
88
  # Or, if you're using
89
- # [FactoryGirl](http://github.com/thoughtbot/factory_girl) and you have a
89
+ # [FactoryBot](https://github.com/thoughtbot/factory_bot) and you have a
90
90
  # `post` factory defined which automatically fills in `content`, you can
91
91
  # say:
92
92
  #
93
93
  # RSpec.describe Post, type: :model do
94
94
  # describe "validations" do
95
- # subject { FactoryGirl.build(:post) }
95
+ # subject { FactoryBot.create(:post) }
96
96
  # it { should validate_uniqueness_of(:title) }
97
97
  # end
98
98
  # end
@@ -102,7 +102,7 @@ module Shoulda
102
102
  # Use `on` if your validation applies only under a certain context.
103
103
  #
104
104
  # class Post < ActiveRecord::Base
105
- # validates_uniqueness_of :title, on: :create
105
+ # validates :title, uniqueness: true, on: :create
106
106
  # end
107
107
  #
108
108
  # # RSpec
@@ -120,7 +120,7 @@ module Shoulda
120
120
  # Use `with_message` if you are using a custom validation message.
121
121
  #
122
122
  # class Post < ActiveRecord::Base
123
- # validates_uniqueness_of :title, message: 'Please choose another title'
123
+ # validates :title, uniqueness: true, message: 'Please choose another title'
124
124
  # end
125
125
  #
126
126
  # # RSpec
@@ -144,7 +144,7 @@ module Shoulda
144
144
  # unique, but the scoped attributes are not unique either.
145
145
  #
146
146
  # class Post < ActiveRecord::Base
147
- # validates_uniqueness_of :slug, scope: :journal_id
147
+ # validates :slug, uniqueness: true, scope: :journal_id
148
148
  # end
149
149
  #
150
150
  # # RSpec
@@ -165,7 +165,7 @@ module Shoulda
165
165
  # attributes in the pre-existing record.
166
166
  #
167
167
  # class Post < ActiveRecord::Base
168
- # validates_uniqueness_of :key, case_sensitive: false
168
+ # validates :key, uniqueness: { case_sensitive: false }
169
169
  # end
170
170
  #
171
171
  # # RSpec
@@ -193,7 +193,7 @@ module Shoulda
193
193
  # attribute.
194
194
  #
195
195
  # class User < ActiveRecord::Base
196
- # validates_uniqueness_of :email
196
+ # validates :email, uniqueness: true
197
197
  #
198
198
  # def email=(value)
199
199
  # super(value.downcase)
@@ -217,7 +217,7 @@ module Shoulda
217
217
  # Use `allow_nil` to assert that the attribute allows nil.
218
218
  #
219
219
  # class Post < ActiveRecord::Base
220
- # validates_uniqueness_of :author_id, allow_nil: true
220
+ # validates :author_id, uniqueness: true, allow_nil: true
221
221
  # end
222
222
  #
223
223
  # # RSpec
@@ -237,7 +237,7 @@ module Shoulda
237
237
  # Use `allow_blank` to assert that the attribute allows a blank value.
238
238
  #
239
239
  # class Post < ActiveRecord::Base
240
- # validates_uniqueness_of :author_id, allow_blank: true
240
+ # validates :author_id, uniqueness: true, allow_blank: true
241
241
  # end
242
242
  #
243
243
  # # RSpec
@@ -296,7 +296,7 @@ module Shoulda
296
296
  end
297
297
 
298
298
  def expects_to_allow_nil?
299
- @options[:allow_nil]
299
+ @options[:allow_nil] == true
300
300
  end
301
301
 
302
302
  def allow_blank
@@ -305,7 +305,7 @@ module Shoulda
305
305
  end
306
306
 
307
307
  def expects_to_allow_blank?
308
- @options[:allow_blank]
308
+ @options[:allow_blank] == true
309
309
  end
310
310
 
311
311
  def simple_description
@@ -324,14 +324,29 @@ module Shoulda
324
324
  @given_record = given_record
325
325
  @all_records = model.all
326
326
 
327
- validate_attribute_present_on_model? &&
328
- validate_scopes_present_on_model? &&
329
- validate_scopes_match? &&
330
- validate_two_records_with_same_non_blank_value_cannot_coexist? &&
331
- validate_case_sensitivity? &&
332
- validate_after_scope_change? &&
333
- allows_nil? &&
334
- allows_blank?
327
+ matches_presence_of_attribute? &&
328
+ matches_presence_of_scopes? &&
329
+ matches_scopes_configuration? &&
330
+ matches_uniqueness_without_scopes? &&
331
+ matches_uniqueness_with_case_sensitivity_strategy? &&
332
+ matches_uniqueness_with_scopes? &&
333
+ matches_allow_nil? &&
334
+ matches_allow_blank?
335
+ ensure
336
+ Uniqueness::TestModels.remove_all
337
+ end
338
+
339
+ def does_not_match?(given_record)
340
+ @given_record = given_record
341
+ @all_records = model.all
342
+
343
+ does_not_match_presence_of_scopes? ||
344
+ does_not_match_scopes_configuration? ||
345
+ does_not_match_uniqueness_without_scopes? ||
346
+ does_not_match_uniqueness_with_case_sensitivity_strategy? ||
347
+ does_not_match_uniqueness_with_scopes? ||
348
+ does_not_match_allow_nil? ||
349
+ does_not_match_allow_blank?
335
350
  ensure
336
351
  Uniqueness::TestModels.remove_all
337
352
  end
@@ -386,26 +401,45 @@ module Shoulda
386
401
  end
387
402
  end
388
403
 
389
- def validate_scopes_match?
404
+ def matches_scopes_configuration?
390
405
  if scopes_match?
391
406
  true
392
407
  else
393
- @failure_reason = 'Expected the validation'
408
+ @failure_reason = 'Expected the validation '
394
409
 
395
410
  if expected_scopes.empty?
396
- @failure_reason << ' not to be scoped to anything'
411
+ @failure_reason << 'not to be scoped to anything, '
397
412
  else
398
- @failure_reason << " to be scoped to #{inspected_expected_scopes}"
413
+ @failure_reason << "to be scoped to #{inspected_expected_scopes}, "
399
414
  end
400
415
 
401
- if actual_sets_of_scopes.empty?
402
- @failure_reason << ', but it was not scoped to anything.'
416
+ if actual_sets_of_scopes.any?
417
+ @failure_reason << 'but it was scoped to '
418
+ @failure_reason << "#{inspected_actual_scopes} instead."
403
419
  else
404
- @failure_reason << ', but it was scoped to '
420
+ @failure_reason << 'but it was not scoped to anything.'
421
+ end
422
+
423
+ false
424
+ end
425
+ end
426
+
427
+ def does_not_match_scopes_configuration?
428
+ if scopes_match?
429
+ @failure_reason = 'Expected the validation '
430
+
431
+ if expected_scopes.empty?
432
+ @failure_reason << 'to be scoped to nothing, '
433
+ @failure_reason << 'but it was scoped to '
405
434
  @failure_reason << "#{inspected_actual_scopes} instead."
435
+ else
436
+ @failure_reason << 'not to be scoped to '
437
+ @failure_reason << inspected_expected_scopes
406
438
  end
407
439
 
408
440
  false
441
+ else
442
+ true
409
443
  end
410
444
  end
411
445
 
@@ -420,8 +454,8 @@ module Shoulda
420
454
 
421
455
  def inspected_actual_scopes
422
456
  inspected_actual_sets_of_scopes.to_sentence(
423
- words_connector: " or ",
424
- last_word_connector: ", or"
457
+ words_connector: ' and ',
458
+ last_word_connector: ', and'
425
459
  )
426
460
  end
427
461
 
@@ -447,22 +481,32 @@ module Shoulda
447
481
  end.reject(&:empty?)
448
482
  end
449
483
 
450
- def allows_nil?
451
- if expects_to_allow_nil?
452
- update_existing_record!(nil)
484
+ def matches_allow_nil?
485
+ !expects_to_allow_nil? || (
486
+ update_existing_record!(nil) &&
453
487
  allows_value_of(nil, @expected_message)
454
- else
455
- true
456
- end
488
+ )
489
+ end
490
+
491
+ def does_not_match_allow_nil?
492
+ expects_to_allow_nil? && (
493
+ update_existing_record!(nil) &&
494
+ (@failure_reason = nil || disallows_value_of(nil, @expected_message))
495
+ )
457
496
  end
458
497
 
459
- def allows_blank?
460
- if expects_to_allow_blank?
461
- update_existing_record!('')
498
+ def matches_allow_blank?
499
+ !expects_to_allow_blank? || (
500
+ update_existing_record!('') &&
462
501
  allows_value_of('', @expected_message)
463
- else
464
- true
465
- end
502
+ )
503
+ end
504
+
505
+ def does_not_match_allow_blank?
506
+ expects_to_allow_blank? && (
507
+ update_existing_record!('') &&
508
+ (@failure_reason = nil || disallows_value_of('', @expected_message))
509
+ )
466
510
  end
467
511
 
468
512
  def existing_record
@@ -515,13 +559,16 @@ module Shoulda
515
559
  # but that would break users' existing tests
516
560
  existing_record.save(validate: false)
517
561
  end
562
+
563
+ true
518
564
  end
519
565
 
520
566
  def arbitrary_non_blank_value
567
+ non_blank_value = dummy_value_for(@attribute)
521
568
  limit = column_limit_for(@attribute)
522
- non_blank_value = 'an arbitrary value'
523
569
 
524
- if limit && limit < non_blank_value.length
570
+ is_string_value = non_blank_value.is_a?(String)
571
+ if is_string_value && limit && limit < non_blank_value.length
525
572
  'x' * limit
526
573
  else
527
574
  non_blank_value
@@ -547,7 +594,7 @@ module Shoulda
547
594
  @new_record
548
595
  end
549
596
 
550
- def validate_attribute_present_on_model?
597
+ def matches_presence_of_attribute?
551
598
  if attribute_present_on_model?
552
599
  true
553
600
  else
@@ -557,20 +604,32 @@ module Shoulda
557
604
  end
558
605
  end
559
606
 
607
+ def does_not_match_presence_of_attribute?
608
+ if attribute_present_on_model?
609
+ @failure_reason =
610
+ ":#{attribute} seems to be an attribute on #{model.name}."
611
+ false
612
+ else
613
+ true
614
+ end
615
+ end
616
+
560
617
  def attribute_present_on_model?
561
618
  model.method_defined?("#{attribute}=") ||
562
619
  model.columns_hash.key?(attribute.to_s)
563
620
  end
564
621
 
565
- def validate_scopes_present_on_model?
566
- if all_scopes_present_on_model?
622
+ def matches_presence_of_scopes?
623
+ if scopes_missing_on_model.none?
567
624
  true
568
625
  else
626
+ inspected_scopes = scopes_missing_on_model.map(&:inspect)
627
+
569
628
  reason = ''
570
629
 
571
- reason << inspected_scopes_missing_on_model.to_sentence
630
+ reason << inspected_scopes.to_sentence
572
631
 
573
- if inspected_scopes_missing_on_model.many?
632
+ if inspected_scopes.many?
574
633
  reason << " do not seem to be attributes"
575
634
  else
576
635
  reason << " does not seem to be an attribute"
@@ -584,8 +643,34 @@ module Shoulda
584
643
  end
585
644
  end
586
645
 
587
- def all_scopes_present_on_model?
588
- scopes_missing_on_model.none?
646
+ def does_not_match_presence_of_scopes?
647
+ if scopes_missing_on_model.any?
648
+ true
649
+ else
650
+ inspected_scopes = scopes_present_on_model.map(&:inspect)
651
+
652
+ reason = ''
653
+
654
+ reason << inspected_scopes.to_sentence
655
+
656
+ if inspected_scopes.many?
657
+ reason << " seem to be attributes"
658
+ else
659
+ reason << " seems to be an attribute"
660
+ end
661
+
662
+ reason << " on #{model.name}."
663
+
664
+ @failure_reason = reason
665
+
666
+ false
667
+ end
668
+ end
669
+
670
+ def scopes_present_on_model
671
+ @_present_scopes ||= expected_scopes.select do |scope|
672
+ model.method_defined?("#{scope}=")
673
+ end
589
674
  end
590
675
 
591
676
  def scopes_missing_on_model
@@ -594,20 +679,26 @@ module Shoulda
594
679
  end
595
680
  end
596
681
 
597
- def inspected_scopes_missing_on_model
598
- scopes_missing_on_model.map(&:inspect)
682
+ def matches_uniqueness_without_scopes?
683
+ if existing_value_read.blank?
684
+ update_existing_record!(arbitrary_non_blank_value)
685
+ end
686
+
687
+ disallows_value_of(existing_value_read, @expected_message)
599
688
  end
600
689
 
601
- def validate_two_records_with_same_non_blank_value_cannot_coexist?
690
+ def does_not_match_uniqueness_without_scopes?
691
+ @failure_reason = nil
692
+
602
693
  if existing_value_read.blank?
603
694
  update_existing_record!(arbitrary_non_blank_value)
604
695
  end
605
696
 
606
- disallows_value_of(existing_value_read, @expected_message)
697
+ allows_value_of(existing_value_read, @expected_message)
607
698
  end
608
699
 
609
- def validate_case_sensitivity?
610
- if should_validate_case_sensitivity?
700
+ def matches_uniqueness_with_case_sensitivity_strategy?
701
+ if should_test_case_sensitivity?
611
702
  value = existing_value_read
612
703
  swapcased_value = value.swapcase
613
704
 
@@ -629,7 +720,32 @@ module Shoulda
629
720
  end
630
721
  end
631
722
 
632
- def should_validate_case_sensitivity?
723
+ def does_not_match_uniqueness_with_case_sensitivity_strategy?
724
+ if should_test_case_sensitivity?
725
+ @failure_reason = nil
726
+
727
+ value = existing_value_read
728
+ swapcased_value = value.swapcase
729
+
730
+ if case_sensitivity_strategy == :sensitive
731
+ disallows_value_of(swapcased_value, @expected_message)
732
+ else
733
+ if value == swapcased_value
734
+ raise NonCaseSwappableValueError.create(
735
+ model: model,
736
+ attribute: @attribute,
737
+ value: value
738
+ )
739
+ end
740
+
741
+ allows_value_of(swapcased_value, @expected_message)
742
+ end
743
+ else
744
+ true
745
+ end
746
+ end
747
+
748
+ def should_test_case_sensitivity?
633
749
  case_sensitivity_strategy != :ignore &&
634
750
  existing_value_read.respond_to?(:swapcase) &&
635
751
  !existing_value_read.empty?
@@ -641,30 +757,42 @@ module Shoulda
641
757
  false
642
758
  end
643
759
 
644
- def validate_after_scope_change?
645
- if expected_scopes.empty? || all_scopes_are_booleans?
646
- true
647
- else
760
+ def matches_uniqueness_with_scopes?
761
+ expected_scopes.none? ||
762
+ all_scopes_are_booleans? ||
648
763
  expected_scopes.all? do |scope|
649
- previous_value = @all_records.map(&scope).compact.max
650
-
651
- next_value =
652
- if previous_value.blank?
653
- dummy_value_for(scope)
654
- else
655
- next_value_for(scope, previous_value)
656
- end
657
-
658
- set_attribute_on_new_record!(scope, next_value)
659
-
660
- if allows_value_of(existing_value_read, @expected_message)
661
- set_attribute_on_new_record!(scope, previous_value)
662
- true
663
- else
664
- false
764
+ setting_next_value_for(scope) do
765
+ allows_value_of(existing_value_read, @expected_message)
665
766
  end
666
767
  end
667
- end
768
+ end
769
+
770
+ def does_not_match_uniqueness_with_scopes?
771
+ expected_scopes.any? &&
772
+ !all_scopes_are_booleans? &&
773
+ expected_scopes.any? do |scope|
774
+ setting_next_value_for(scope) do
775
+ @failure_reason = nil
776
+ disallows_value_of(existing_value_read, @expected_message)
777
+ end
778
+ end
779
+ end
780
+
781
+ def setting_next_value_for(scope)
782
+ previous_value = @all_records.map(&scope).compact.max
783
+
784
+ next_value =
785
+ if previous_value.blank?
786
+ dummy_value_for(scope)
787
+ else
788
+ next_value_for(scope, previous_value)
789
+ end
790
+
791
+ set_attribute_on_new_record!(scope, next_value)
792
+
793
+ yield
794
+ ensure
795
+ set_attribute_on_new_record!(scope, previous_value)
668
796
  end
669
797
 
670
798
  def dummy_value_for(scope)