shoulda-matchers 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -3
  4. data/CONTRIBUTING.md +60 -28
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +15 -12
  7. data/NEWS.md +111 -0
  8. data/README.md +94 -6
  9. data/Rakefile +10 -8
  10. data/custom_plan.rb +88 -0
  11. data/gemfiles/4.0.0.gemfile +1 -0
  12. data/gemfiles/4.0.0.gemfile.lock +21 -18
  13. data/gemfiles/4.0.1.gemfile +1 -0
  14. data/gemfiles/4.0.1.gemfile.lock +21 -18
  15. data/gemfiles/4.1.gemfile +1 -0
  16. data/gemfiles/4.1.gemfile.lock +21 -18
  17. data/gemfiles/4.2.gemfile +1 -0
  18. data/gemfiles/4.2.gemfile.lock +24 -21
  19. data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
  20. data/lib/shoulda/matchers/active_model.rb +10 -1
  21. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
  22. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  23. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  24. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
  25. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  26. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  27. data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  28. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  29. data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  30. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
  31. data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
  32. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
  36. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
  37. data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
  38. data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  39. data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  40. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
  41. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
  42. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
  43. data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
  44. data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
  45. data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
  46. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
  47. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
  48. data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
  49. data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
  50. data/lib/shoulda/matchers/active_model/validator.rb +71 -52
  51. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
  52. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
  53. data/lib/shoulda/matchers/util.rb +43 -0
  54. data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
  55. data/lib/shoulda/matchers/version.rb +1 -1
  56. data/script/update_gem_in_all_appraisals +1 -1
  57. data/script/update_gems_in_all_appraisals +1 -1
  58. data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
  59. data/spec/acceptance/rails_integration_spec.rb +6 -2
  60. data/spec/spec_helper.rb +1 -3
  61. data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
  62. data/spec/support/tests/current_bundle.rb +21 -7
  63. data/spec/support/unit/active_record/create_table.rb +54 -0
  64. data/spec/support/unit/attribute.rb +47 -0
  65. data/spec/support/unit/capture.rb +6 -0
  66. data/spec/support/unit/change_value.rb +111 -0
  67. data/spec/support/unit/create_model_arguments/basic.rb +135 -0
  68. data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
  69. data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
  70. data/spec/support/unit/helpers/active_record_versions.rb +1 -1
  71. data/spec/support/unit/helpers/class_builder.rb +61 -47
  72. data/spec/support/unit/helpers/database_helpers.rb +5 -3
  73. data/spec/support/unit/helpers/model_builder.rb +77 -97
  74. data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
  75. data/spec/support/unit/load_environment.rb +12 -0
  76. data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
  77. data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
  78. data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
  79. data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
  80. data/spec/support/unit/model_creators.rb +19 -0
  81. data/spec/support/unit/model_creators/active_model.rb +39 -0
  82. data/spec/support/unit/model_creators/active_record.rb +43 -0
  83. data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
  84. data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
  85. data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
  86. data/spec/support/unit/model_creators/basic.rb +97 -0
  87. data/spec/support/unit/rails_application.rb +1 -1
  88. data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
  89. data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
  90. data/spec/support/unit/validation_matcher_scenario.rb +62 -0
  91. data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
  92. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
  93. data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
  94. data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
  95. data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
  96. data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
  97. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
  98. data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
  99. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
  100. data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
  101. data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
  102. data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
  103. data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
  104. data/spec/unit_spec_helper.rb +10 -22
  105. data/zeus.json +11 -0
  106. metadata +64 -23
  107. data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
  108. data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
  109. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
  110. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
  111. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
  112. data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -253,40 +253,50 @@ module Shoulda
253
253
  self
254
254
  end
255
255
 
256
- def with_short_message(message)
256
+ def with_message(message)
257
257
  if message
258
+ @expects_custom_validation_message = true
258
259
  @short_message = message
260
+ @long_message = message
259
261
  end
262
+
260
263
  self
261
264
  end
262
265
 
263
- def with_long_message(message)
266
+ def with_short_message(message)
264
267
  if message
265
- @long_message = message
268
+ @expects_custom_validation_message = true
269
+ @short_message = message
266
270
  end
271
+
267
272
  self
268
273
  end
269
274
 
270
- def with_message(message)
275
+ def with_long_message(message)
271
276
  if message
272
- @short_message = message
277
+ @expects_custom_validation_message = true
273
278
  @long_message = message
274
279
  end
280
+
275
281
  self
276
282
  end
277
283
 
278
- def description
279
- description = "ensure #{@attribute} has a length "
284
+ def simple_description
285
+ description = "validate that the length of :#{@attribute}"
286
+
280
287
  if @options.key?(:minimum) && @options.key?(:maximum)
281
288
  if @options[:minimum] == @options[:maximum]
282
- description << "of exactly #{@options[:minimum]}"
289
+ description << " is #{@options[:minimum]}"
283
290
  else
284
- description << "between #{@options[:minimum]} and #{@options[:maximum]}"
291
+ description << " is between #{@options[:minimum]}"
292
+ description << " and #{@options[:maximum]}"
285
293
  end
286
- else
287
- description << "of at least #{@options[:minimum]}" if @options[:minimum]
288
- description << "of at most #{@options[:maximum]}" if @options[:maximum]
294
+ elsif @options.key?(:minimum)
295
+ description << " is at least #{@options[:minimum]}"
296
+ elsif @options.key?(:maximum)
297
+ description << " is at most #{@options[:maximum]}"
289
298
  end
299
+
290
300
  description
291
301
  end
292
302
 
@@ -279,7 +279,7 @@ module Shoulda
279
279
  #
280
280
  # Use `allow_nil` to assert that the attribute allows nil.
281
281
  #
282
- # class Age
282
+ # class Post
283
283
  # include ActiveModel::Model
284
284
  # attr_accessor :age
285
285
  #
@@ -304,27 +304,37 @@ module Shoulda
304
304
 
305
305
  # @private
306
306
  class ValidateNumericalityOfMatcher
307
- NUMERIC_NAME = 'numbers'
307
+ NUMERIC_NAME = 'number'
308
308
  NON_NUMERIC_VALUE = 'abcd'
309
309
  DEFAULT_DIFF_TO_COMPARE = 1
310
310
 
311
+ include Qualifiers::IgnoringInterferenceByWriter
312
+
311
313
  attr_reader :diff_to_compare
312
314
 
313
315
  def initialize(attribute)
316
+ super
314
317
  @attribute = attribute
315
318
  @submatchers = []
316
319
  @diff_to_compare = DEFAULT_DIFF_TO_COMPARE
317
- @strict = false
318
-
319
- add_disallow_value_matcher
320
+ @expects_custom_validation_message = false
321
+ @expects_to_allow_nil = false
322
+ @expects_strict = false
323
+ @allowed_type_adjective = nil
324
+ @allowed_type_name = 'number'
325
+ @context = nil
326
+ @expected_message = nil
320
327
  end
321
328
 
322
329
  def strict
323
- @strict = true
324
- @submatchers.each(&:strict)
330
+ @expects_strict = true
325
331
  self
326
332
  end
327
333
 
334
+ def expects_strict?
335
+ @expects_strict
336
+ end
337
+
328
338
  def only_integer
329
339
  prepare_submatcher(
330
340
  NumericalityMatchers::OnlyIntegerMatcher.new(self, @attribute)
@@ -333,6 +343,7 @@ module Shoulda
333
343
  end
334
344
 
335
345
  def allow_nil
346
+ @expects_to_allow_nil = true
336
347
  prepare_submatcher(
337
348
  AllowValueMatcher.new(nil)
338
349
  .for(@attribute)
@@ -341,6 +352,10 @@ module Shoulda
341
352
  self
342
353
  end
343
354
 
355
+ def expects_to_allow_nil?
356
+ @expects_to_allow_nil
357
+ end
358
+
344
359
  def odd
345
360
  prepare_submatcher(
346
361
  NumericalityMatchers::OddNumberMatcher.new(self, @attribute)
@@ -381,120 +396,216 @@ module Shoulda
381
396
  end
382
397
 
383
398
  def with_message(message)
384
- @submatchers.each { |matcher| matcher.with_message(message) }
399
+ @expects_custom_validation_message = true
400
+ @expected_message = message
385
401
  self
386
402
  end
387
403
 
404
+ def expects_custom_validation_message?
405
+ @expects_custom_validation_message
406
+ end
407
+
388
408
  def on(context)
389
- @submatchers.each { |matcher| matcher.on(context) }
409
+ @context = context
390
410
  self
391
411
  end
392
412
 
393
413
  def matches?(subject)
394
414
  @subject = subject
415
+ @number_of_submatchers = @submatchers.size
395
416
 
396
- if given_numeric_column?
397
- remove_disallow_value_matcher
398
- end
399
-
400
- if @submatchers.empty?
401
- raise IneffectiveTestError.create(
402
- model: @subject.class,
403
- attribute: @attribute,
404
- column_type: column_type
405
- )
406
- end
417
+ add_disallow_value_matcher
418
+ qualify_submatchers
407
419
 
408
420
  first_failing_submatcher.nil?
409
421
  end
410
422
 
411
- def description
412
- description_parts = ["only allow #{allowed_types} for #{@attribute}"]
423
+ def simple_description
424
+ description = ''
425
+
426
+ description << "validate that :#{@attribute} looks like "
427
+ description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
413
428
 
414
429
  if comparison_descriptions.present?
415
- description_parts << comparison_descriptions
430
+ description << ' ' + comparison_descriptions
416
431
  end
417
432
 
418
- if @strict
419
- description_parts.insert(1, 'strictly')
420
- description_parts.join(', ')
421
- else
422
- description_parts.join(' ')
423
- end
433
+ description
434
+ end
435
+
436
+ def description
437
+ ValidationMatcher::BuildDescription.call(self, simple_description)
424
438
  end
425
439
 
426
440
  def failure_message
427
- first_failing_submatcher.failure_message
441
+ overall_failure_message.dup.tap do |message|
442
+ message << "\n"
443
+ message << failure_message_for_first_failing_submatcher
444
+ end
428
445
  end
429
446
 
430
447
  def failure_message_when_negated
431
- first_failing_submatcher.failure_message_when_negated
448
+ overall_failure_message_when_negated.dup.tap do |message|
449
+ if submatcher_failure_message_when_negated.present?
450
+ raise "hmm, this needs to be implemented."
451
+ message << "\n"
452
+ message << Shoulda::Matchers.word_wrap(
453
+ submatcher_failure_message_when_negated,
454
+ indent: 2
455
+ )
456
+ end
457
+ end
432
458
  end
433
459
 
434
460
  def given_numeric_column?
435
- [:integer, :float, :decimal].include?(column_type)
461
+ attribute_is_active_record_column? &&
462
+ [:integer, :float, :decimal].include?(column_type)
436
463
  end
437
464
 
438
465
  private
439
466
 
467
+ def model
468
+ @subject.class
469
+ end
470
+
471
+ def overall_failure_message
472
+ Shoulda::Matchers.word_wrap(
473
+ "#{model.name} did not properly #{description}."
474
+ )
475
+ end
476
+
477
+ def overall_failure_message_when_negated
478
+ Shoulda::Matchers.word_wrap(
479
+ "Expected #{model.name} not to #{description}, but it did."
480
+ )
481
+ end
482
+
483
+ def attribute_is_active_record_column?
484
+ columns_hash.key?(@attribute.to_s)
485
+ end
486
+
440
487
  def column_type
488
+ columns_hash[@attribute.to_s].type
489
+ end
490
+
491
+ def columns_hash
441
492
  if @subject.class.respond_to?(:columns_hash)
442
- @subject.class.columns_hash[@attribute.to_s].type
493
+ @subject.class.columns_hash
494
+ else
495
+ {}
443
496
  end
444
497
  end
445
498
 
446
499
  def add_disallow_value_matcher
447
- disallow_value_matcher = DisallowValueMatcher.new(NON_NUMERIC_VALUE).
500
+ disallow_value_matcher = DisallowValueMatcher.
501
+ new(NON_NUMERIC_VALUE).
448
502
  for(@attribute).
449
503
  with_message(:not_a_number)
450
504
 
451
505
  add_submatcher(disallow_value_matcher)
452
506
  end
453
507
 
454
- def remove_disallow_value_matcher
455
- @submatchers.shift
456
- end
457
-
458
508
  def prepare_submatcher(submatcher)
459
509
  add_submatcher(submatcher)
460
-
461
- if submatcher.respond_to?(:diff_to_compare)
462
- update_diff_to_compare(submatcher)
463
- end
510
+ submatcher
464
511
  end
465
512
 
466
513
  def comparison_matcher_for(value, operator)
467
- NumericalityMatchers::ComparisonMatcher
468
- .new(self, value, operator)
469
- .for(@attribute)
514
+ NumericalityMatchers::ComparisonMatcher.
515
+ new(self, value, operator).
516
+ for(@attribute)
470
517
  end
471
518
 
472
519
  def add_submatcher(submatcher)
520
+ if submatcher.respond_to?(:allowed_type_name)
521
+ @allowed_type_name = submatcher.allowed_type_name
522
+ end
523
+
524
+ if submatcher.respond_to?(:allowed_type_adjective)
525
+ @allowed_type_adjective = submatcher.allowed_type_adjective
526
+ end
527
+
528
+ if submatcher.respond_to?(:diff_to_compare)
529
+ @diff_to_compare = [@diff_to_compare, submatcher.diff_to_compare].max
530
+ end
531
+
473
532
  @submatchers << submatcher
474
533
  end
475
534
 
476
- def update_diff_to_compare(matcher)
477
- @diff_to_compare = [@diff_to_compare, matcher.diff_to_compare].max
535
+ def qualify_submatchers
536
+ @submatchers.each do |submatcher|
537
+ if @expects_strict
538
+ submatcher.strict(@expects_strict)
539
+ end
540
+
541
+ if @expected_message.present?
542
+ submatcher.with_message(@expected_message)
543
+ end
544
+
545
+ if @context
546
+ submatcher.on(@context)
547
+ end
548
+
549
+ submatcher.ignoring_interference_by_writer(
550
+ ignore_interference_by_writer
551
+ )
552
+ end
553
+ end
554
+
555
+ def number_of_submatchers_for_failure_message
556
+ if has_been_qualified?
557
+ @submatchers.size - 1
558
+ else
559
+ @submatchers.size
560
+ end
561
+ end
562
+
563
+ def has_been_qualified?
564
+ @submatchers.any? do |submatcher|
565
+ submatcher.class.parent == NumericalityMatchers
566
+ end
478
567
  end
479
568
 
480
569
  def first_failing_submatcher
481
- @_first_failing_submatcher ||= @submatchers.detect do |submatcher|
570
+ @_failing_submatchers ||= @submatchers.detect do |submatcher|
482
571
  !submatcher.matches?(@subject)
483
572
  end
484
573
  end
485
574
 
486
- def allowed_types
487
- allowed_array = submatcher_allowed_types
488
- allowed_array.empty? ? NUMERIC_NAME : allowed_array.join(', ')
575
+ def submatcher_failure_message
576
+ first_failing_submatcher.failure_message
489
577
  end
490
578
 
491
- def submatcher_allowed_types
492
- @submatchers.inject([]){|m, s| m << s.allowed_type if s.respond_to?(:allowed_type); m }
579
+ def submatcher_failure_message_when_negated
580
+ first_failing_submatcher.failure_message_when_negated
581
+ end
582
+
583
+ def failure_message_for_first_failing_submatcher
584
+ submatcher = first_failing_submatcher
585
+
586
+ if number_of_submatchers_for_failure_message > 1
587
+ submatcher_description = submatcher.simple_description.
588
+ sub(/\bvalidate that\b/, 'validates').
589
+ sub(/\bdisallow\b/, 'disallows').
590
+ sub(/\ballow\b/, 'allows')
591
+ submatcher_message =
592
+ "In checking that #{model.name} #{submatcher_description}, " +
593
+ submatcher.failure_message[0].downcase +
594
+ submatcher.failure_message[1..-1]
595
+ else
596
+ submatcher_message = submatcher.failure_message
597
+ end
598
+
599
+ Shoulda::Matchers.word_wrap(submatcher_message, indent: 2)
600
+ end
601
+
602
+ def full_allowed_type
603
+ "#{@allowed_type_adjective} #{@allowed_type_name}".strip
493
604
  end
494
605
 
495
606
  def comparison_descriptions
496
607
  description_array = submatcher_comparison_descriptions
497
- description_array.empty? ? '' : 'which are ' + submatcher_comparison_descriptions.join(' and ')
608
+ description_array.empty? ? '' : submatcher_comparison_descriptions.join(' and ')
498
609
  end
499
610
 
500
611
  def submatcher_comparison_descriptions
@@ -505,39 +616,6 @@ module Shoulda
505
616
  arr
506
617
  end
507
618
  end
508
-
509
- class IneffectiveTestError < Shoulda::Matchers::Error
510
- attr_accessor :model, :attribute, :column_type
511
-
512
- def message
513
- Shoulda::Matchers.word_wrap <<-MESSAGE
514
- You are attempting to use validate_numericality_of, but the attribute you're
515
- testing, :#{attribute}, is #{a_or_an(column_type)} column. One of the things
516
- that the numericality matcher does is to assert that setting :#{attribute} to a
517
- string that doesn't look like #{a_or_an(column_type)} will cause your
518
- #{humanized_model_name} to become invalid. In this case, it's impossible to make
519
- this assertion since :#{attribute} will typecast any incoming value to
520
- #{a_or_an(column_type)}. This means that it's already guaranteed to be numeric!
521
- Since this matcher isn't doing anything, you can remove it from your model
522
- tests, and in fact, you can remove the validation from your model as it isn't
523
- doing anything either.
524
- MESSAGE
525
- end
526
-
527
- private
528
-
529
- def humanized_model_name
530
- model.name.humanize.downcase
531
- end
532
-
533
- def a_or_an(next_word)
534
- if next_word =~ /[aeiou]/
535
- "an #{next_word}"
536
- else
537
- "a #{next_word}"
538
- end
539
- end
540
- end
541
619
  end
542
620
  end
543
621
  end
@@ -111,24 +111,24 @@ module Shoulda
111
111
 
112
112
  # @private
113
113
  class ValidatePresenceOfMatcher < ValidationMatcher
114
- def with_message(message)
115
- @expected_message = message if message
116
- self
114
+ def initialize(attribute)
115
+ super
116
+ @expected_message = :blank
117
117
  end
118
118
 
119
119
  def matches?(subject)
120
120
  super(subject)
121
- @expected_message ||= :blank
122
121
 
123
122
  if secure_password_being_validated?
123
+ ignore_interference_by_writer.default_to(when: :blank?)
124
124
  disallows_and_double_checks_value_of!(blank_value, @expected_message)
125
125
  else
126
126
  disallows_original_or_typecast_value?(blank_value, @expected_message)
127
127
  end
128
128
  end
129
129
 
130
- def description
131
- "require #{@attribute} to be set"
130
+ def simple_description
131
+ "validate that :#{@attribute} cannot be empty/falsy"
132
132
  end
133
133
 
134
134
  private
@@ -141,14 +141,12 @@ module Shoulda
141
141
 
142
142
  def disallows_and_double_checks_value_of!(value, message)
143
143
  disallows_value_of(value, message)
144
- rescue ActiveModel::AllowValueMatcher::CouldNotSetAttributeError
144
+ rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError
145
145
  raise ActiveModel::CouldNotSetPasswordError.create(@subject.class)
146
146
  end
147
147
 
148
148
  def disallows_original_or_typecast_value?(value, message)
149
149
  disallows_value_of(blank_value, @expected_message)
150
- rescue ActiveModel::AllowValueMatcher::CouldNotSetAttributeError => error
151
- error.actual_value.blank?
152
150
  end
153
151
 
154
152
  def blank_value