Permalink
Browse files

Add `ActiveModel::Validations::AbsenceValidator`, a validator to chec…

…k the absence of attributes.

Add `ActiveModel::Errors#add_on_present` method. Adds error messages to present attributes.
  • Loading branch information...
1 parent c298ee4 commit d72a07f1d1478db9daed847eadb35bfd840674f6 @robotex82 robotex82 committed with steveklabnik Jul 25, 2012
View
@@ -1,4 +1,23 @@
## Rails 4.0.0 (unreleased) ##
+* Add `ActiveModel::Validations::AbsenceValidator`, a validator to check the
+ absence of attributes.
+
+ class Person < ActiveRecord::Base
+ validates_absence_of :first_name
+ end
+
+ person = Person.new
+ person.first_name = "John"
+ person.valid?
+ => false
+ # first_name must be blank
+
+ * Roberto Vasquez Angel*
+
+* Added `ActiveModel::Errors#add_on_present` method. Adds error messages to
+ present attributes.
+
+ *Roberto Vasquez Angel*
* `[attribute]_changed?` now returns `false` after a call to `reset_[attribute]!`
@@ -328,6 +328,19 @@ def add_on_blank(attributes, options = {})
end
end
+ # Will add an error message to each of the attributes in +attributes+ that
+ # is present (using Object#present?).
+ #
+ # person.errors.add_on_present(:name)
+ # person.errors.messages
+ # # => { :name => ["must be blank"] }
+ def add_on_present(attributes, options = {})
+ Array(attributes).flatten.each do |attribute|
+ value = @base.send(:read_attribute_for_validation, attribute)
+ add(attribute, :not_blank, options) if value.present?
+ end
+ end
carlosantoniodasilva
carlosantoniodasilva Dec 17, 2012 Owner

I think @josevalim had some concerns about adding more of these to AMo.

steveklabnik
steveklabnik Dec 17, 2012 Member

#7155 (comment) 😝 💣

😄 😆 ❤️

josevalim
josevalim Dec 18, 2012 Member

Yeah, I don't see the need for such helpers. We don't have add_on_unnaceptable, add_on_non_uniq and as such.

steveklabnik
steveklabnik via email Dec 18, 2012 Member
carlosantoniodasilva
carlosantoniodasilva Dec 18, 2012 Owner

I don't think it needs to be reverted, it's just that this logic could be inside the validator itself instead of adding another api in AMo::Errors I think.

steveklabnik
steveklabnik Dec 21, 2012 Member

Thank you! I've been training all week, so I've been slow on the Rails uptake.

+
# Returns +true+ if an error on the attribute with the given message is
# present, +false+ otherwise. +message+ is treated the same as for +add+.
#
@@ -13,6 +13,7 @@ en:
accepted: "must be accepted"
empty: "can't be empty"
blank: "can't be blank"
+ not_blank: "must be blank"
too_long: "is too long (maximum is %{count} characters)"
too_short: "is too short (minimum is %{count} characters)"
wrong_length: "is the wrong length (should be %{count} characters)"
@@ -0,0 +1,31 @@
+module ActiveModel
+ module Validations
+ # == Active Model Absence Validator
+ class AbsenceValidator < EachValidator #:nodoc:
+ def validate(record)
+ record.errors.add_on_present(attributes, options)
+ end
+ end
+
+ module HelperMethods
+ # Validates that the specified attributes are blank (as defined by
+ # Object#blank?). Happens by default on save.
+ #
+ # class Person < ActiveRecord::Base
+ # validates_absence_of :first_name
+ # end
+ #
+ # The first_name attribute must be in the object and it must be blank.
+ #
+ # Configuration options:
+ # * <tt>:message</tt> - A custom error message (default is: "must be blank").
+ #
+ # There is also a list of default options supported by every validator:
+ # +:if+, +:unless+, +:on+ and +:strict+.
+ # See <tt>ActiveModel::Validation#validates</tt> for more information
+ def validates_absence_of(*attr_names)
+ validates_with AbsenceValidator, _merge_attributes(attr_names)
+ end
+ end
+ end
+end
@@ -0,0 +1,67 @@
+# encoding: utf-8
+require 'cases/helper'
+require 'models/topic'
+require 'models/person'
+require 'models/custom_reader'
+
+class AbsenceValidationTest < ActiveModel::TestCase
+ teardown do
+ Topic.reset_callbacks(:validate)
+ Person.reset_callbacks(:validate)
+ CustomReader.reset_callbacks(:validate)
+ end
+
+ def test_validate_absences
+ Topic.validates_absence_of(:title, :content)
+ t = Topic.new
+ t.title = "foo"
+ t.content = "bar"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ t.title = ""
+ t.content = "something"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:content]
+ t.content = ""
+ assert t.valid?
+ end
+
+ def test_accepts_array_arguments
+ Topic.validates_absence_of %w(title content)
+ t = Topic.new
+ t.title = "foo"
+ t.content = "bar"
+ assert t.invalid?
+ assert_equal ["must be blank"], t.errors[:title]
+ assert_equal ["must be blank"], t.errors[:content]
+ end
+
+ def test_validates_acceptance_of_with_custom_error_using_quotes
+ Person.validates_absence_of :karma, message: "This string contains 'single' and \"double\" quotes"
+ p = Person.new
+ p.karma = "good"
+ assert p.invalid?
+ assert_equal "This string contains 'single' and \"double\" quotes", p.errors[:karma].last
+ end
+
+ def test_validates_absence_of_for_ruby_class
+ Person.validates_absence_of :karma
+ p = Person.new
+ p.karma = "good"
+ assert p.invalid?
+ assert_equal ["must be blank"], p.errors[:karma]
+ p.karma = nil
+ assert p.valid?
+ end
+
+ def test_validates_absence_of_for_ruby_class_with_custom_reader
+ CustomReader.validates_absence_of :karma
+ p = CustomReader.new
+ p[:karma] = "excellent"
+ assert p.invalid?
+ assert_equal ["must be blank"], p.errors[:karma]
+ p[:karma] = ""
+ assert p.valid?
+ end
+end

1 comment on commit d72a07f

Member

Oh, for future reference, this was introduced in #7155.

Please sign in to comment.