diff --git a/NEWS.md b/NEWS.md index 59cfecc0..46b61254 100644 --- a/NEWS.md +++ b/NEWS.md @@ -30,6 +30,9 @@ * Additionally, we now manage Appraisals using the `appraisal` executable that is coming in Appraisal 1.0.0 (we're using the beta for now). +* Add `allow_nil` option to `validate_numericality_of` so that you can validate + that numeric values are validated only if a value is supplied. + # v 2.5.0 * Fix Rails/Test::Unit integration to ensure that the test case classes we are diff --git a/README.md b/README.md index 959c9249..331d86b0 100644 --- a/README.md +++ b/README.md @@ -448,6 +448,7 @@ class Person < ActiveRecord::Base validates_numericality_of :birth_year, less_than_or_equal_to: 1987 validates_numericality_of :birth_day, odd: true validates_numericality_of :birth_month, even: true + validates_numericality_of :rank, less_than_or_equal_to: 10, allow_nil: true validates_numericality_of :number_of_dependents, message: 'Number of dependents must be a number' diff --git a/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb index 954279c5..3c5ff013 100644 --- a/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb @@ -10,12 +10,14 @@ module Shoulda # :nodoc: # * only_integer - allows only integer values # * odd - Specifies the value must be an odd number. # * even - Specifies the value must be an even number. + # * allow_nil - allows nil values # # Examples: # it { should validate_numericality_of(:price) } # it { should validate_numericality_of(:age).only_integer } # it { should validate_numericality_of(:frequency).odd } # it { should validate_numericality_of(:frequency).even } + # it { should validate_numericality_of(:rank).less_than_or_equal_to(10).allow_nil } # def validate_numericality_of(attr) ValidateNumericalityOfMatcher.new(attr) @@ -37,6 +39,11 @@ module Shoulda # :nodoc: self end + def allow_nil + add_submatcher(AllowValueMatcher.new(nil).for(@attribute).with_message(:not_a_number)) + self + end + def is_greater_than(value) add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>).for(@attribute)) self diff --git a/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb b/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb index 2a3cefe6..61006f8a 100644 --- a/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +++ b/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb @@ -47,6 +47,19 @@ describe Shoulda::Matchers::ActiveModel::ValidateNumericalityOfMatcher do end end + context 'with the allow_nil option' do + it 'allows nil values for that attribute' do + expect(validating_numericality(allow_nil: true)).to matcher.allow_nil + end + + it 'rejects when the model does not allow nil' do + the_matcher = matcher.allow_nil + expect { + expect(validating_numericality).to the_matcher + }.to fail_with_message_including('Did not expect errors to include "is not a number"') + end + end + context 'with the only_integer option' do it 'allows integer values for that attribute' do expect(validating_numericality(only_integer: true)).to matcher.only_integer