diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2007-07-24 01:48:17 +0000 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2007-07-24 01:48:17 +0000 |
commit | 1e1f93fd10f186a764cdff0e89936c55274171a9 (patch) | |
tree | f65a6f126c2716fce779a8e01747918d6eb13ac5 | |
parent | 230615b2860165368b95aa31a75d719b0b0c379f (diff) | |
download | rails-1e1f93fd10f186a764cdff0e89936c55274171a9.tar.gz rails-1e1f93fd10f186a764cdff0e89936c55274171a9.tar.bz2 rails-1e1f93fd10f186a764cdff0e89936c55274171a9.zip |
Added :unless clause to validations (closes #8003) [monki]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@7215 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r-- | activerecord/CHANGELOG | 10 | ||||
-rwxr-xr-x | activerecord/lib/active_record/validations.rb | 39 | ||||
-rwxr-xr-x | activerecord/test/validations_test.rb | 65 |
3 files changed, 105 insertions, 9 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 886d049de0..5cb626f91a 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -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] diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index c2f381e6d1..10ca8fa291 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -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) diff --git a/activerecord/test/validations_test.rb b/activerecord/test/validations_test.rb index 105b8edd40..19bca29fd8 100755 --- a/activerecord/test/validations_test.rb +++ b/activerecord/test/validations_test.rb @@ -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!") |