Added :unless clause to validations (closes #8003) [monki]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7215 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson
2007-07-24 01:48:17 +00:00
parent 230615b286
commit 1e1f93fd10
3 changed files with 105 additions and 9 deletions

View File

@@ -1,5 +1,15 @@
*SVN*
* Added :unless clause to validations #8003 [monki]. Example:
def using_open_id?
!identity_url.blank?
end
validates_presence_of :identity_url, :if => using_open_id?
validates_presence_of :username, :unless => using_open_id?
validates_presence_of :password, :unless => using_open_id?
* Fix #count on a has_many :through association so that it recognizes the :uniq option. Closes #8801 [lifofifo]
* Fix and properly document/test count(column_name) usage. Closes #8999 [lifofifo]

View File

@@ -362,14 +362,17 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_each(*attrs)
options = attrs.last.is_a?(Hash) ? attrs.pop.symbolize_keys : {}
attrs = attrs.flatten
# Declare the validation.
send(validation_method(options[:on] || :save)) do |record|
# Don't validate when there is an :if condition and that condition is false
unless options[:if] && !evaluate_condition(options[:if], record)
# Don't validate when there is an :if condition and that condition is false or there is an :unless condition and that condition is true
unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record))
attrs.each do |attr|
value = record.send(attr)
next if value.nil? && options[:allow_nil]
@@ -401,6 +404,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_confirmation_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -431,6 +437,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_acceptance_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -460,6 +469,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
#
# === Warning
# Validate the presence of the foreign key, not the instance variable itself.
@@ -480,7 +492,7 @@ module ActiveRecord
# while errors.add_on_empty can
attr_names.each do |attr_name|
send(validation_method(configuration[:on])) do |record|
unless configuration[:if] and not evaluate_condition(configuration[:if], record)
unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record))
record.errors.add_on_blank(attr_name,configuration[:message])
end
end
@@ -514,6 +526,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_length_of(*attrs)
# Merge given options with defaults.
options = {
@@ -599,6 +614,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_uniqueness_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -647,6 +665,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_format_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -672,6 +693,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_inclusion_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -699,6 +723,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_exclusion_of(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -739,6 +766,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_associated(*attr_names)
configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
@@ -772,6 +802,9 @@ module ActiveRecord
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). 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. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
# method, proc or string should return or evaluate to a true or false value.
def validates_numericality_of(*attr_names)
configuration = { :on => :save, :only_integer => false, :allow_nil => false }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)

View File

@@ -988,7 +988,7 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
end
def test_conditional_validation_using_method_true
def test_if_validation_using_method_true
# When the method returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -997,7 +997,15 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
def test_conditional_validation_using_method_false
def test_unless_validation_using_method_true
# When the method returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_if_validation_using_method_false
# When the method returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -1005,7 +1013,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
def test_conditional_validation_using_string_true
def test_unless_validation_using_method_false
# When the method returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "hoo 5", t.errors["title"]
end
def test_if_validation_using_string_true
# When the evaluated string returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -1014,7 +1031,15 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
def test_conditional_validation_using_string_false
def test_unless_validation_using_string_true
# When the evaluated string returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_if_validation_using_string_false
# When the evaluated string returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
@@ -1022,7 +1047,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
def test_conditional_validation_using_block_true
def test_unless_validation_using_string_false
# When the evaluated string returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "hoo 5", t.errors["title"]
end
def test_if_validation_using_block_true
# When the block returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:if => Proc.new { |r| r.content.size > 4 } )
@@ -1032,7 +1066,16 @@ class ValidationsTest < Test::Unit::TestCase
assert_equal "hoo 5", t.errors["title"]
end
def test_conditional_validation_using_block_false
def test_unless_validation_using_block_true
# When the block returns true
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:unless => Proc.new { |r| r.content.size > 4 } )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_if_validation_using_block_false
# When the block returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:if => Proc.new { |r| r.title != "uhohuhoh"} )
@@ -1041,6 +1084,16 @@ class ValidationsTest < Test::Unit::TestCase
assert !t.errors.on(:title)
end
def test_unless_validation_using_block_false
# When the block returns false
Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
:unless => Proc.new { |r| r.title != "uhohuhoh"} )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "hoo 5", t.errors["title"]
end
def test_validates_associated_missing
Reply.validates_presence_of(:topic)
r = Reply.create("title" => "A reply", "content" => "with content!")