thoughtbot--shoulda-matchers/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb

72 lines
1.9 KiB
Ruby
Raw Normal View History

Add `in: range` matcher to validate_numericality_of (#1512) Closes: #1493 In Rails 7 was added a new option to validate numericality. You can use `in: range` to specify a range to validate an attribute. ```ruby class User < ApplicationRecord validates :age, numericality: { greater_than_or_equal_to: 18, less_than_or_equal_to: 65 } end class User < ApplicationRecord validates :age, numericality: { in: 18..65 } end ``` In this commit we are adding the support matcher to this new functionality, while also making a refactor on the numericality matchers that use the concept of submatchers. We've created a new class (`NumericalityMatchers::Submatcher`) that's been used by `NumericalityMatchers::RangeMatcher` and `NumericalityMatchers::ComparisonMatcher`, this new class wil handle shared logic regarding having submatchers that will check if the parent matcher is valid or not. Our new class `Numericality::Matchers::RangeMatcher` is using as submatchers two `NumericalityMatchers::ComparisonMatcher` instances to avoid creating new logic to handle this new option and also to replicate what was being used before this option existed in Rails (see example above) In this commit we are adding: * NumericalityMatchers::RangeMatcher file to support the new `in: range` option. * Specs on ValidateNumericalityOfMatcherSpec file for the new supported option, only running on rails_versions > 7. * NumericalityMatchers::Submatchers file to handle having submatchers inside a matcher file. * Refactors to NumericalityMatchers::ComparisonMatcher.
2022-10-29 11:17:09 +00:00
require 'active_support/core_ext/module/delegation'
module Shoulda
module Matchers
module ActiveModel
module NumericalityMatchers
# @private
class RangeMatcher < ValidationMatcher
OPERATORS = [:>=, :<=].freeze
delegate :failure_message, to: :submatchers
def initialize(numericality_matcher, attribute, range)
super(attribute)
unless numericality_matcher.respond_to? :diff_to_compare
raise ArgumentError, 'numericality_matcher is invalid'
end
@numericality_matcher = numericality_matcher
@range = range
@attribute = attribute
end
def matches?(subject)
@subject = subject
submatchers.matches?(subject)
end
def simple_description
description = ''
if expects_strict?
description << ' strictly'
end
description +
"disallow :#{attribute} from being a number that is not " +
range_description
end
def range_description
"from #{Shoulda::Matchers::Util.inspect_range(@range)}"
end
def submatchers
@_submatchers ||= NumericalityMatchers::Submatchers.new(build_submatchers)
end
private
def build_submatchers
submatcher_combos.map do |value, operator|
build_comparison_submatcher(value, operator)
end
end
def submatcher_combos
@range.minmax.zip(OPERATORS)
end
def build_comparison_submatcher(value, operator)
NumericalityMatchers::ComparisonMatcher.new(@numericality_matcher, value, operator).
for(@attribute).
with_message(@message).
on(@context)
end
end
end
end
end
end