diff options
Diffstat (limited to 'activemodel')
18 files changed, 77 insertions, 50 deletions
diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 09fdd84844..0148066bac 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,3 +1,23 @@ +* Ability to specify multiple contexts when defining a validation. + + Example: + + class Person + include ActiveModel::Validations + + attr_reader :name + validates_presence_of :name, on: [:verify, :approve] + end + + person = Person.new + person.valid? # => true + person.valid?(:verify) # => false + person.errors.full_messages_for(:name) # => ["Name can't be blank"] + person.valid?(:approve) # => false + person.errors.full_messages_for(:name) # => ["Name can't be blank"] + + *Vince Puzzella* + * `attribute_changed?` now accepts parameters which check the old and new value of the attribute `model.name_changed?(from: "Pete", to: "Ringo")` diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 08928a713d..e9674d5143 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -66,8 +66,10 @@ module ActiveModel # end # # Options: - # * <tt>:on</tt> - Specifies the context where this validation is active - # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>) + # * <tt>:on</tt> - Specifies the contexts where this validation is active. + # You can pass a symbol or an array of symbols. + # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt> or + # <tt>on: [:create, :custom_validation_context]</tt>) # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+. # * <tt>:allow_blank</tt> - Skip validation if attribute is blank. # * <tt>:if</tt> - Specifies a method, proc or string to call to determine @@ -124,8 +126,10 @@ module ActiveModel # end # # Options: - # * <tt>:on</tt> - Specifies the context where this validation is active - # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt>) + # * <tt>:on</tt> - Specifies the contexts where this validation is active. + # You can pass a symbol or an array of symbols. + # (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt> or + # <tt>on: [:create, :custom_validation_context]</tt>) # * <tt>:if</tt> - Specifies a method, proc or string to call to determine # if the validation should occur (e.g. <tt>if: :allow_validation</tt>, # or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, @@ -141,7 +145,7 @@ module ActiveModel options = options.dup options[:if] = Array(options[:if]) options[:if].unshift lambda { |o| - o.validation_context == options[:on] + Array(options[:on]).include?(o.validation_context) } end args << options @@ -197,12 +201,12 @@ module ActiveModel # # #<StrictValidator:0x007fbff3204a30 @options={strict:true}> # # ] # - # If one runs Person.clear_validators! and then checks to see what + # If one runs <tt>Person.clear_validators!</tt> and then checks to see what # validators this class has, you would obtain: # # Person.validators # => [] # - # Also, the callback set by +validate :cannot_be_robot+ will be erased + # Also, the callback set by <tt>validate :cannot_be_robot</tt> will be erased # so that: # # Person._validate_callbacks.empty? # => true diff --git a/activemodel/test/cases/validations/absence_validation_test.rb b/activemodel/test/cases/validations/absence_validation_test.rb index c05d71de5a..795ce16d28 100644 --- a/activemodel/test/cases/validations/absence_validation_test.rb +++ b/activemodel/test/cases/validations/absence_validation_test.rb @@ -6,9 +6,9 @@ require 'models/custom_reader' class AbsenceValidationTest < ActiveModel::TestCase teardown do - Topic.reset_callbacks(:validate) - Person.reset_callbacks(:validate) - CustomReader.reset_callbacks(:validate) + Topic.clear_validators! + Person.clear_validators! + CustomReader.clear_validators! end def test_validate_absences diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb index dc413bef30..d9d8c6b0f8 100644 --- a/activemodel/test/cases/validations/acceptance_validation_test.rb +++ b/activemodel/test/cases/validations/acceptance_validation_test.rb @@ -8,7 +8,7 @@ require 'models/person' class AcceptanceValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_terms_of_service_agreement_no_acceptance diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 5049d6dd61..1261937b56 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -6,7 +6,7 @@ require 'models/topic' class ConditionalValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_if_validation_using_method_true diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb index f03de2c24a..4957ba5d0a 100644 --- a/activemodel/test/cases/validations/confirmation_validation_test.rb +++ b/activemodel/test/cases/validations/confirmation_validation_test.rb @@ -7,7 +7,7 @@ require 'models/person' class ConfirmationValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_no_title_confirmation @@ -49,7 +49,7 @@ class ConfirmationValidationTest < ActiveModel::TestCase p.karma = "None" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end def test_title_confirmation_with_i18n_attribute diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb index 81455ba519..1ce41f9bc9 100644 --- a/activemodel/test/cases/validations/exclusion_validation_test.rb +++ b/activemodel/test/cases/validations/exclusion_validation_test.rb @@ -7,7 +7,7 @@ require 'models/person' class ExclusionValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_validates_exclusion_of @@ -50,7 +50,7 @@ class ExclusionValidationTest < ActiveModel::TestCase p.karma = "Lifo" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end def test_validates_exclusion_of_with_lambda @@ -87,6 +87,6 @@ class ExclusionValidationTest < ActiveModel::TestCase assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end end diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index 26e8dbf19c..0f91b73cd7 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -7,7 +7,7 @@ require 'models/person' class PresenceValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_validate_format @@ -68,11 +68,11 @@ class PresenceValidationTest < ActiveModel::TestCase assert t.invalid? assert_equal ["can't be Invalid title"], t.errors[:title] end - + def test_validate_format_of_with_multiline_regexp_should_raise_error assert_raise(ArgumentError) { Topic.validates_format_of(:title, with: /^Valid Title$/) } end - + def test_validate_format_of_with_multiline_regexp_and_option assert_nothing_raised(ArgumentError) do Topic.validates_format_of(:title, with: /^Valid Title$/, multiline: true) @@ -144,6 +144,6 @@ class PresenceValidationTest < ActiveModel::TestCase p.karma = "1234" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end end diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 40a5aee997..93600c587a 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -4,7 +4,7 @@ require 'models/person' class I18nGenerateMessageValidationTest < ActiveModel::TestCase def setup - Person.reset_callbacks(:validate) + Person.clear_validators! @person = Person.new end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index e29771d6b7..d10010537e 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -6,7 +6,7 @@ require 'models/person' class I18nValidationTest < ActiveModel::TestCase def setup - Person.reset_callbacks(:validate) + Person.clear_validators! @person = Person.new @old_load_path, @old_backend = I18n.load_path.dup, I18n.backend @@ -16,7 +16,7 @@ class I18nValidationTest < ActiveModel::TestCase end def teardown - Person.reset_callbacks(:validate) + Person.clear_validators! I18n.load_path.replace @old_load_path I18n.backend = @old_backend end diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb index 8b90856869..3a8f3080e1 100644 --- a/activemodel/test/cases/validations/inclusion_validation_test.rb +++ b/activemodel/test/cases/validations/inclusion_validation_test.rb @@ -8,7 +8,7 @@ require 'models/person' class InclusionValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_validates_inclusion_of_range @@ -105,7 +105,7 @@ class InclusionValidationTest < ActiveModel::TestCase p.karma = "monkey" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end def test_validates_inclusion_of_with_lambda @@ -142,6 +142,6 @@ class InclusionValidationTest < ActiveModel::TestCase assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end end diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 8b2f886cc4..046ffcb16f 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -6,7 +6,7 @@ require 'models/person' class LengthValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_validates_length_of_with_allow_nil @@ -354,7 +354,7 @@ class LengthValidationTest < ActiveModel::TestCase p.karma = "The Smiths" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end def test_validates_length_of_for_infinite_maxima diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 84332ed014..f77cf47fb7 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -9,7 +9,7 @@ require 'bigdecimal' class NumericalityValidationTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end NIL = [nil] @@ -157,7 +157,7 @@ class NumericalityValidationTest < ActiveModel::TestCase p.karma = "1234" assert p.valid? ensure - Person.reset_callbacks(:validate) + Person.clear_validators! end def test_validates_numericality_with_invalid_args diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index 2f228cfa83..ecf16d1e16 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -8,9 +8,9 @@ require 'models/custom_reader' class PresenceValidationTest < ActiveModel::TestCase teardown do - Topic.reset_callbacks(:validate) - Person.reset_callbacks(:validate) - CustomReader.reset_callbacks(:validate) + Topic.clear_validators! + Person.clear_validators! + CustomReader.clear_validators! end def test_validate_presences diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index c1914b32bc..699a872e42 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -11,9 +11,9 @@ class ValidatesTest < ActiveModel::TestCase teardown :reset_callbacks def reset_callbacks - Person.reset_callbacks(:validate) - Topic.reset_callbacks(:validate) - PersonWithValidator.reset_callbacks(:validate) + Person.clear_validators! + Topic.clear_validators! + PersonWithValidator.clear_validators! end def test_validates_with_messages_empty diff --git a/activemodel/test/cases/validations/validations_context_test.rb b/activemodel/test/cases/validations/validations_context_test.rb index 5f99b320a6..005bf118c6 100644 --- a/activemodel/test/cases/validations/validations_context_test.rb +++ b/activemodel/test/cases/validations/validations_context_test.rb @@ -4,10 +4,8 @@ require 'cases/helper' require 'models/topic' class ValidationsContextTest < ActiveModel::TestCase - def teardown - Topic.reset_callbacks(:validate) - Topic._validators.clear + Topic.clear_validators! end ERROR_MESSAGE = "Validation error from validator" @@ -36,4 +34,17 @@ class ValidationsContextTest < ActiveModel::TestCase assert topic.invalid?(:create), "Validation does run on create if 'on' is set to create" assert topic.errors[:base].include?(ERROR_MESSAGE) end + + test "with a class that adds errors on multiple contexts and validating a new model" do + Topic.validates_with(ValidatorThatAddsErrors, on: [:context1, :context2]) + + topic = Topic.new + assert topic.valid?, "Validation ran with no context given when 'on' is set to context1 and context2" + + assert topic.invalid?(:context1), "Validation did not run on context1 when 'on' is set to context1 and context2" + assert topic.errors[:base].include?(ERROR_MESSAGE) + + assert topic.invalid?(:context2), "Validation did not run on context2 when 'on' is set to context1 and context2" + assert topic.errors[:base].include?(ERROR_MESSAGE) + end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 93716f1433..736c2deea8 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -6,8 +6,7 @@ require 'models/topic' class ValidatesWithTest < ActiveModel::TestCase def teardown - Topic.reset_callbacks(:validate) - Topic._validators.clear + Topic.clear_validators! end ERROR_MESSAGE = "Validation error from validator" diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 039b6b8872..bee8ece992 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -10,17 +10,10 @@ require 'active_support/json' require 'active_support/xml_mini' class ValidationsTest < ActiveModel::TestCase - class CustomStrictValidationException < StandardError; end - def setup - Topic._validators.clear - end - - # Most of the tests mess with the validations of Topic, so lets repair it all the time. - # Other classes we mess with will be dealt with in the specific tests def teardown - Topic.reset_callbacks(:validate) + Topic.clear_validators! end def test_single_field_validation |