shoulda-matchers 5.2.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|