shoulda-matchers 5.3.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/LICENSE +1 -1
- data/README.md +39 -15
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +7 -9
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +13 -15
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +46 -1
- data/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +7 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +16 -6
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +0 -6
- data/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +532 -0
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +3 -3
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +24 -11
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +64 -9
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +40 -96
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +6 -7
- 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 +2 -1
- data/lib/shoulda/matchers/active_record/association_matcher.rb +543 -15
- 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/association_matchers/optional_matcher.rb +23 -19
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +27 -23
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +0 -8
- data/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +46 -6
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +24 -13
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +3 -5
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -1
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +82 -70
- data/lib/shoulda/matchers/active_record.rb +2 -0
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +2 -6
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -6
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +13 -15
- 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/rails_shim.rb +8 -6
- data/lib/shoulda/matchers/util/word_wrap.rb +1 -1
- data/lib/shoulda/matchers/util.rb +18 -20
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers.rb +2 -2
- data/shoulda-matchers.gemspec +1 -1
- metadata +11 -8
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +0 -136
| @@ -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
         | 
| @@ -357,51 +357,33 @@ module Shoulda | |
| 357 357 | 
             
                  end
         | 
| 358 358 |  | 
| 359 359 | 
             
                  # @private
         | 
| 360 | 
            -
                  class ValidateNumericalityOfMatcher
         | 
| 360 | 
            +
                  class ValidateNumericalityOfMatcher < ValidationMatcher
         | 
| 361 361 | 
             
                    NUMERIC_NAME = 'number'.freeze
         | 
| 362 362 | 
             
                    DEFAULT_DIFF_TO_COMPARE = 1
         | 
| 363 363 |  | 
| 364 | 
            -
                    include Qualifiers::IgnoringInterferenceByWriter
         | 
| 365 | 
            -
             | 
| 366 364 | 
             
                    attr_reader :diff_to_compare
         | 
| 367 365 |  | 
| 368 366 | 
             
                    def initialize(attribute)
         | 
| 369 367 | 
             
                      super
         | 
| 370 | 
            -
                      @attribute = attribute
         | 
| 371 368 | 
             
                      @submatchers = []
         | 
| 372 369 | 
             
                      @diff_to_compare = DEFAULT_DIFF_TO_COMPARE
         | 
| 373 | 
            -
                      @expects_custom_validation_message = false
         | 
| 374 370 | 
             
                      @expects_to_allow_nil = false
         | 
| 375 | 
            -
                      @expects_strict = false
         | 
| 376 371 | 
             
                      @allowed_type_adjective = nil
         | 
| 377 372 | 
             
                      @allowed_type_name = 'number'
         | 
| 378 | 
            -
                      @context = nil
         | 
| 379 | 
            -
                      @expected_message = nil
         | 
| 380 | 
            -
                    end
         | 
| 381 | 
            -
             | 
| 382 | 
            -
                    def strict
         | 
| 383 | 
            -
                      @expects_strict = true
         | 
| 384 | 
            -
                      self
         | 
| 385 | 
            -
                    end
         | 
| 386 373 |  | 
| 387 | 
            -
             | 
| 388 | 
            -
                      @expects_strict
         | 
| 374 | 
            +
                      add_disallow_non_numeric_value_matcher
         | 
| 389 375 | 
             
                    end
         | 
| 390 376 |  | 
| 391 377 | 
             
                    def only_integer
         | 
| 392 378 | 
             
                      prepare_submatcher(
         | 
| 393 | 
            -
                        NumericalityMatchers::OnlyIntegerMatcher.new(self,  | 
| 379 | 
            +
                        NumericalityMatchers::OnlyIntegerMatcher.new(self, attribute),
         | 
| 394 380 | 
             
                      )
         | 
| 395 381 | 
             
                      self
         | 
| 396 382 | 
             
                    end
         | 
| 397 383 |  | 
| 398 384 | 
             
                    def allow_nil
         | 
| 399 385 | 
             
                      @expects_to_allow_nil = true
         | 
| 400 | 
            -
                      prepare_submatcher(
         | 
| 401 | 
            -
                        AllowValueMatcher.new(nil).
         | 
| 402 | 
            -
                          for(@attribute).
         | 
| 403 | 
            -
                          with_message(:not_a_number),
         | 
| 404 | 
            -
                      )
         | 
| 386 | 
            +
                      prepare_submatcher(allow_value_matcher(nil, :not_a_number))
         | 
| 405 387 | 
             
                      self
         | 
| 406 388 | 
             
                    end
         | 
| 407 389 |  | 
| @@ -411,70 +393,55 @@ module Shoulda | |
| 411 393 |  | 
| 412 394 | 
             
                    def odd
         | 
| 413 395 | 
             
                      prepare_submatcher(
         | 
| 414 | 
            -
                        NumericalityMatchers::OddNumberMatcher.new(self,  | 
| 396 | 
            +
                        NumericalityMatchers::OddNumberMatcher.new(self, attribute),
         | 
| 415 397 | 
             
                      )
         | 
| 416 398 | 
             
                      self
         | 
| 417 399 | 
             
                    end
         | 
| 418 400 |  | 
| 419 401 | 
             
                    def even
         | 
| 420 402 | 
             
                      prepare_submatcher(
         | 
| 421 | 
            -
                        NumericalityMatchers::EvenNumberMatcher.new(self,  | 
| 403 | 
            +
                        NumericalityMatchers::EvenNumberMatcher.new(self, attribute),
         | 
| 422 404 | 
             
                      )
         | 
| 423 405 | 
             
                      self
         | 
| 424 406 | 
             
                    end
         | 
| 425 407 |  | 
| 426 408 | 
             
                    def is_greater_than(value)
         | 
| 427 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :>).for( | 
| 409 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :>).for(attribute))
         | 
| 428 410 | 
             
                      self
         | 
| 429 411 | 
             
                    end
         | 
| 430 412 |  | 
| 431 413 | 
             
                    def is_greater_than_or_equal_to(value)
         | 
| 432 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :>=).for( | 
| 414 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :>=).for(attribute))
         | 
| 433 415 | 
             
                      self
         | 
| 434 416 | 
             
                    end
         | 
| 435 417 |  | 
| 436 418 | 
             
                    def is_equal_to(value)
         | 
| 437 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :==).for( | 
| 419 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :==).for(attribute))
         | 
| 438 420 | 
             
                      self
         | 
| 439 421 | 
             
                    end
         | 
| 440 422 |  | 
| 441 423 | 
             
                    def is_less_than(value)
         | 
| 442 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :<).for( | 
| 424 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :<).for(attribute))
         | 
| 443 425 | 
             
                      self
         | 
| 444 426 | 
             
                    end
         | 
| 445 427 |  | 
| 446 428 | 
             
                    def is_less_than_or_equal_to(value)
         | 
| 447 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :<=).for( | 
| 429 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :<=).for(attribute))
         | 
| 448 430 | 
             
                      self
         | 
| 449 431 | 
             
                    end
         | 
| 450 432 |  | 
| 451 433 | 
             
                    def is_other_than(value)
         | 
| 452 | 
            -
                      prepare_submatcher(comparison_matcher_for(value, :!=).for( | 
| 434 | 
            +
                      prepare_submatcher(comparison_matcher_for(value, :!=).for(attribute))
         | 
| 453 435 | 
             
                      self
         | 
| 454 436 | 
             
                    end
         | 
| 455 437 |  | 
| 456 438 | 
             
                    def is_in(range)
         | 
| 457 439 | 
             
                      prepare_submatcher(
         | 
| 458 | 
            -
                        NumericalityMatchers::RangeMatcher.new(self,  | 
| 440 | 
            +
                        NumericalityMatchers::RangeMatcher.new(self, attribute, range),
         | 
| 459 441 | 
             
                      )
         | 
| 460 442 | 
             
                      self
         | 
| 461 443 | 
             
                    end
         | 
| 462 444 |  | 
| 463 | 
            -
                    def with_message(message)
         | 
| 464 | 
            -
                      @expects_custom_validation_message = true
         | 
| 465 | 
            -
                      @expected_message = message
         | 
| 466 | 
            -
                      self
         | 
| 467 | 
            -
                    end
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                    def expects_custom_validation_message?
         | 
| 470 | 
            -
                      @expects_custom_validation_message
         | 
| 471 | 
            -
                    end
         | 
| 472 | 
            -
             | 
| 473 | 
            -
                    def on(context)
         | 
| 474 | 
            -
                      @context = context
         | 
| 475 | 
            -
                      self
         | 
| 476 | 
            -
                    end
         | 
| 477 | 
            -
             | 
| 478 445 | 
             
                    def matches?(subject)
         | 
| 479 446 | 
             
                      matches_or_does_not_match?(subject)
         | 
| 480 447 | 
             
                      first_submatcher_that_fails_to_match.nil?
         | 
| @@ -486,24 +453,18 @@ module Shoulda | |
| 486 453 | 
             
                    end
         | 
| 487 454 |  | 
| 488 455 | 
             
                    def simple_description
         | 
| 489 | 
            -
                       | 
| 456 | 
            +
                      String.new.tap do |description|
         | 
| 457 | 
            +
                        description << "validate that :#{attribute} looks like "
         | 
| 458 | 
            +
                        description << Shoulda::Matchers::Util.a_or_an(full_allowed_type)
         | 
| 490 459 |  | 
| 491 | 
            -
             | 
| 492 | 
            -
             | 
| 493 | 
            -
             | 
| 494 | 
            -
                      if range_description.present?
         | 
| 495 | 
            -
                        description << " #{range_description}"
         | 
| 496 | 
            -
                      end
         | 
| 460 | 
            +
                        if range_description.present?
         | 
| 461 | 
            +
                          description << " #{range_description}"
         | 
| 462 | 
            +
                        end
         | 
| 497 463 |  | 
| 498 | 
            -
             | 
| 499 | 
            -
             | 
| 464 | 
            +
                        if comparison_descriptions.present?
         | 
| 465 | 
            +
                          description << " #{comparison_descriptions}"
         | 
| 466 | 
            +
                        end
         | 
| 500 467 | 
             
                      end
         | 
| 501 | 
            -
             | 
| 502 | 
            -
                      description
         | 
| 503 | 
            -
                    end
         | 
| 504 | 
            -
             | 
| 505 | 
            -
                    def description
         | 
| 506 | 
            -
                      ValidationMatcher::BuildDescription.call(self, simple_description)
         | 
| 507 468 | 
             
                    end
         | 
| 508 469 |  | 
| 509 470 | 
             
                    def failure_message
         | 
| @@ -532,44 +493,29 @@ module Shoulda | |
| 532 493 | 
             
                      @subject = subject
         | 
| 533 494 | 
             
                      @number_of_submatchers = @submatchers.size
         | 
| 534 495 |  | 
| 535 | 
            -
                      add_disallow_value_matcher
         | 
| 536 496 | 
             
                      qualify_submatchers
         | 
| 537 497 | 
             
                    end
         | 
| 538 498 |  | 
| 539 | 
            -
                    def overall_failure_message
         | 
| 540 | 
            -
                      Shoulda::Matchers.word_wrap(
         | 
| 541 | 
            -
                        "Expected #{model.name} to #{description}, but this could not "\
         | 
| 542 | 
            -
                        'be proved.',
         | 
| 543 | 
            -
                      )
         | 
| 544 | 
            -
                    end
         | 
| 545 | 
            -
             | 
| 546 | 
            -
                    def overall_failure_message_when_negated
         | 
| 547 | 
            -
                      Shoulda::Matchers.word_wrap(
         | 
| 548 | 
            -
                        "Expected #{model.name} not to #{description}, but this could not "\
         | 
| 549 | 
            -
                        'be proved.',
         | 
| 550 | 
            -
                      )
         | 
| 551 | 
            -
                    end
         | 
| 552 | 
            -
             | 
| 553 499 | 
             
                    def attribute_is_active_record_column?
         | 
| 554 | 
            -
                      columns_hash.key?( | 
| 500 | 
            +
                      columns_hash.key?(attribute.to_s)
         | 
| 555 501 | 
             
                    end
         | 
| 556 502 |  | 
| 557 503 | 
             
                    def column_type
         | 
| 558 | 
            -
                      columns_hash[ | 
| 504 | 
            +
                      columns_hash[attribute.to_s].type
         | 
| 559 505 | 
             
                    end
         | 
| 560 506 |  | 
| 561 507 | 
             
                    def columns_hash
         | 
| 562 | 
            -
                      if  | 
| 563 | 
            -
                         | 
| 508 | 
            +
                      if subject.class.respond_to?(:columns_hash)
         | 
| 509 | 
            +
                        subject.class.columns_hash
         | 
| 564 510 | 
             
                      else
         | 
| 565 511 | 
             
                        {}
         | 
| 566 512 | 
             
                      end
         | 
| 567 513 | 
             
                    end
         | 
| 568 514 |  | 
| 569 | 
            -
                    def  | 
| 515 | 
            +
                    def add_disallow_non_numeric_value_matcher
         | 
| 570 516 | 
             
                      disallow_value_matcher = DisallowValueMatcher.
         | 
| 571 517 | 
             
                        new(non_numeric_value).
         | 
| 572 | 
            -
                        for( | 
| 518 | 
            +
                        for(attribute).
         | 
| 573 519 | 
             
                        with_message(:not_a_number)
         | 
| 574 520 |  | 
| 575 521 | 
             
                      add_submatcher(disallow_value_matcher)
         | 
| @@ -581,9 +527,9 @@ module Shoulda | |
| 581 527 | 
             
                    end
         | 
| 582 528 |  | 
| 583 529 | 
             
                    def comparison_matcher_for(value, operator)
         | 
| 584 | 
            -
                       | 
| 530 | 
            +
                      ComparisonMatcher.
         | 
| 585 531 | 
             
                        new(self, value, operator).
         | 
| 586 | 
            -
                        for( | 
| 532 | 
            +
                        for(attribute)
         | 
| 587 533 | 
             
                    end
         | 
| 588 534 |  | 
| 589 535 | 
             
                    def add_submatcher(submatcher)
         | 
| @@ -615,8 +561,8 @@ module Shoulda | |
| 615 561 | 
             
                          submatcher.with_message(@expected_message)
         | 
| 616 562 | 
             
                        end
         | 
| 617 563 |  | 
| 618 | 
            -
                        if  | 
| 619 | 
            -
                          submatcher.on( | 
| 564 | 
            +
                        if context
         | 
| 565 | 
            +
                          submatcher.on(context)
         | 
| 620 566 | 
             
                        end
         | 
| 621 567 |  | 
| 622 568 | 
             
                        submatcher.ignoring_interference_by_writer(
         | 
| @@ -634,23 +580,25 @@ module Shoulda | |
| 634 580 | 
             
                    end
         | 
| 635 581 |  | 
| 636 582 | 
             
                    def has_been_qualified?
         | 
| 637 | 
            -
                      @submatchers.any?  | 
| 638 | 
            -
             | 
| 639 | 
            -
             | 
| 640 | 
            -
             | 
| 583 | 
            +
                      @submatchers.any? { |submatcher| submatcher_qualified?(submatcher) }
         | 
| 584 | 
            +
                    end
         | 
| 585 | 
            +
             | 
| 586 | 
            +
                    def submatcher_qualified?(submatcher)
         | 
| 587 | 
            +
                      Shoulda::Matchers::RailsShim.parent_of(submatcher.class) ==
         | 
| 588 | 
            +
                        NumericalityMatchers || submatcher.instance_of?(ComparisonMatcher)
         | 
| 641 589 | 
             
                    end
         | 
| 642 590 |  | 
| 643 591 | 
             
                    def first_submatcher_that_fails_to_match
         | 
| 644 592 | 
             
                      @_first_submatcher_that_fails_to_match ||=
         | 
| 645 593 | 
             
                        @submatchers.detect do |submatcher|
         | 
| 646 | 
            -
                          !submatcher.matches?( | 
| 594 | 
            +
                          !submatcher.matches?(subject)
         | 
| 647 595 | 
             
                        end
         | 
| 648 596 | 
             
                    end
         | 
| 649 597 |  | 
| 650 598 | 
             
                    def first_submatcher_that_fails_to_not_match
         | 
| 651 599 | 
             
                      @_first_submatcher_that_fails_to_not_match ||=
         | 
| 652 600 | 
             
                        @submatchers.detect do |submatcher|
         | 
| 653 | 
            -
                          !submatcher.does_not_match?( | 
| 601 | 
            +
                          !submatcher.does_not_match?(subject)
         | 
| 654 602 | 
             
                        end
         | 
| 655 603 | 
             
                    end
         | 
| 656 604 |  | 
| @@ -719,10 +667,6 @@ module Shoulda | |
| 719 667 | 
             
                      range_submatcher&.range_description
         | 
| 720 668 | 
             
                    end
         | 
| 721 669 |  | 
| 722 | 
            -
                    def model
         | 
| 723 | 
            -
                      @subject.class
         | 
| 724 | 
            -
                    end
         | 
| 725 | 
            -
             | 
| 726 670 | 
             
                    def non_numeric_value
         | 
| 727 671 | 
             
                      'abcd'
         | 
| 728 672 | 
             
                    end
         | 
| @@ -42,13 +42,12 @@ module Shoulda | |
| 42 42 | 
             
                        description_clauses = []
         | 
| 43 43 |  | 
| 44 44 | 
             
                        if matcher.try(:expects_strict?)
         | 
| 45 | 
            -
                          description_clauses << | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
                             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                          description_clauses.last << ' on failure'
         | 
| 45 | 
            +
                          description_clauses <<
         | 
| 46 | 
            +
                            if matcher.try(:expects_custom_validation_message?)
         | 
| 47 | 
            +
                              'raising a validation exception with a custom message on failure'
         | 
| 48 | 
            +
                            else
         | 
| 49 | 
            +
                              'raising a validation exception on failure'
         | 
| 50 | 
            +
                            end
         | 
| 52 51 | 
             
                        elsif matcher.try(:expects_custom_validation_message?)
         | 
| 53 52 | 
             
                          description_clauses <<
         | 
| 54 53 | 
             
                            'producing a custom validation error on failure'
         | 
| @@ -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,8 +21,9 @@ 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'
         |