Merge pull request #6827 from zephyr-dev/master
Validates_presence_of associated object marked for destruction
This commit is contained in:
commit
1fab518c6a
@ -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"
|
||||
|
64
activerecord/lib/active_record/validations/presence.rb
Normal file
64
activerecord/lib/active_record/validations/presence.rb
Normal file
@ -0,0 +1,64 @@
|
||||
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
|
@ -0,0 +1,44 @@
|
||||
# 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_".
|
||||
|
Loading…
Reference in New Issue
Block a user