shoulda-matchers 6.1.0 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 500d5928f097ad1ca9d9c1ed3a0e0c925f504549c1594b57e93efc92316df558
4
- data.tar.gz: 8a94b930a96fe1f2e3a78c916ca4fa6b68c21ab61ae9baccb109ec51e9a2961c
3
+ metadata.gz: d549ac8f3629ad37bc56d0d09daf37416ec23d78374a8ed6a630ea23e5c9e487
4
+ data.tar.gz: f09bc94b6be181564cdde56729c2055192a4725adee4ba8b6b7f8e84429fe9a0
5
5
  SHA512:
6
- metadata.gz: 43d89cf2b6684f31f6fee6611fe694732446d50aee9fea5b8a5fefc79cd5ec4dc08337cb098e531cbfe4eac3ac46ed6cda46c6207b2f35956cf9b07d2925af1a
7
- data.tar.gz: 586d777927cdafba0aa92ecc6c437b562a24a44aa343a8be309ee7563a3a2b98e4426c8804f1c40ad46a1987ff7f160e97473fcd178f3fdb57c2c471d210044b
6
+ metadata.gz: 1f3ba08a6d15ad56cb583c0a8289e180854f5d89020b3182b2d6140289800d71bca467f187c9a4718ba26078cc2562f6377d77b00a0e84f2e683079d62ef60d3
7
+ data.tar.gz: 45d0141c79d9642439c9c03ffcd8e41d1aee993eafaea11fc72812456ead1dcf17d1698e9eb7f311c40945c54c77fc4e573a820ffa115fc344e9b0049ed69479
data/README.md CHANGED
@@ -385,8 +385,10 @@ about any of them, make sure to [consult the documentation][rubydocs]!
385
385
  tests your `belongs_to` associations.
386
386
  * **[define_enum_for](lib/shoulda/matchers/active_record/define_enum_for_matcher.rb)**
387
387
  tests usage of the `enum` macro.
388
- * **[have_and_belong_to_many](lib/shoulda/matchers/active_record/association_matcher.rb#L827)**
388
+ * **[have_and_belong_to_many](lib/shoulda/matchers/active_record/association_matcher.rb)**
389
389
  tests your `has_and_belongs_to_many` associations.
390
+ * **[have_delegated_type](lib/shoulda/matchers/active_record/association_matcher.rb#L687)**
391
+ tests usage of the `delegated_type` macro.
390
392
  * **[have_db_column](lib/shoulda/matchers/active_record/have_db_column_matcher.rb)**
391
393
  tests that the table that backs your model has a specific column.
392
394
  * **[have_db_index](lib/shoulda/matchers/active_record/have_db_index_matcher.rb)**
@@ -515,16 +517,21 @@ redistributed under the terms specified in the [LICENSE](LICENSE) file.
515
517
 
516
518
  [thoughtbot-website]: https://thoughtbot.com
517
519
 
520
+ <!-- START /templates/footer.md -->
518
521
  ## About thoughtbot
519
522
 
520
- ![thoughtbot][thoughtbot-logo]
521
-
522
- [thoughtbot-logo]: https://thoughtbot.com/brand_assets/93:44.svg
523
+ ![thoughtbot](https://thoughtbot.com/thoughtbot-logo-for-readmes.svg)
523
524
 
525
+ This repo is maintained and funded by thoughtbot, inc.
524
526
  The names and logos for thoughtbot are trademarks of thoughtbot, inc.
525
527
 
526
- We are passionate about open source software. See [our other
527
- projects][community]. We are [available for hire][hire].
528
+ We love open source software!
529
+ See [our other projects][community].
530
+
531
+ We are [available for hire][hire].
528
532
 
529
533
  [community]: https://thoughtbot.com/community?utm_source=github
530
- [hire]: https://thoughtbot.com?utm_source=github
534
+ [hire]: https://thoughtbot.com/hire-us?utm_source=github
535
+
536
+
537
+ <!-- END /templates/footer.md -->
@@ -169,6 +169,36 @@ module Shoulda
169
169
  # on(:create)
170
170
  # end
171
171
  #
172
+ # ##### against
173
+ #
174
+ # Use `against` if the validation is on an attribute
175
+ # other than the attribute being validated:
176
+ #
177
+ # class UserProfile
178
+ # include ActiveModel::Model
179
+ # attr_accessor :website_url
180
+ #
181
+ # alias_attribute :url, :website_url
182
+ #
183
+ # validates_format_of :url, with: URI.regexp
184
+ # end
185
+ #
186
+ # # RSpec
187
+ # RSpec.describe UserProfile, type: :model do
188
+ # it do
189
+ # should allow_value('https://foo.com').
190
+ # for(:website_url).
191
+ # against(:url)
192
+ # end
193
+ # end
194
+ #
195
+ # # Minitest (Shoulda)
196
+ # class UserProfileTest < ActiveSupport::TestCase
197
+ # should allow_value('https://foo.com').
198
+ # for(:website_url).
199
+ # against(:url)
200
+ # end
201
+ #
172
202
  # ##### with_message
173
203
  #
174
204
  # Use `with_message` if you are using a custom validation message.
@@ -349,6 +379,12 @@ module Shoulda
349
379
  self
350
380
  end
351
381
 
382
+ def against(attribute)
383
+ @attribute_to_check_message_against = attribute if attribute.present?
384
+
385
+ self
386
+ end
387
+
352
388
  def with_message(message, given_options = {})
353
389
  if message.present?
354
390
  @expects_custom_validation_message = true
@@ -13,12 +13,12 @@ module Shoulda
13
13
  #
14
14
  # # RSpec
15
15
  # RSpec.describe Person, type: :model do
16
- # it { should validate_comparison_of(:gpa).greater_than(10) }
16
+ # it { should validate_comparison_of(:gpa).is_greater_than(10) }
17
17
  # end
18
18
  #
19
19
  # # Minitest (Shoulda)
20
20
  # class PersonTest < ActiveSupport::TestCase
21
- # should validate_comparison_of(:gpa).greater_than(10)
21
+ # should validate_comparison_of(:gpa).is_greater_than(10)
22
22
  # end
23
23
  #
24
24
  # #### Qualifiers
@@ -39,14 +39,14 @@ module Shoulda
39
39
  # RSpec.describe Person, type: :model do
40
40
  # it do
41
41
  # should validate_comparison_of(:number_of_dependents).
42
- # greater_than(0).
42
+ # is_greater_than(0).
43
43
  # on(:create)
44
44
  # end
45
45
  # end
46
46
  #
47
47
  # # Minitest (Shoulda)
48
48
  # class PersonTest < ActiveSupport::TestCase
49
- # should validate_comparison_of(:number_of_dependents).greater_than(0).on(:create)
49
+ # should validate_comparison_of(:number_of_dependents).is_greater_than(0).on(:create)
50
50
  # end
51
51
  #
52
52
  # ##### is_less_than
@@ -309,8 +309,8 @@ EOT
309
309
 
310
310
  def in_range(range)
311
311
  @range = range
312
- @minimum = range.first
313
- @maximum = range.max
312
+ @minimum = minimum_range_value
313
+ @maximum = maximum_range_value
314
314
  self
315
315
  end
316
316
 
@@ -400,6 +400,18 @@ EOT
400
400
 
401
401
  private
402
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
+
403
415
  def matches_for_range?
404
416
  disallows_lower_value &&
405
417
  allows_minimum_value &&
@@ -441,27 +453,27 @@ EOT
441
453
  end
442
454
 
443
455
  def allows_minimum_value
444
- allows_value_of(@minimum, @low_message)
456
+ @minimum.nil? || allows_value_of(@minimum, @low_message)
445
457
  end
446
458
 
447
459
  def disallows_minimum_value
448
- disallows_value_of(@minimum, @low_message)
460
+ @minimum.nil? || disallows_value_of(@minimum, @low_message)
449
461
  end
450
462
 
451
463
  def allows_maximum_value
452
- allows_value_of(@maximum, @high_message)
464
+ @maximum.nil? || allows_value_of(@maximum, @high_message)
453
465
  end
454
466
 
455
467
  def disallows_maximum_value
456
- disallows_value_of(@maximum, @high_message)
468
+ @maximum.nil? || disallows_value_of(@maximum, @high_message)
457
469
  end
458
470
 
459
471
  def allows_higher_value
460
- allows_value_of(@maximum + 1, @high_message)
472
+ @maximum.nil? || allows_value_of(@maximum + 1, @high_message)
461
473
  end
462
474
 
463
475
  def disallows_higher_value
464
- disallows_value_of(@maximum + 1, @high_message)
476
+ @maximum.nil? || disallows_value_of(@maximum + 1, @high_message)
465
477
  end
466
478
 
467
479
  def allows_all_values_in_array?
@@ -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,6 +1491,11 @@ 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
@@ -1128,6 +1536,11 @@ module Shoulda
1128
1536
  self
1129
1537
  end
1130
1538
 
1539
+ def strict_loading(strict_loading = true)
1540
+ @options[:strict_loading] = strict_loading
1541
+ self
1542
+ end
1543
+
1131
1544
  def join_table(join_table_name)
1132
1545
  @options[:join_table_name] = join_table_name
1133
1546
  self
@@ -1160,6 +1573,7 @@ module Shoulda
1160
1573
  macro_correct? &&
1161
1574
  validate_inverse_of_through_association &&
1162
1575
  (polymorphic? || class_exists?) &&
1576
+ foreign_type_matches? &&
1163
1577
  foreign_key_exists? &&
1164
1578
  primary_key_exists? &&
1165
1579
  query_constraints_exists? &&
@@ -1170,6 +1584,8 @@ module Shoulda
1170
1584
  conditions_correct? &&
1171
1585
  validate_correct? &&
1172
1586
  touch_correct? &&
1587
+ types_correct? &&
1588
+ strict_loading_correct? &&
1173
1589
  submatchers_match?
1174
1590
  end
1175
1591
 
@@ -1265,14 +1681,24 @@ module Shoulda
1265
1681
  end
1266
1682
 
1267
1683
  def macro_is_not_through?
1268
- macro == :belongs_to ||
1269
- ([:has_many, :has_one].include?(macro) && !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?
1270
1689
  end
1271
1690
 
1272
1691
  def foreign_key_exists?
1273
1692
  !(belongs_foreign_key_missing? || has_foreign_key_missing?)
1274
1693
  end
1275
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
+
1276
1702
  def primary_key_exists?
1277
1703
  !macro_is_not_through? || primary_key_correct?(model_class)
1278
1704
  end
@@ -1299,12 +1725,20 @@ module Shoulda
1299
1725
  macro == :belongs_to && !class_has_foreign_key?(model_class)
1300
1726
  end
1301
1727
 
1728
+ def belongs_foreign_type_missing?
1729
+ macro == :belongs_to && !class_has_foreign_type?(model_class)
1730
+ end
1731
+
1302
1732
  def has_foreign_key_missing?
1303
- [:has_many, :has_one].include?(macro) &&
1304
- !through? &&
1733
+ has_association_not_through? &&
1305
1734
  !class_has_foreign_key?(associated_class)
1306
1735
  end
1307
1736
 
1737
+ def has_foreign_type_missing?
1738
+ has_association_not_through? &&
1739
+ !class_has_foreign_type?(associated_class)
1740
+ end
1741
+
1308
1742
  def class_name_correct?
1309
1743
  if options.key?(:class_name)
1310
1744
  if option_verifier.correct_for_constant?(
@@ -1414,6 +1848,45 @@ module Shoulda
1414
1848
  end
1415
1849
  end
1416
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
+
1417
1890
  def class_has_foreign_key?(klass)
1418
1891
  @missing = validate_foreign_key(klass)
1419
1892
 
@@ -1428,6 +1901,22 @@ module Shoulda
1428
1901
  end
1429
1902
  end
1430
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
+
1431
1920
  def has_column?(klass, column)
1432
1921
  case column
1433
1922
  when Array
@@ -1444,10 +1933,21 @@ module Shoulda
1444
1933
  )
1445
1934
  end
1446
1935
 
1936
+ def foreign_type_correct?
1937
+ option_verifier.correct_for_string?(
1938
+ :foreign_type,
1939
+ options[:foreign_type],
1940
+ )
1941
+ end
1942
+
1447
1943
  def foreign_key_failure_message(klass, foreign_key)
1448
1944
  "#{klass} does not have a #{foreign_key} foreign key."
1449
1945
  end
1450
1946
 
1947
+ def foreign_type_failure_message(klass, foreign_type)
1948
+ "#{klass} does not have a #{foreign_type} foreign type."
1949
+ end
1950
+
1451
1951
  def primary_key_correct?(klass)
1452
1952
  if options.key?(:primary_key)
1453
1953
  if option_verifier.correct_for_string?(
@@ -1490,6 +1990,14 @@ module Shoulda
1490
1990
  end
1491
1991
  end
1492
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
+
1493
2001
  def submatchers_match?
1494
2002
  failing_submatchers.empty?
1495
2003
  end
@@ -13,7 +13,11 @@ module Shoulda
13
13
  end
14
14
 
15
15
  def associated_class
16
- reflection.klass
16
+ if polymorphic?
17
+ subject
18
+ else
19
+ reflection.klass
20
+ end
17
21
  end
18
22
 
19
23
  def polymorphic?
@@ -70,6 +74,10 @@ module Shoulda
70
74
  reflection.options[:through]
71
75
  end
72
76
 
77
+ def strict_loading?
78
+ reflection.options.fetch(:strict_loading, subject.strict_loading_by_default)
79
+ end
80
+
73
81
  protected
74
82
 
75
83
  attr_reader :reflection, :subject
@@ -8,6 +8,7 @@ module Shoulda
8
8
  :associated_class,
9
9
  :association_foreign_key,
10
10
  :foreign_key,
11
+ :foreign_type,
11
12
  :has_and_belongs_to_many_name,
12
13
  :join_table_name,
13
14
  :polymorphic?,
@@ -122,6 +122,10 @@ module Shoulda
122
122
  reflector.associated_class
123
123
  end
124
124
 
125
+ def actual_value_for_strict_loading
126
+ reflection.strict_loading?
127
+ end
128
+
125
129
  def actual_value_for_option(name)
126
130
  option_value = reflection.options[name]
127
131
 
@@ -40,7 +40,7 @@ module Shoulda
40
40
  #
41
41
  # # Minitest (Shoulda)
42
42
  # class User < ActiveSupport::TestCase
43
- # should normalize(:email, handle).from(" Example\n").to("example")
43
+ # should normalize(:email, :handle).from(" Example\n").to("example")
44
44
  # end
45
45
  #
46
46
  # If the normalization accepts nil values with the `apply_to_nil` option,
@@ -29,7 +29,7 @@ module Shoulda
29
29
  end
30
30
 
31
31
  def symlink_to(parent)
32
- namespace.set(name, parent.dup)
32
+ namespace.set(name, Class.new(parent))
33
33
  end
34
34
 
35
35
  def to_s
@@ -12,15 +12,17 @@ module Shoulda
12
12
  def integrate_with(test_framework)
13
13
  test_framework.include(matchers_module, type: :controller)
14
14
 
15
- include_into(::ActionController::TestCase, matchers_module) do
16
- def subject # rubocop:disable Lint/NestedMethodDefinition
17
- @controller
15
+ tap do |instance|
16
+ ActiveSupport.on_load(:action_controller_test_case, run_once: true) do
17
+ instance.include_into(::ActionController::TestCase, instance.matchers_module) do
18
+ def subject # rubocop:disable Lint/NestedMethodDefinition
19
+ @controller
20
+ end
21
+ end
18
22
  end
19
23
  end
20
24
  end
21
25
 
22
- private
23
-
24
26
  def matchers_module
25
27
  Shoulda::Matchers::ActionController
26
28
  end
@@ -12,11 +12,13 @@ module Shoulda
12
12
  def integrate_with(test_framework)
13
13
  test_framework.include(matchers_module, type: :routing)
14
14
 
15
- include_into(::ActionController::TestCase, matchers_module)
15
+ tap do |instance|
16
+ ActiveSupport.on_load(:action_controller_test_case, run_once: true) do
17
+ instance.include_into(::ActionController::TestCase, instance.matchers_module)
18
+ end
19
+ end
16
20
  end
17
21
 
18
- private
19
-
20
22
  def matchers_module
21
23
  Shoulda::Matchers::Routing
22
24
  end
@@ -66,7 +66,7 @@ module Shoulda
66
66
  end
67
67
 
68
68
  def self.inspect_range(range)
69
- "#{inspect_value(range.first)} to #{inspect_value(range.last)}"
69
+ "#{inspect_value(range.begin)} to #{inspect_value(range.end)}"
70
70
  end
71
71
 
72
72
  def self.inspect_hash(hash)
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '6.1.0'.freeze
4
+ VERSION = '6.2.0'.freeze
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoulda-matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.1.0
4
+ version: 6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tammer Saleh
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2024-01-19 00:00:00.000000000 Z
17
+ date: 2024-03-15 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activesupport
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
198
  - !ruby/object:Gem::Version
199
199
  version: '0'
200
200
  requirements: []
201
- rubygems_version: 3.5.3
201
+ rubygems_version: 3.5.6
202
202
  signing_key:
203
203
  specification_version: 4
204
204
  summary: Simple one-liner tests for common Rails functionality