提交 1fab518c 编写于 作者: J José Valim

Merge pull request #6827 from zephyr-dev/master

Validates_presence_of associated object marked for destruction
......@@ -96,6 +96,13 @@
*kennyj*
* Changed validates_presence_of on an association so that children objects
do not validate as being present if they are marked for destruction. This
prevents you from saving the parent successfully and thus putting the parent
in an invalid state.
*Nick Monje & Brent Wheeldon*
* `FinderMethods#exists?` now returns `false` with the `false` argument.
*Egor Lynko*
......
......@@ -81,3 +81,4 @@ def perform_validations(options={})
require "active_record/validations/associated"
require "active_record/validations/uniqueness"
require "active_record/validations/presence"
module ActiveRecord
module Validations
class PresenceValidator < ActiveModel::Validations::PresenceValidator
def validate(record)
super
attributes.each do |attribute|
next unless record.class.reflect_on_association(attribute)
value = record.send(attribute)
if Array(value).all? { |r| r.marked_for_destruction? }
record.errors.add(attribute, :blank, options)
end
end
end
end
module ClassMethods
# Validates that the specified attributes are not blank (as defined by
# Object#blank?), and, if the attribute is an association, that the
# associated object is not marked for destruction. Happens by default
# on save.
#
# class Person < ActiveRecord::Base
# has_one :face
# validates_presence_of :face
# end
#
# The face attribute must be in the object and it cannot be blank or marked
# for destruction.
#
# If you want to validate the presence of a boolean field (where the real values
# are true and false), you will want to use
# <tt>validates_inclusion_of :field_name, :in => [true, false]</tt>.
#
# This is due to the way Object#blank? handles boolean values:
# <tt>false.blank? # => true</tt>.
#
# This validator defers to the ActiveModel validation for presence, adding the
# check to see that an associated object is not marked for destruction. This
# prevents the parent object from validating successfully and saving, which then
# deletes the associated object, thus putting the parent object into an invalid
# state.
#
# Configuration options:
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
# validation contexts by default (+nil+), other options are <tt>:create</tt>
# and <tt>:update</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, proc
# or string should return or evaluate to a true or false value.
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
# if the validation should not occur (e.g. <tt>:unless => :skip_validation</tt>,
# or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
# proc or string should return or evaluate to a true or false value.
# * <tt>:strict</tt> - Specifies whether validation should be strict.
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
#
def validates_presence_of(*attr_names)
validates_with PresenceValidator, _merge_attributes(attr_names)
end
end
end
end
# encoding: utf-8
require "cases/helper"
require 'models/man'
require 'models/face'
require 'models/interest'
class PresenceValidationTest < ActiveRecord::TestCase
class Boy < Man; end
repair_validations(Boy)
def test_validates_presence_of_non_association
Boy.validates_presence_of(:name)
b = Boy.new
assert b.invalid?
b.name = "Alex"
assert b.valid?
end
def test_validates_presence_of_has_one_marked_for_destruction
Boy.validates_presence_of(:face)
b = Boy.new
f = Face.new
b.face = f
assert b.valid?
f.mark_for_destruction
assert b.invalid?
end
def test_validates_presence_of_has_many_marked_for_destruction
Boy.validates_presence_of(:interests)
b = Boy.new
b.interests << [i1 = Interest.new, i2 = Interest.new]
assert b.valid?
i1.mark_for_destruction
assert b.valid?
i2.mark_for_destruction
assert b.invalid?
end
end
......@@ -374,6 +374,8 @@ class LineItem < ActiveRecord::Base
end
</ruby>
If you validate the presence of an object associated via a +has_one+ or +has_many+ relationship, it will check that the object is neither +blank?+ nor +marked_for_destruction?+.
Since +false.blank?+ is true, if you want to validate the presence of a boolean field you should use <tt>validates :field_name, :inclusion => { :in => [true, false] }</tt>.
The default error message is "_can't be empty_".
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册