shoulda-matchers 5.1.0 → 6.4.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +41 -18
  4. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
  5. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +1 -1
  6. data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
  7. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
  8. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  9. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
  10. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +3 -5
  11. data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  12. data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  13. data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  14. data/lib/shoulda/matchers/active_model/qualifiers.rb +1 -0
  15. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -7
  16. data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
  17. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +5 -5
  18. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +29 -14
  19. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +65 -10
  20. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +76 -86
  21. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +29 -4
  22. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
  23. data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
  24. data/lib/shoulda/matchers/active_model/validator.rb +4 -0
  25. data/lib/shoulda/matchers/active_model.rb +4 -1
  26. data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
  27. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +34 -4
  28. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +9 -1
  29. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +1 -0
  30. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +4 -0
  31. data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +23 -19
  32. data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
  33. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +338 -30
  34. data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  35. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
  36. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
  37. data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
  38. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
  39. data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  40. data/lib/shoulda/matchers/active_record/uniqueness/model.rb +13 -1
  41. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
  42. data/lib/shoulda/matchers/active_record.rb +2 -0
  43. data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
  44. data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
  45. data/lib/shoulda/matchers/doublespeak.rb +0 -1
  46. data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
  47. data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +7 -5
  48. data/lib/shoulda/matchers/integrations/libraries/routing.rb +5 -3
  49. data/lib/shoulda/matchers/rails_shim.rb +22 -6
  50. data/lib/shoulda/matchers/util/word_wrap.rb +2 -2
  51. data/lib/shoulda/matchers/util.rb +18 -20
  52. data/lib/shoulda/matchers/version.rb +1 -1
  53. data/lib/shoulda/matchers.rb +2 -2
  54. data/shoulda-matchers.gemspec +2 -2
  55. metadata +15 -9
  56. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -157
@@ -144,6 +144,28 @@ module Shoulda
144
144
  # with_foreign_key('country_id')
145
145
  # end
146
146
  #
147
+ # ##### with_foreign_type
148
+ #
149
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
150
+ #
151
+ # class Visitor < ActiveRecord::Base
152
+ # belongs_to :location, foreign_type: 'facility_type', polymorphic: true
153
+ # end
154
+ #
155
+ # # RSpec
156
+ # RSpec.describe Visitor, type: :model do
157
+ # it do
158
+ # should belong_to(:location).
159
+ # with_foreign_type('facility_type')
160
+ # end
161
+ # end
162
+ #
163
+ # # Minitest (Shoulda)
164
+ # class VisitorTest < ActiveSupport::TestCase
165
+ # should belong_to(:location).
166
+ # with_foreign_type('facility_type')
167
+ # end
168
+ #
147
169
  # ##### dependent
148
170
  #
149
171
  # Use `dependent` to assert that the `:dependent` option was specified.
@@ -217,6 +239,36 @@ module Shoulda
217
239
  # should belong_to(:organization).touch(true)
218
240
  # end
219
241
  #
242
+ # ##### strict_loading
243
+ #
244
+ # Use `strict_loading` to assert that the `:strict_loading` option was specified.
245
+ #
246
+ # class Organization < ActiveRecord::Base
247
+ # has_many :people, strict_loading: true
248
+ # end
249
+ #
250
+ # # RSpec
251
+ # RSpec.describe Organization, type: :model do
252
+ # it { should have_many(:people).strict_loading(true) }
253
+ # end
254
+ #
255
+ # # Minitest (Shoulda)
256
+ # class OrganizationTest < ActiveSupport::TestCase
257
+ # should have_many(:people).strict_loading(true)
258
+ # end
259
+ #
260
+ # Default value is true when no argument is specified
261
+ #
262
+ # # RSpec
263
+ # RSpec.describe Organization, type: :model do
264
+ # it { should have_many(:people).strict_loading }
265
+ # end
266
+ #
267
+ # # Minitest (Shoulda)
268
+ # class OrganizationTest < ActiveSupport::TestCase
269
+ # should have_many(:people).strict_loading
270
+ # end
271
+ #
220
272
  # ##### autosave
221
273
  #
222
274
  # Use `autosave` to assert that the `:autosave` option was specified.
@@ -326,6 +378,316 @@ module Shoulda
326
378
  AssociationMatcher.new(:belongs_to, name)
327
379
  end
328
380
 
381
+ # The `have_delegated_type` matcher is used to ensure that a `belong_to` association
382
+ # exists on your model using the delegated_type macro.
383
+ #
384
+ # class Vehicle < ActiveRecord::Base
385
+ # delegated_type :drivable, types: %w(Car Truck)
386
+ # end
387
+ #
388
+ # # RSpec
389
+ # RSpec.describe Vehicle, type: :model do
390
+ # it { should have_delegated_type(:drivable) }
391
+ # end
392
+ #
393
+ # # Minitest (Shoulda)
394
+ # class VehicleTest < ActiveSupport::TestCase
395
+ # should have_delegated_type(:drivable)
396
+ # end
397
+ #
398
+ # #### Qualifiers
399
+ #
400
+ # ##### types
401
+ #
402
+ # Use `types` to test the types that are allowed for the association.
403
+ #
404
+ # class Vehicle < ActiveRecord::Base
405
+ # delegated_type :drivable, types: %w(Car Truck)
406
+ # end
407
+ #
408
+ # # RSpec
409
+ # RSpec.describe Vehicle, type: :model do
410
+ # it do
411
+ # should have_delegated_type(:drivable).
412
+ # types(%w(Car Truck))
413
+ # end
414
+ # end
415
+ #
416
+ # # Minitest (Shoulda)
417
+ # class VehicleTest < ActiveSupport::TestCase
418
+ # should have_delegated_type(:drivable).
419
+ # types(%w(Car Truck))
420
+ # end
421
+ #
422
+ # ##### conditions
423
+ #
424
+ # Use `conditions` if your association is defined with a scope that sets
425
+ # the `where` clause.
426
+ #
427
+ # class Vehicle < ActiveRecord::Base
428
+ # delegated_type :drivable, types: %w(Car Truck), scope: -> { where(with_wheels: true) }
429
+ # end
430
+ #
431
+ # # RSpec
432
+ # RSpec.describe Vehicle, type: :model do
433
+ # it do
434
+ # should have_delegated_type(:drivable).
435
+ # conditions(with_wheels: true)
436
+ # end
437
+ # end
438
+ #
439
+ # # Minitest (Shoulda)
440
+ # class VehicleTest < ActiveSupport::TestCase
441
+ # should have_delegated_type(:drivable).
442
+ # conditions(everyone_is_perfect: false)
443
+ # end
444
+ #
445
+ # ##### order
446
+ #
447
+ # Use `order` if your association is defined with a scope that sets the
448
+ # `order` clause.
449
+ #
450
+ # class Person < ActiveRecord::Base
451
+ # delegated_type :drivable, types: %w(Car Truck), scope: -> { order('wheels desc') }
452
+ # end
453
+ #
454
+ # # RSpec
455
+ # RSpec.describe Vehicle, type: :model do
456
+ # it { should have_delegated_type(:drivable).order('wheels desc') }
457
+ # end
458
+ #
459
+ # # Minitest (Shoulda)
460
+ # class VehicleTest < ActiveSupport::TestCase
461
+ # should have_delegated_type(:drivable).order('wheels desc')
462
+ # end
463
+ #
464
+ # ##### with_primary_key
465
+ #
466
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
467
+ #
468
+ # class Vehicle < ActiveRecord::Base
469
+ # delegated_type :drivable, types: %w(Car Truck), primary_key: 'vehicle_id'
470
+ # end
471
+ #
472
+ # # RSpec
473
+ # RSpec.describe Vehicle, type: :model do
474
+ # it do
475
+ # should have_delegated_type(:drivable).
476
+ # with_primary_key('vehicle_id')
477
+ # end
478
+ # end
479
+ #
480
+ # # Minitest (Shoulda)
481
+ # class VehicleTest < ActiveSupport::TestCase
482
+ # should have_delegated_type(:drivable).
483
+ # with_primary_key('vehicle_id')
484
+ # end
485
+ #
486
+ # ##### with_foreign_key
487
+ #
488
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
489
+ #
490
+ # class Vehicle < ActiveRecord::Base
491
+ # delegated_type :drivable, types: %w(Car Truck), foreign_key: 'drivable_uuid'
492
+ # end
493
+ #
494
+ # # RSpec
495
+ # RSpec.describe Vehicle, type: :model do
496
+ # it do
497
+ # should have_delegated_type(:drivable).
498
+ # with_foreign_key('drivable_uuid')
499
+ # end
500
+ # end
501
+ #
502
+ # # Minitest (Shoulda)
503
+ # class VehicleTest < ActiveSupport::TestCase
504
+ # should have_delegated_type(:drivable).
505
+ # with_foreign_key('drivable_uuid')
506
+ # end
507
+ #
508
+ # ##### dependent
509
+ #
510
+ # Use `dependent` to assert that the `:dependent` option was specified.
511
+ #
512
+ # class Vehicle < ActiveRecord::Base
513
+ # delegated_type :drivable, types: %w(Car Truck), dependent: :destroy
514
+ # end
515
+ #
516
+ # # RSpec
517
+ # RSpec.describe Vehicle, type: :model do
518
+ # it { should have_delegated_type(:drivable).dependent(:destroy) }
519
+ # end
520
+ #
521
+ # # Minitest (Shoulda)
522
+ # class VehicleTest < ActiveSupport::TestCase
523
+ # should have_delegated_type(:drivable).dependent(:destroy)
524
+ # end
525
+ #
526
+ # To assert that *any* `:dependent` option was specified, use `true`:
527
+ #
528
+ # # RSpec
529
+ # RSpec.describe Vehicle, type: :model do
530
+ # it { should have_delegated_type(:drivable).dependent(true) }
531
+ # end
532
+ #
533
+ # To assert that *no* `:dependent` option was specified, use `false`:
534
+ #
535
+ # class Vehicle < ActiveRecord::Base
536
+ # delegated_type :drivable, types: %w(Car Truck)
537
+ # end
538
+ #
539
+ # # RSpec
540
+ # RSpec.describe Vehicle, type: :model do
541
+ # it { should have_delegated_type(:drivable).dependent(false) }
542
+ # end
543
+ #
544
+ # ##### counter_cache
545
+ #
546
+ # Use `counter_cache` to assert that the `:counter_cache` option was
547
+ # specified.
548
+ #
549
+ # class Vehicle < ActiveRecord::Base
550
+ # delegated_type :drivable, types: %w(Car Truck), counter_cache: true
551
+ # end
552
+ #
553
+ # # RSpec
554
+ # RSpec.describe Vehicle, type: :model do
555
+ # it { should have_delegated_type(:drivable).counter_cache(true) }
556
+ # end
557
+ #
558
+ # # Minitest (Shoulda)
559
+ # class VehicleTest < ActiveSupport::TestCase
560
+ # should have_delegated_type(:drivable).counter_cache(true)
561
+ # end
562
+ #
563
+ # ##### touch
564
+ #
565
+ # Use `touch` to assert that the `:touch` option was specified.
566
+ #
567
+ # class Vehicle < ActiveRecord::Base
568
+ # delegated_type :drivable, types: %w(Car Truck), touch: true
569
+ # end
570
+ #
571
+ # # RSpec
572
+ # RSpec.describe Vehicle, type: :model do
573
+ # it { should have_delegated_type(:drivable).touch(true) }
574
+ # end
575
+ #
576
+ # # Minitest (Shoulda)
577
+ # class VehicleTest < ActiveSupport::TestCase
578
+ # should have_delegated_type(:drivable).touch(true)
579
+ # end
580
+ #
581
+ # ##### autosave
582
+ #
583
+ # Use `autosave` to assert that the `:autosave` option was specified.
584
+ #
585
+ # class Vehicle < ActiveRecord::Base
586
+ # delegated_type :drivable, types: %w(Car Truck), autosave: true
587
+ # end
588
+ #
589
+ # # RSpec
590
+ # RSpec.describe Vehicle, type: :model do
591
+ # it { should have_delegated_type(:drivable).autosave(true) }
592
+ # end
593
+ #
594
+ # # Minitest (Shoulda)
595
+ # class VehicleTest < ActiveSupport::TestCase
596
+ # should have_delegated_type(:drivable).autosave(true)
597
+ # end
598
+ #
599
+ # ##### inverse_of
600
+ #
601
+ # Use `inverse_of` to assert that the `:inverse_of` option was specified.
602
+ #
603
+ # class Vehicle < ActiveRecord::Base
604
+ # delegated_type :drivable, types: %w(Car Truck), inverse_of: :vehicle
605
+ # end
606
+ #
607
+ # # RSpec
608
+ # describe Vehicle
609
+ # it { should have_delegated_type(:drivable).inverse_of(:vehicle) }
610
+ # end
611
+ #
612
+ # # Minitest (Shoulda)
613
+ # class VehicleTest < ActiveSupport::TestCase
614
+ # should have_delegated_type(:drivable).inverse_of(:vehicle)
615
+ # end
616
+ #
617
+ # ##### required
618
+ #
619
+ # Use `required` to assert that the association is not allowed to be nil.
620
+ # (Enabled by default in Rails 5+.)
621
+ #
622
+ # class Vehicle < ActiveRecord::Base
623
+ # delegated_type :drivable, types: %w(Car Truck), required: true
624
+ # end
625
+ #
626
+ # # RSpec
627
+ # describe Vehicle
628
+ # it { should have_delegated_type(:drivable).required }
629
+ # end
630
+ #
631
+ # # Minitest (Shoulda)
632
+ # class VehicleTest < ActiveSupport::TestCase
633
+ # should have_delegated_type(:drivable).required
634
+ # end
635
+ #
636
+ # ##### without_validating_presence
637
+ #
638
+ # Use `without_validating_presence` with `belong_to` to prevent the
639
+ # matcher from checking whether the association disallows nil (Rails 5+
640
+ # only). This can be helpful if you have a custom hook that always sets
641
+ # the association to a meaningful value:
642
+ #
643
+ # class Vehicle < ActiveRecord::Base
644
+ # delegated_type :drivable, types: %w(Car Truck)
645
+ #
646
+ # before_validation :autoassign_drivable
647
+ #
648
+ # private
649
+ #
650
+ # def autoassign_drivable
651
+ # self.drivable = Car.create!
652
+ # end
653
+ # end
654
+ #
655
+ # # RSpec
656
+ # describe Vehicle
657
+ # it { should have_delegated_type(:drivable).without_validating_presence }
658
+ # end
659
+ #
660
+ # # Minitest (Shoulda)
661
+ # class VehicleTest < ActiveSupport::TestCase
662
+ # should have_delegated_type(:drivable).without_validating_presence
663
+ # end
664
+ #
665
+ # ##### optional
666
+ #
667
+ # Use `optional` to assert that the association is allowed to be nil.
668
+ # (Rails 5+ only.)
669
+ #
670
+ # class Vehicle < ActiveRecord::Base
671
+ # delegated_type :drivable, types: %w(Car Truck), optional: true
672
+ # end
673
+ #
674
+ # # RSpec
675
+ # describe Vehicle
676
+ # it { should have_delegated_type(:drivable).optional }
677
+ # end
678
+ #
679
+ # # Minitest (Shoulda)
680
+ # class VehicleTest < ActiveSupport::TestCase
681
+ # should have_delegated_type(:drivable).optional
682
+ # end
683
+ #
684
+ # @return [AssociationMatcher]
685
+ #
686
+
687
+ def have_delegated_type(name)
688
+ AssociationMatcher.new(:belongs_to, name)
689
+ end
690
+
329
691
  # The `have_many` matcher is used to test that a `has_many` or `has_many
330
692
  # :through` association exists on your model.
331
693
  #
@@ -455,6 +817,24 @@ module Shoulda
455
817
  # should have_many(:worries).with_foreign_key('worrier_id')
456
818
  # end
457
819
  #
820
+ # ##### with_foreign_type
821
+ #
822
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
823
+ #
824
+ # class Hotel < ActiveRecord::Base
825
+ # has_many :visitors, foreign_key: 'facility_type', as: :location
826
+ # end
827
+ #
828
+ # # RSpec
829
+ # RSpec.describe Hotel, type: :model do
830
+ # it { should have_many(:visitors).with_foreign_type('facility_type') }
831
+ # end
832
+ #
833
+ # # Minitest (Shoulda)
834
+ # class HotelTest < ActiveSupport::TestCase
835
+ # should have_many(:visitors).with_foreign_type('facility_type')
836
+ # end
837
+ #
458
838
  # ##### dependent
459
839
  #
460
840
  # Use `dependent` to assert that the `:dependent` option was specified.
@@ -726,6 +1106,24 @@ module Shoulda
726
1106
  # should have_one(:job).with_foreign_key('worker_id')
727
1107
  # end
728
1108
  #
1109
+ # ##### with_foreign_type
1110
+ #
1111
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
1112
+ #
1113
+ # class Hotel < ActiveRecord::Base
1114
+ # has_one :special_guest, foreign_type: 'facility_type', as: :location
1115
+ # end
1116
+ #
1117
+ # # RSpec
1118
+ # RSpec.describe Hotel, type: :model do
1119
+ # it { should have_one(:special_guest).with_foreign_type('facility_type') }
1120
+ # end
1121
+ #
1122
+ # # Minitest (Shoulda)
1123
+ # class HotelTest < ActiveSupport::TestCase
1124
+ # should have_one(:special_guest).with_foreign_type('facility_type')
1125
+ # end
1126
+ #
729
1127
  # ##### through
730
1128
  #
731
1129
  # Use `through` to test usage of the `:through` option. This asserts that
@@ -1068,6 +1466,11 @@ module Shoulda
1068
1466
  self
1069
1467
  end
1070
1468
 
1469
+ def types(types)
1470
+ @options[:types] = types
1471
+ self
1472
+ end
1473
+
1071
1474
  def autosave(autosave)
1072
1475
  @options[:autosave] = autosave
1073
1476
  self
@@ -1088,11 +1491,21 @@ module Shoulda
1088
1491
  self
1089
1492
  end
1090
1493
 
1494
+ def with_foreign_type(foreign_type)
1495
+ @options[:foreign_type] = foreign_type
1496
+ self
1497
+ end
1498
+
1091
1499
  def with_primary_key(primary_key)
1092
1500
  @options[:primary_key] = primary_key
1093
1501
  self
1094
1502
  end
1095
1503
 
1504
+ def with_query_constraints(query_constraints)
1505
+ @options[:query_constraints] = query_constraints
1506
+ self
1507
+ end
1508
+
1096
1509
  def required(required = true)
1097
1510
  remove_submatcher(AssociationMatchers::OptionalMatcher)
1098
1511
  add_submatcher(
@@ -1123,6 +1536,11 @@ module Shoulda
1123
1536
  self
1124
1537
  end
1125
1538
 
1539
+ def strict_loading(strict_loading = true)
1540
+ @options[:strict_loading] = strict_loading
1541
+ self
1542
+ end
1543
+
1126
1544
  def join_table(join_table_name)
1127
1545
  @options[:join_table_name] = join_table_name
1128
1546
  self
@@ -1155,8 +1573,10 @@ module Shoulda
1155
1573
  macro_correct? &&
1156
1574
  validate_inverse_of_through_association &&
1157
1575
  (polymorphic? || class_exists?) &&
1576
+ foreign_type_matches? &&
1158
1577
  foreign_key_exists? &&
1159
1578
  primary_key_exists? &&
1579
+ query_constraints_exists? &&
1160
1580
  class_name_correct? &&
1161
1581
  join_table_correct? &&
1162
1582
  autosave_correct? &&
@@ -1164,6 +1584,8 @@ module Shoulda
1164
1584
  conditions_correct? &&
1165
1585
  validate_correct? &&
1166
1586
  touch_correct? &&
1587
+ types_correct? &&
1588
+ strict_loading_correct? &&
1167
1589
  submatchers_match?
1168
1590
  end
1169
1591
 
@@ -1258,29 +1680,65 @@ module Shoulda
1258
1680
  false
1259
1681
  end
1260
1682
 
1261
- def macro_supports_primary_key?
1262
- macro == :belongs_to ||
1263
- ([:has_many, :has_one].include?(macro) && !through?)
1683
+ def macro_is_not_through?
1684
+ macro == :belongs_to || has_association_not_through?
1685
+ end
1686
+
1687
+ def has_association_not_through?
1688
+ [:has_many, :has_one].include?(macro) && !through?
1264
1689
  end
1265
1690
 
1266
1691
  def foreign_key_exists?
1267
1692
  !(belongs_foreign_key_missing? || has_foreign_key_missing?)
1268
1693
  end
1269
1694
 
1695
+ def foreign_type_matches?
1696
+ !options.key?(:foreign_type) || (
1697
+ !belongs_foreign_type_missing? &&
1698
+ !has_foreign_type_missing?
1699
+ )
1700
+ end
1701
+
1270
1702
  def primary_key_exists?
1271
- !macro_supports_primary_key? || primary_key_correct?(model_class)
1703
+ !macro_is_not_through? || primary_key_correct?(model_class)
1704
+ end
1705
+
1706
+ def query_constraints_exists?
1707
+ !macro_is_not_through? || query_constraints_correct?
1708
+ end
1709
+
1710
+ def query_constraints_correct?
1711
+ if options.key?(:query_constraints)
1712
+ if option_verifier.correct_for_string?(:query_constraints, options[:query_constraints])
1713
+ true
1714
+ else
1715
+ @missing = "#{model_class} should have \:query_constraints"\
1716
+ " options set to #{options[:query_constraints]}"
1717
+ false
1718
+ end
1719
+ else
1720
+ true
1721
+ end
1272
1722
  end
1273
1723
 
1274
1724
  def belongs_foreign_key_missing?
1275
1725
  macro == :belongs_to && !class_has_foreign_key?(model_class)
1276
1726
  end
1277
1727
 
1728
+ def belongs_foreign_type_missing?
1729
+ macro == :belongs_to && !class_has_foreign_type?(model_class)
1730
+ end
1731
+
1278
1732
  def has_foreign_key_missing?
1279
- [:has_many, :has_one].include?(macro) &&
1280
- !through? &&
1733
+ has_association_not_through? &&
1281
1734
  !class_has_foreign_key?(associated_class)
1282
1735
  end
1283
1736
 
1737
+ def has_foreign_type_missing?
1738
+ has_association_not_through? &&
1739
+ !class_has_foreign_type?(associated_class)
1740
+ end
1741
+
1284
1742
  def class_name_correct?
1285
1743
  if options.key?(:class_name)
1286
1744
  if option_verifier.correct_for_constant?(
@@ -1299,10 +1757,7 @@ module Shoulda
1299
1757
  end
1300
1758
 
1301
1759
  def join_table_correct?
1302
- if (
1303
- macro != :has_and_belongs_to_many ||
1304
- join_table_matcher.matches?(@subject)
1305
- )
1760
+ if macro != :has_and_belongs_to_many || join_table_matcher.matches?(@subject)
1306
1761
  true
1307
1762
  else
1308
1763
  @missing = join_table_matcher.failure_message
@@ -1393,6 +1848,45 @@ module Shoulda
1393
1848
  end
1394
1849
  end
1395
1850
 
1851
+ def types_correct?
1852
+ if options.key?(:types)
1853
+ types = options[:types]
1854
+
1855
+ correct = types.all? do |type|
1856
+ scope_name = type.tableize.tr('/', '_')
1857
+ singular = scope_name.singularize
1858
+ query = "#{singular}?"
1859
+
1860
+ Object.const_defined?(type) && @subject.respond_to?(query) &&
1861
+ @subject.respond_to?(singular)
1862
+ end
1863
+
1864
+ if correct
1865
+ true
1866
+ else
1867
+ @missing = "#{name} should have types: #{options[:types]}"
1868
+ false
1869
+ end
1870
+ else
1871
+ true
1872
+ end
1873
+ end
1874
+
1875
+ def strict_loading_correct?
1876
+ return true unless options.key?(:strict_loading)
1877
+
1878
+ if option_verifier.correct_for_boolean?(:strict_loading, options[:strict_loading])
1879
+ return true
1880
+ end
1881
+
1882
+ @missing = [
1883
+ "#{name} should have strict_loading set to ",
1884
+ options[:strict_loading].to_s,
1885
+ ].join
1886
+
1887
+ false
1888
+ end
1889
+
1396
1890
  def class_has_foreign_key?(klass)
1397
1891
  @missing = validate_foreign_key(klass)
1398
1892
 
@@ -1407,6 +1901,22 @@ module Shoulda
1407
1901
  end
1408
1902
  end
1409
1903
 
1904
+ def class_has_foreign_type?(klass)
1905
+ if options.key?(:foreign_type) && !foreign_type_correct?
1906
+ @missing = foreign_type_failure_message(
1907
+ klass,
1908
+ options[:foreign_type],
1909
+ )
1910
+
1911
+ false
1912
+ elsif !has_column?(klass, foreign_type)
1913
+ @missing = foreign_type_failure_message(klass, foreign_type)
1914
+ false
1915
+ else
1916
+ true
1917
+ end
1918
+ end
1919
+
1410
1920
  def has_column?(klass, column)
1411
1921
  case column
1412
1922
  when Array
@@ -1423,10 +1933,21 @@ module Shoulda
1423
1933
  )
1424
1934
  end
1425
1935
 
1936
+ def foreign_type_correct?
1937
+ option_verifier.correct_for_string?(
1938
+ :foreign_type,
1939
+ options[:foreign_type],
1940
+ )
1941
+ end
1942
+
1426
1943
  def foreign_key_failure_message(klass, foreign_key)
1427
1944
  "#{klass} does not have a #{foreign_key} foreign key."
1428
1945
  end
1429
1946
 
1947
+ def foreign_type_failure_message(klass, foreign_type)
1948
+ "#{klass} does not have a #{foreign_type} foreign type."
1949
+ end
1950
+
1430
1951
  def primary_key_correct?(klass)
1431
1952
  if options.key?(:primary_key)
1432
1953
  if option_verifier.correct_for_string?(
@@ -1457,11 +1978,10 @@ module Shoulda
1457
1978
  end
1458
1979
 
1459
1980
  def foreign_key_reflection
1460
- if (
1461
- [:has_one, :has_many].include?(macro) &&
1462
- reflection.options.include?(:inverse_of) &&
1463
- reflection.options[:inverse_of] != false
1464
- )
1981
+ if [:has_one, :has_many].include?(macro) &&
1982
+ reflection.options.include?(:inverse_of) &&
1983
+ reflection.options[:inverse_of] != false
1984
+
1465
1985
  associated_class.reflect_on_association(
1466
1986
  reflection.options[:inverse_of],
1467
1987
  )
@@ -1470,6 +1990,14 @@ module Shoulda
1470
1990
  end
1471
1991
  end
1472
1992
 
1993
+ def foreign_type
1994
+ if [:has_one, :has_many].include?(macro)
1995
+ reflection.type
1996
+ else
1997
+ reflection.foreign_type
1998
+ end
1999
+ end
2000
+
1473
2001
  def submatchers_match?
1474
2002
  failing_submatchers.empty?
1475
2003
  end