shoulda-matchers 6.1.0 → 6.2.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.
- checksums.yaml +4 -4
- data/README.md +14 -7
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +36 -0
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +4 -4
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +20 -8
- data/lib/shoulda/matchers/active_record/association_matcher.rb +512 -4
- 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/normalize_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -1
- 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/util.rb +1 -1
- data/lib/shoulda/matchers/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d549ac8f3629ad37bc56d0d09daf37416ec23d78374a8ed6a630ea23e5c9e487
|
4
|
+
data.tar.gz: f09bc94b6be181564cdde56729c2055192a4725adee4ba8b6b7f8e84429fe9a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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]
|
521
|
-
|
522
|
-
[thoughtbot-logo]: https://thoughtbot.com/brand_assets/93:44.svg
|
523
|
+

|
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
|
527
|
-
|
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).
|
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).
|
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
|
-
#
|
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).
|
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 =
|
313
|
-
@maximum =
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -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,
|
@@ -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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
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
|
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.
|
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-
|
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.
|
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
|