From 97488690910520ed8e1f2e164b1982eff5ef1f19 Mon Sep 17 00:00:00 2001 From: Elliot Winkler Date: Wed, 1 Apr 2015 00:00:14 -0600 Subject: [PATCH] Add #on qualifier to numericality matcher This is present in all other matchers through ValidationMatcher. However, ValidateNumericalityOfMatcher does not inherit from ValidationMatcher. (This happens to be okay, since ValidateNumericalityOfMatcher contains submatchers and we have to make sure to pass the context all the way through.) Hat tip @sj26 for most of the content of this commit, as well as @anujbiyani for another approach to this. --- NEWS.md | 4 +++ .../numeric_type_matcher.rb | 5 +++ .../validate_numericality_of_matcher.rb | 5 +++ .../comparison_matcher_spec.rb | 20 +++++++++++ .../even_number_matcher_spec.rb | 20 +++++++++++ .../odd_number_matcher_spec.rb | 20 +++++++++++ .../only_integer_matcher_spec.rb | 20 +++++++++++ .../validate_numericality_of_matcher_spec.rb | 35 ++++++++++++++++++- 8 files changed, 128 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 4df66abb9..0b27f1b51 100644 --- a/NEWS.md +++ b/NEWS.md @@ -70,6 +70,8 @@ * Add `strict` qualifier to `validate_numericality_of`. ([#620]) +* Add `on` qualifier to `validate_numericality_of`. (h/t [#356], [#358]) + [#402]: /~https://github.com/thoughtbot/shoulda-matchers/pull/402 [#587]: /~https://github.com/thoughtbot/shoulda-matchers/pull/587 [#662]: /~https://github.com/thoughtbot/shoulda-matchers/pull/662 @@ -82,6 +84,8 @@ [#677]: /~https://github.com/thoughtbot/shoulda-matchers/pull/677 [#620]: /~https://github.com/thoughtbot/shoulda-matchers/pull/620 [#693]: /~https://github.com/thoughtbot/shoulda-matchers/pull/693 +[#356]: /~https://github.com/thoughtbot/shoulda-matchers/pull/356 +[#358]: /~https://github.com/thoughtbot/shoulda-matchers/pull/358 # 2.8.0 diff --git a/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb b/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb index ca79d6e12..04ecc4ddf 100644 --- a/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +++ b/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb @@ -22,6 +22,11 @@ def strict self end + def on(context) + @disallow_value_matcher.on(context) + self + end + def allowed_type raise NotImplementedError end 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 99fc96100..571a7ff96 100644 --- a/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb @@ -384,6 +384,11 @@ def with_message(message) self end + def on(context) + @submatchers.each { |matcher| matcher.on(context) } + self + end + def matches?(subject) @subject = subject failing_submatchers.empty? diff --git a/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb index 649b5db34..addb3c1c8 100644 --- a/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb @@ -197,6 +197,26 @@ def matcher_qualifier end end + context 'qualified with on and validating with on' do + it 'accepts' do + expect(instance_with_validations(on: :customizable)). + to matcher.on(:customizable) + end + end + + context 'qualified with on but not validating with on' do + it 'accepts since the validation never considers a context' do + expect(instance_with_validations).to matcher.on(:customizable) + end + end + + context 'not qualified with on but validating with on' do + it 'rejects since the validation never runs' do + expect(instance_with_validations(on: :customizable)). + not_to matcher + end + end + describe '#comparison_description' do [{ operator: :>, value: 0, expectation: 'greater than 0' }, { operator: :>=, value: -1.0, expectation: 'greater than or equal to -1.0' }, diff --git a/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb index fe528c19d..94244dc61 100644 --- a/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb @@ -64,6 +64,26 @@ end end + context 'qualified with on and validating with on' do + it 'accepts' do + expect(validating_even_number(on: :customizable)). + to subject.on(:customizable) + end + end + + context 'qualified with on but not validating with on' do + it 'accepts since the validation never considers a context' do + expect(validating_even_number).to subject.on(:customizable) + end + end + + context 'not qualified with on but validating with on' do + it 'rejects since the validation never runs' do + expect(validating_even_number(on: :customizable)). + not_to subject + end + end + def validating_even_number(options = {}) define_model :example, attr: :string do validates_numericality_of :attr, { even: true }.merge(options) diff --git a/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb index 167ca8898..fe071c6fa 100644 --- a/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb @@ -64,6 +64,26 @@ end end + context 'qualified with on and validating with on' do + it 'accepts' do + expect(validating_odd_number(on: :customizable)). + to subject.on(:customizable) + end + end + + context 'qualified with on but not validating with on' do + it 'accepts since the validation never considers a context' do + expect(validating_odd_number).to subject.on(:customizable) + end + end + + context 'not qualified with on but validating with on' do + it 'rejects since the validation never runs' do + expect(validating_odd_number(on: :customizable)). + not_to subject + end + end + def validating_odd_number(options = {}) define_model :example, attr: :string do validates_numericality_of :attr, { odd: true }.merge(options) diff --git a/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb index c833a0e81..d365a3366 100644 --- a/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb @@ -64,6 +64,26 @@ end end + context 'qualified with on and validating with on' do + it 'accepts' do + expect(validating_only_integer(on: :customizable)). + to subject.on(:customizable) + end + end + + context 'qualified with on but not validating with on' do + it 'accepts since the validation never considers a context' do + expect(validating_only_integer).to subject.on(:customizable) + end + end + + context 'not qualified with on but validating with on' do + it 'rejects since the validation never runs' do + expect(validating_only_integer(on: :customizable)). + not_to subject + end + end + def validating_only_integer(options = {}) define_model :example, attr: :string do validates_numericality_of :attr, { only_integer: true }.merge(options) diff --git a/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb index 6d01aa255..8e4c9549c 100644 --- a/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb @@ -55,6 +55,12 @@ def all_qualifiers name: :only_integer, validation_name: :only_integer, validation_value: true, + }, + { + name: :on, + argument: :customizable, + validation_name: :on, + validation_value: :customizable } ] end @@ -454,6 +460,32 @@ def default_validation_values end end + context 'qualified with on and validating with on' do + it 'accepts' do + record = build_record_validating_numericality(on: :customizable) + expect(record).to validate_numericality.on(:customizable) + end + end + + context 'qualified with on but not validating with on' do + it 'accepts since the validation never considers a context' do + record = build_record_validating_numericality + expect(record).to validate_numericality.on(:customizable) + end + end + + context 'not qualified with on but validating with on' do + it 'rejects since the validation never runs' do + record = build_record_validating_numericality(on: :customizable) + assertion = lambda do + expect(record).to validate_numericality + end + expect(&assertion).to fail_with_message_including( + 'Expected errors to include "is not a number"' + ) + end + end + context 'with combinations of qualifiers together' do all_qualifier_combinations.each do |combination| if combination.size > 1 @@ -778,7 +810,8 @@ def apply_qualifiers!(args) end def define_model_validating_numericality(options = {}) - attribute_name = options.fetch(:attribute_name) { self.attribute_name } + attribute_name = options.delete(:attribute_name) { self.attribute_name } + define_model 'Example', attribute_name => :string do |model| model.validates_numericality_of(attribute_name, options) end