forked from rails/rails
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AR has a subclass of AM:PresenceValidator.
This allows us to mark the parent object as invalid if all associated objects in a presence validated association are marked for destruction. See: rails#6812
- Loading branch information
Brent Wheeldon & Nick Monje
authored and
Ben Moss
committed
Jul 20, 2012
1 parent
215d41d
commit 9feda92
Showing
5 changed files
with
118 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
44 changes: 44 additions & 0 deletions
44
activerecord/test/cases/validations/presence_validation_test.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters