shoulda-matchers 5.2.0 → 6.0.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/LICENSE +1 -1
- data/README.md +22 -7
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +9 -0
- data/lib/shoulda/matchers/active_model/comparison_matcher.rb +162 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +3 -5
- data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +58 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +2 -1
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +534 -0
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +5 -5
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +9 -6
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +64 -9
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +72 -80
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +6 -0
- data/lib/shoulda/matchers/active_model/validator.rb +4 -0
- data/lib/shoulda/matchers/active_model.rb +4 -1
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +0 -8
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +14 -3
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
- data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/lib/shoulda/matchers/active_record.rb +1 -0
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +1 -1
- data/lib/shoulda/matchers/rails_shim.rb +10 -8
- data/lib/shoulda/matchers/util/word_wrap.rb +2 -2
- data/lib/shoulda/matchers/util.rb +1 -1
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +2 -2
- data/shoulda-matchers.gemspec +1 -1
- metadata +12 -8
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -157
@@ -3,7 +3,7 @@ module Shoulda
|
|
3
3
|
module ActiveModel
|
4
4
|
# The `validate_length_of` matcher tests usage of the
|
5
5
|
# `validates_length_of` matcher. Note that this matcher is intended to be
|
6
|
-
# used against string columns and not integer columns.
|
6
|
+
# used against string columns and associations and not integer columns.
|
7
7
|
#
|
8
8
|
# #### Qualifiers
|
9
9
|
#
|
@@ -36,7 +36,8 @@ module Shoulda
|
|
36
36
|
#
|
37
37
|
# Use `is_at_least` to test usage of the `:minimum` option. This asserts
|
38
38
|
# that the attribute can take a string which is equal to or longer than
|
39
|
-
# the given length and cannot take a string which is shorter.
|
39
|
+
# the given length and cannot take a string which is shorter. This qualifier
|
40
|
+
# also works for associations.
|
40
41
|
#
|
41
42
|
# class User
|
42
43
|
# include ActiveModel::Model
|
@@ -61,7 +62,8 @@ module Shoulda
|
|
61
62
|
#
|
62
63
|
# Use `is_at_most` to test usage of the `:maximum` option. This asserts
|
63
64
|
# that the attribute can take a string which is equal to or shorter than
|
64
|
-
# the given length and cannot take a string which is longer.
|
65
|
+
# the given length and cannot take a string which is longer. This qualifier
|
66
|
+
# also works for associations.
|
65
67
|
#
|
66
68
|
# class User
|
67
69
|
# include ActiveModel::Model
|
@@ -84,7 +86,8 @@ module Shoulda
|
|
84
86
|
#
|
85
87
|
# Use `is_equal_to` to test usage of the `:is` option. This asserts that
|
86
88
|
# the attribute can take a string which is exactly equal to the given
|
87
|
-
# length and cannot take a string which is shorter or longer.
|
89
|
+
# length and cannot take a string which is shorter or longer. This qualifier
|
90
|
+
# also works for associations.
|
88
91
|
#
|
89
92
|
# class User
|
90
93
|
# include ActiveModel::Model
|
@@ -106,7 +109,7 @@ module Shoulda
|
|
106
109
|
# ##### is_at_least + is_at_most
|
107
110
|
#
|
108
111
|
# Use `is_at_least` and `is_at_most` together to test usage of the `:in`
|
109
|
-
# option.
|
112
|
+
# option. This qualifies also works for associations.
|
110
113
|
#
|
111
114
|
# class User
|
112
115
|
# include ActiveModel::Model
|
@@ -260,6 +263,29 @@ module Shoulda
|
|
260
263
|
# should validate_length_of(:bio).is_at_least(15).allow_blank
|
261
264
|
# end
|
262
265
|
#
|
266
|
+
# ##### as_array
|
267
|
+
#
|
268
|
+
# Use `as_array` if you have an ActiveModel model and the attribute being validated
|
269
|
+
# is designed to store an array. (This is not necessary if you have an ActiveRecord
|
270
|
+
# model with an array column, as the matcher will detect this automatically.)
|
271
|
+
#
|
272
|
+
# class User
|
273
|
+
# include ActiveModel::Model
|
274
|
+
# attribute :arr, array: true
|
275
|
+
#
|
276
|
+
# validates_length_of :arr, minimum: 15
|
277
|
+
# end
|
278
|
+
#
|
279
|
+
# # RSpec
|
280
|
+
# describe User do
|
281
|
+
# it { should validate_length_of(:arr).as_array.is_at_least(15) }
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# # Minitest (Shoulda)
|
285
|
+
# class UserTest < ActiveSupport::TestCase
|
286
|
+
# should validate_length_of(:arr).as_array.is_at_least(15)
|
287
|
+
# end
|
288
|
+
#
|
263
289
|
def validate_length_of(attr)
|
264
290
|
ValidateLengthOfMatcher.new(attr)
|
265
291
|
end
|
@@ -275,6 +301,11 @@ module Shoulda
|
|
275
301
|
@long_message = nil
|
276
302
|
end
|
277
303
|
|
304
|
+
def as_array
|
305
|
+
@options[:array] = true
|
306
|
+
self
|
307
|
+
end
|
308
|
+
|
278
309
|
def is_at_least(length)
|
279
310
|
@options[:minimum] = length
|
280
311
|
@short_message ||= :too_short
|
@@ -451,15 +482,39 @@ module Shoulda
|
|
451
482
|
end
|
452
483
|
|
453
484
|
def allows_length_of?(length, message)
|
454
|
-
allows_value_of(
|
485
|
+
allows_value_of(value_of_length(length), message)
|
455
486
|
end
|
456
487
|
|
457
488
|
def disallows_length_of?(length, message)
|
458
|
-
disallows_value_of(
|
489
|
+
disallows_value_of(value_of_length(length), message)
|
490
|
+
end
|
491
|
+
|
492
|
+
def value_of_length(length)
|
493
|
+
if array_column?
|
494
|
+
['x'] * length
|
495
|
+
elsif collection_association?
|
496
|
+
Array.new(length) { association_reflection.klass.new }
|
497
|
+
else
|
498
|
+
'x' * length
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
def array_column?
|
503
|
+
@options[:array] || super
|
504
|
+
end
|
505
|
+
|
506
|
+
def collection_association?
|
507
|
+
association? && [:has_many, :has_and_belongs_to_many].include?(
|
508
|
+
association_reflection.macro,
|
509
|
+
)
|
510
|
+
end
|
511
|
+
|
512
|
+
def association?
|
513
|
+
association_reflection.present?
|
459
514
|
end
|
460
515
|
|
461
|
-
def
|
462
|
-
|
516
|
+
def association_reflection
|
517
|
+
model.try(:reflect_on_association, @attribute)
|
463
518
|
end
|
464
519
|
|
465
520
|
def translated_short_message
|
@@ -276,6 +276,33 @@ module Shoulda
|
|
276
276
|
# should validate_numericality_of(:birth_day).odd
|
277
277
|
# end
|
278
278
|
#
|
279
|
+
# ##### is_in
|
280
|
+
#
|
281
|
+
# Use `is_in` to test usage of the `:in` option.
|
282
|
+
# This asserts that the attribute can take a number which is contained
|
283
|
+
# in the given range.
|
284
|
+
#
|
285
|
+
# class Person
|
286
|
+
# include ActiveModel::Model
|
287
|
+
# attr_accessor :legal_age
|
288
|
+
#
|
289
|
+
# validates_numericality_of :birth_month, in: 1..12
|
290
|
+
# end
|
291
|
+
#
|
292
|
+
# # RSpec
|
293
|
+
# RSpec.describe Person, type: :model do
|
294
|
+
# it do
|
295
|
+
# should validate_numericality_of(:birth_month).
|
296
|
+
# is_in(1..12)
|
297
|
+
# end
|
298
|
+
# end
|
299
|
+
#
|
300
|
+
# # Minitest (Shoulda)
|
301
|
+
# class PersonTest < ActiveSupport::TestCase
|
302
|
+
# should validate_numericality_of(:birth_month).
|
303
|
+
# is_in(1..12)
|
304
|
+
# end
|
305
|
+
#
|
279
306
|
# ##### with_message
|
280
307
|
#
|
281
308
|
# Use `with_message` if you are using a custom validation message.
|
@@ -330,51 +357,33 @@ module Shoulda
|
|
330
357
|
end
|
331
358
|
|
332
359
|
# @private
|
333
|
-
class ValidateNumericalityOfMatcher
|
360
|
+
class ValidateNumericalityOfMatcher < ValidationMatcher
|
334
361
|
NUMERIC_NAME = 'number'.freeze
|
335
362
|
DEFAULT_DIFF_TO_COMPARE = 1
|
336
363
|
|
337
|
-
include Qualifiers::IgnoringInterferenceByWriter
|
338
|
-
|
339
364
|
attr_reader :diff_to_compare
|
340
365
|
|
341
366
|
def initialize(attribute)
|
342
367
|
super
|
343
|
-
@attribute = attribute
|
344
368
|
@submatchers = []
|
345
369
|
@diff_to_compare = DEFAULT_DIFF_TO_COMPARE
|
346
|
-
@expects_custom_validation_message = false
|
347
370
|
@expects_to_allow_nil = false
|
348
|
-
@expects_strict = false
|
349
371
|
@allowed_type_adjective = nil
|
350
372
|
@allowed_type_name = 'number'
|
351
|
-
@context = nil
|
352
|
-
@expected_message = nil
|
353
|
-
end
|
354
|
-
|
355
|
-
def strict
|
356
|
-
@expects_strict = true
|
357
|
-
self
|
358
|
-
end
|
359
373
|
|
360
|
-
|
361
|
-
@expects_strict
|
374
|
+
add_disallow_non_numeric_value_matcher
|
362
375
|
end
|
363
376
|
|
364
377
|
def only_integer
|
365
378
|
prepare_submatcher(
|
366
|
-
NumericalityMatchers::OnlyIntegerMatcher.new(self,
|
379
|
+
NumericalityMatchers::OnlyIntegerMatcher.new(self, attribute),
|
367
380
|
)
|
368
381
|
self
|
369
382
|
end
|
370
383
|
|
371
384
|
def allow_nil
|
372
385
|
@expects_to_allow_nil = true
|
373
|
-
prepare_submatcher(
|
374
|
-
AllowValueMatcher.new(nil).
|
375
|
-
for(@attribute).
|
376
|
-
with_message(:not_a_number),
|
377
|
-
)
|
386
|
+
prepare_submatcher(allow_value_matcher(nil, :not_a_number))
|
378
387
|
self
|
379
388
|
end
|
380
389
|
|
@@ -384,60 +393,52 @@ module Shoulda
|
|
384
393
|
|
385
394
|
def odd
|
386
395
|
prepare_submatcher(
|
387
|
-
NumericalityMatchers::OddNumberMatcher.new(self,
|
396
|
+
NumericalityMatchers::OddNumberMatcher.new(self, attribute),
|
388
397
|
)
|
389
398
|
self
|
390
399
|
end
|
391
400
|
|
392
401
|
def even
|
393
402
|
prepare_submatcher(
|
394
|
-
NumericalityMatchers::EvenNumberMatcher.new(self,
|
403
|
+
NumericalityMatchers::EvenNumberMatcher.new(self, attribute),
|
395
404
|
)
|
396
405
|
self
|
397
406
|
end
|
398
407
|
|
399
408
|
def is_greater_than(value)
|
400
|
-
prepare_submatcher(comparison_matcher_for(value, :>).for(
|
409
|
+
prepare_submatcher(comparison_matcher_for(value, :>).for(attribute))
|
401
410
|
self
|
402
411
|
end
|
403
412
|
|
404
413
|
def is_greater_than_or_equal_to(value)
|
405
|
-
prepare_submatcher(comparison_matcher_for(value, :>=).for(
|
414
|
+
prepare_submatcher(comparison_matcher_for(value, :>=).for(attribute))
|
406
415
|
self
|
407
416
|
end
|
408
417
|
|
409
418
|
def is_equal_to(value)
|
410
|
-
prepare_submatcher(comparison_matcher_for(value, :==).for(
|
419
|
+
prepare_submatcher(comparison_matcher_for(value, :==).for(attribute))
|
411
420
|
self
|
412
421
|
end
|
413
422
|
|
414
423
|
def is_less_than(value)
|
415
|
-
prepare_submatcher(comparison_matcher_for(value, :<).for(
|
424
|
+
prepare_submatcher(comparison_matcher_for(value, :<).for(attribute))
|
416
425
|
self
|
417
426
|
end
|
418
427
|
|
419
428
|
def is_less_than_or_equal_to(value)
|
420
|
-
prepare_submatcher(comparison_matcher_for(value, :<=).for(
|
429
|
+
prepare_submatcher(comparison_matcher_for(value, :<=).for(attribute))
|
421
430
|
self
|
422
431
|
end
|
423
432
|
|
424
433
|
def is_other_than(value)
|
425
|
-
prepare_submatcher(comparison_matcher_for(value, :!=).for(
|
426
|
-
self
|
427
|
-
end
|
428
|
-
|
429
|
-
def with_message(message)
|
430
|
-
@expects_custom_validation_message = true
|
431
|
-
@expected_message = message
|
434
|
+
prepare_submatcher(comparison_matcher_for(value, :!=).for(attribute))
|
432
435
|
self
|
433
436
|
end
|
434
437
|
|
435
|
-
def
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
def on(context)
|
440
|
-
@context = context
|
438
|
+
def is_in(range)
|
439
|
+
prepare_submatcher(
|
440
|
+
NumericalityMatchers::RangeMatcher.new(self, attribute, range),
|
441
|
+
)
|
441
442
|
self
|
442
443
|
end
|
443
444
|
|
@@ -454,9 +455,13 @@ module Shoulda
|
|
454
455
|
def simple_description
|
455
456
|
description = ''
|
456
457
|
|
457
|
-
description << "validate that :#{
|
458
|
+
description << "validate that :#{attribute} looks like "
|
458
459
|
description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
|
459
460
|
|
461
|
+
if range_description.present?
|
462
|
+
description << " #{range_description}"
|
463
|
+
end
|
464
|
+
|
460
465
|
if comparison_descriptions.present?
|
461
466
|
description << " #{comparison_descriptions}"
|
462
467
|
end
|
@@ -464,10 +469,6 @@ module Shoulda
|
|
464
469
|
description
|
465
470
|
end
|
466
471
|
|
467
|
-
def description
|
468
|
-
ValidationMatcher::BuildDescription.call(self, simple_description)
|
469
|
-
end
|
470
|
-
|
471
472
|
def failure_message
|
472
473
|
overall_failure_message.dup.tap do |message|
|
473
474
|
message << "\n"
|
@@ -494,44 +495,29 @@ module Shoulda
|
|
494
495
|
@subject = subject
|
495
496
|
@number_of_submatchers = @submatchers.size
|
496
497
|
|
497
|
-
add_disallow_value_matcher
|
498
498
|
qualify_submatchers
|
499
499
|
end
|
500
500
|
|
501
|
-
def overall_failure_message
|
502
|
-
Shoulda::Matchers.word_wrap(
|
503
|
-
"Expected #{model.name} to #{description}, but this could not "\
|
504
|
-
'be proved.',
|
505
|
-
)
|
506
|
-
end
|
507
|
-
|
508
|
-
def overall_failure_message_when_negated
|
509
|
-
Shoulda::Matchers.word_wrap(
|
510
|
-
"Expected #{model.name} not to #{description}, but this could not "\
|
511
|
-
'be proved.',
|
512
|
-
)
|
513
|
-
end
|
514
|
-
|
515
501
|
def attribute_is_active_record_column?
|
516
|
-
columns_hash.key?(
|
502
|
+
columns_hash.key?(attribute.to_s)
|
517
503
|
end
|
518
504
|
|
519
505
|
def column_type
|
520
|
-
columns_hash[
|
506
|
+
columns_hash[attribute.to_s].type
|
521
507
|
end
|
522
508
|
|
523
509
|
def columns_hash
|
524
|
-
if
|
525
|
-
|
510
|
+
if subject.class.respond_to?(:columns_hash)
|
511
|
+
subject.class.columns_hash
|
526
512
|
else
|
527
513
|
{}
|
528
514
|
end
|
529
515
|
end
|
530
516
|
|
531
|
-
def
|
517
|
+
def add_disallow_non_numeric_value_matcher
|
532
518
|
disallow_value_matcher = DisallowValueMatcher.
|
533
519
|
new(non_numeric_value).
|
534
|
-
for(
|
520
|
+
for(attribute).
|
535
521
|
with_message(:not_a_number)
|
536
522
|
|
537
523
|
add_submatcher(disallow_value_matcher)
|
@@ -543,9 +529,9 @@ module Shoulda
|
|
543
529
|
end
|
544
530
|
|
545
531
|
def comparison_matcher_for(value, operator)
|
546
|
-
|
532
|
+
ComparisonMatcher.
|
547
533
|
new(self, value, operator).
|
548
|
-
for(
|
534
|
+
for(attribute)
|
549
535
|
end
|
550
536
|
|
551
537
|
def add_submatcher(submatcher)
|
@@ -577,8 +563,8 @@ module Shoulda
|
|
577
563
|
submatcher.with_message(@expected_message)
|
578
564
|
end
|
579
565
|
|
580
|
-
if
|
581
|
-
submatcher.on(
|
566
|
+
if context
|
567
|
+
submatcher.on(context)
|
582
568
|
end
|
583
569
|
|
584
570
|
submatcher.ignoring_interference_by_writer(
|
@@ -596,23 +582,25 @@ module Shoulda
|
|
596
582
|
end
|
597
583
|
|
598
584
|
def has_been_qualified?
|
599
|
-
@submatchers.any?
|
600
|
-
|
601
|
-
|
602
|
-
|
585
|
+
@submatchers.any? { |submatcher| submatcher_qualified?(submatcher) }
|
586
|
+
end
|
587
|
+
|
588
|
+
def submatcher_qualified?(submatcher)
|
589
|
+
Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
|
590
|
+
NumericalityMatchers || submatcher.instance_of?(ComparisonMatcher)
|
603
591
|
end
|
604
592
|
|
605
593
|
def first_submatcher_that_fails_to_match
|
606
594
|
@_first_submatcher_that_fails_to_match ||=
|
607
595
|
@submatchers.detect do |submatcher|
|
608
|
-
!submatcher.matches?(
|
596
|
+
!submatcher.matches?(subject)
|
609
597
|
end
|
610
598
|
end
|
611
599
|
|
612
600
|
def first_submatcher_that_fails_to_not_match
|
613
601
|
@_first_submatcher_that_fails_to_not_match ||=
|
614
602
|
@submatchers.detect do |submatcher|
|
615
|
-
!submatcher.does_not_match?(
|
603
|
+
!submatcher.does_not_match?(subject)
|
616
604
|
end
|
617
605
|
end
|
618
606
|
|
@@ -673,8 +661,12 @@ module Shoulda
|
|
673
661
|
end
|
674
662
|
end
|
675
663
|
|
676
|
-
def
|
677
|
-
@
|
664
|
+
def range_description
|
665
|
+
range_submatcher = @submatchers.detect do |submatcher|
|
666
|
+
submatcher.respond_to? :range_description
|
667
|
+
end
|
668
|
+
|
669
|
+
range_submatcher&.range_description
|
678
670
|
end
|
679
671
|
|
680
672
|
def non_numeric_value
|
@@ -186,6 +186,12 @@ module Shoulda
|
|
186
186
|
def blank_values
|
187
187
|
['', ' ', "\n", "\r", "\t", "\f"]
|
188
188
|
end
|
189
|
+
|
190
|
+
def array_column?
|
191
|
+
@subject.class.respond_to?(:columns_hash) &&
|
192
|
+
@subject.class.columns_hash[@attribute.to_s].respond_to?(:array) &&
|
193
|
+
@subject.class.columns_hash[@attribute.to_s].array
|
194
|
+
end
|
189
195
|
end
|
190
196
|
end
|
191
197
|
end
|
@@ -21,11 +21,14 @@ require 'shoulda/matchers/active_model/validate_presence_of_matcher'
|
|
21
21
|
require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
22
22
|
require 'shoulda/matchers/active_model/validate_confirmation_of_matcher'
|
23
23
|
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
24
|
+
require 'shoulda/matchers/active_model/validate_comparison_of_matcher'
|
25
|
+
require 'shoulda/matchers/active_model/comparison_matcher'
|
24
26
|
require 'shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher'
|
25
|
-
require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
|
26
27
|
require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
|
27
28
|
require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
|
28
29
|
require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
|
30
|
+
require 'shoulda/matchers/active_model/numericality_matchers/range_matcher'
|
31
|
+
require 'shoulda/matchers/active_model/numericality_matchers/submatchers'
|
29
32
|
require 'shoulda/matchers/active_model/errors'
|
30
33
|
require 'shoulda/matchers/active_model/have_secure_password_matcher'
|
31
34
|
|
@@ -227,14 +227,6 @@ module Shoulda
|
|
227
227
|
self
|
228
228
|
end
|
229
229
|
|
230
|
-
def with(expected_enum_values)
|
231
|
-
Shoulda::Matchers.warn_about_deprecated_method(
|
232
|
-
'The `with` qualifier on `define_enum_for`',
|
233
|
-
'`with_values`',
|
234
|
-
)
|
235
|
-
with_values(expected_enum_values)
|
236
|
-
end
|
237
|
-
|
238
230
|
def with_prefix(expected_prefix = true)
|
239
231
|
options[:prefix] = expected_prefix
|
240
232
|
self
|
@@ -48,6 +48,30 @@ module Shoulda
|
|
48
48
|
# should have_db_column(:camera_aperture).of_type(:decimal)
|
49
49
|
# end
|
50
50
|
#
|
51
|
+
# ##### of_sql_type
|
52
|
+
#
|
53
|
+
# Use `of_sql_type` to assert that a column is defined as a certain sql_type.
|
54
|
+
#
|
55
|
+
# class CreatePhones < ActiveRecord::Migration
|
56
|
+
# def change
|
57
|
+
# create_table :phones do |t|
|
58
|
+
# t.string :camera_aperture, limit: 36
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# # RSpec
|
64
|
+
# RSpec.describe Phone, type: :model do
|
65
|
+
# it do
|
66
|
+
# should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# # Minitest (Shoulda)
|
71
|
+
# class PhoneTest < ActiveSupport::TestCase
|
72
|
+
# should have_db_column(:camera_aperture).of_sql_type('varchar(36)')
|
73
|
+
# end
|
74
|
+
#
|
51
75
|
# ##### with_options
|
52
76
|
#
|
53
77
|
# Use `with_options` to assert that a column has been defined with
|
@@ -96,6 +120,11 @@ module Shoulda
|
|
96
120
|
self
|
97
121
|
end
|
98
122
|
|
123
|
+
def of_sql_type(sql_column_type)
|
124
|
+
@options[:sql_column_type] = sql_column_type
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
99
128
|
def with_options(opts = {})
|
100
129
|
validate_options(opts)
|
101
130
|
OPTIONS.each do |attribute|
|
@@ -110,6 +139,7 @@ module Shoulda
|
|
110
139
|
@subject = subject
|
111
140
|
column_exists? &&
|
112
141
|
correct_column_type? &&
|
142
|
+
correct_sql_column_type? &&
|
113
143
|
correct_precision? &&
|
114
144
|
correct_limit? &&
|
115
145
|
correct_default? &&
|
@@ -129,12 +159,9 @@ module Shoulda
|
|
129
159
|
|
130
160
|
def description
|
131
161
|
desc = "have db column named #{@column}"
|
132
|
-
if @options.key?(:column_type)
|
133
|
-
|
134
|
-
|
135
|
-
if @options.key?(:precision)
|
136
|
-
desc << " of precision #{@options[:precision]}"
|
137
|
-
end
|
162
|
+
desc << " of type #{@options[:column_type]}" if @options.key?(:column_type)
|
163
|
+
desc << " of sql_type #{@options[:sql_column_type]}" if @options.key?(:sql_column_type)
|
164
|
+
desc << " of precision #{@options[:precision]}" if @options.key?(:precision)
|
138
165
|
desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
|
139
166
|
desc << " of default #{@options[:default]}" if @options.key?(:default)
|
140
167
|
desc << " of null #{@options[:null]}" if @options.key?(:null)
|
@@ -178,6 +205,19 @@ module Shoulda
|
|
178
205
|
end
|
179
206
|
end
|
180
207
|
|
208
|
+
def correct_sql_column_type?
|
209
|
+
return true unless @options.key?(:sql_column_type)
|
210
|
+
|
211
|
+
if matched_column.sql_type.to_s == @options[:sql_column_type].to_s
|
212
|
+
true
|
213
|
+
else
|
214
|
+
@missing =
|
215
|
+
"#{model_class} has a db column named #{@column} " <<
|
216
|
+
"of sql type #{matched_column.sql_type}, not #{@options[:sql_column_type]}."
|
217
|
+
false
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
181
221
|
def correct_precision?
|
182
222
|
return true unless @options.key?(:precision)
|
183
223
|
|
@@ -197,17 +197,28 @@ module Shoulda
|
|
197
197
|
def matched_index
|
198
198
|
@_matched_index ||=
|
199
199
|
if expected_columns.one?
|
200
|
-
|
200
|
+
sorted_indexes.detect do |index|
|
201
201
|
Array.wrap(index.columns) == expected_columns
|
202
202
|
end
|
203
203
|
else
|
204
|
-
|
204
|
+
sorted_indexes.detect do |index|
|
205
205
|
index.columns == expected_columns
|
206
206
|
end
|
207
207
|
end
|
208
208
|
end
|
209
209
|
|
210
|
-
def
|
210
|
+
def sorted_indexes
|
211
|
+
if qualifiers.include?(:unique)
|
212
|
+
# return indexes with unique matching the qualifier first
|
213
|
+
unsorted_indexes.sort_by do |index|
|
214
|
+
index.unique == qualifiers[:unique] ? 0 : 1
|
215
|
+
end
|
216
|
+
else
|
217
|
+
unsorted_indexes
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def unsorted_indexes
|
211
222
|
model.connection.indexes(table_name)
|
212
223
|
end
|
213
224
|
|
@@ -2,7 +2,7 @@ module Shoulda
|
|
2
2
|
module Matchers
|
3
3
|
module ActiveRecord
|
4
4
|
# The `have_implicit_order_column` matcher tests that the model has `implicit_order_column`
|
5
|
-
# assigned to one of the table columns.
|
5
|
+
# assigned to one of the table columns.
|
6
6
|
#
|
7
7
|
# class Product < ApplicationRecord
|
8
8
|
# self.implicit_order_column = :created_at
|
@@ -20,10 +20,8 @@ module Shoulda
|
|
20
20
|
#
|
21
21
|
# @return [HaveImplicitOrderColumnMatcher]
|
22
22
|
#
|
23
|
-
|
24
|
-
|
25
|
-
HaveImplicitOrderColumnMatcher.new(column_name)
|
26
|
-
end
|
23
|
+
def have_implicit_order_column(column_name)
|
24
|
+
HaveImplicitOrderColumnMatcher.new(column_name)
|
27
25
|
end
|
28
26
|
|
29
27
|
# @private
|