Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself. Added _on_create and _on_update versions for all the new validations

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@107 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2004-12-10 13:11:13 +00:00
parent 8712652dd9
commit 0b92b7de2f
3 changed files with 81 additions and 24 deletions

View File

@ -12,6 +12,8 @@
errors.on(:name) # => "must be shorter"
errors.on("name") # => "must be shorter"
* Added Base.validate_presence as an alternative to implementing validate and doing errors.add_on_empty yourself.
* Added Base.validate_confirmation that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example:
Model:
@ -24,10 +26,9 @@
<%= password_field "person", "password_confirmation" %>
The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
It exists only as an in-memory variable for validating the password.
NOTE: This validation is only happening on create. When you want to update the record, you'll have to decide and pursue your
own course of action.
It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
See validate_confirmation_on_create and validate_confirmation_on_update if you want to restrict the validation to just one of the two
situations.
* Added Base.validate_confirmation that encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
@ -40,7 +41,9 @@
View:
<%= check_box "person", "terms_of_service" %>
The terms_of_service attribute is entirely virtual. It's only used for validation at the time of creation. No database column is needed.
The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
See validate_acceptance_on_create and validate_acceptance_on_update if you want to restrict the validation to just one of the two
situations.
NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.

View File

@ -69,21 +69,30 @@ module ActiveRecord
# <%= password_field "person", "password_confirmation" %>
#
# The person has to already have a password attribute (a column in the people table), but the password_confirmation is virtual.
# It exists only as an in-memory variable for validating the password.
#
# NOTE: This validation is only happening on create. When you want to update the record, you'll have to decide and pursue your
# own course of action.
# It exists only as an in-memory variable for validating the password. This check is performed both on create and update.
# See validate_confirmation_on_create and validate_confirmation_on_update if you want to restrict the validation to just one of the two
# situations.
def validate_confirmation(*attr_names)
error_message = attr_names.last.is_a?(String) ? attr_names.pop : "doesn't match confirmation"
validation_method = block_given? ? yield : "validate"
for attr_name in attr_names
attr_accessor "#{attr_name}_confirmation"
class_eval <<-EOM
validate_on_create %{errors.add('#{attr_name}', "#{error_message}") unless #{attr_name} == #{attr_name}_confirmation}
EOM
class_eval(%(#{validation_method} %{errors.add('#{attr_name}', "#{error_message}") unless #{attr_name} == #{attr_name}_confirmation}))
end
end
# Works like validate_confirmation, but only performs the validation on creation (for new records).
def validate_confirmation_on_create(*attr_names)
validate_confirmation(*attr_names) { "validate_on_create" }
end
# Works like validate_confirmation, but only performs the validation on creation (for new records).
def validate_confirmation_on_update(*attr_names)
validate_confirmation(*attr_names) { "validate_on_update" }
end
# Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example:
#
# Model:
@ -95,19 +104,51 @@ EOM
# View:
# <%= check_box "person", "terms_of_service" %>
#
# The terms_of_service attribute is entirely virtual. It's only used for validation at the time of creation. No database column is needed.
# The terms_of_service attribute is entirely virtual. No database column is needed. This check is performed both on create and update.
# See validate_acceptance_on_create and validate_acceptance_on_update if you want to restrict the validation to just one of the two
# situations.
#
# NOTE: The agreement is considered valid if it's set to the string "1". This makes it easy to relate it to an HTML checkbox.
def validate_acceptance(*attr_names)
error_message = attr_names.last.is_a?(String) ? attr_names.pop : "must be accepted"
validation_method = block_given? ? yield : "validate"
for attr_name in attr_names
attr_accessor(attr_name)
class_eval <<-EOM
validate_on_create %{errors.add('#{attr_name}', '#{error_message}') unless #{attr_name} == "1"}
EOM
class_eval(%(#{validation_method} %{errors.add('#{attr_name}', '#{error_message}') unless #{attr_name} == "1"}))
end
end
# Works like validate_acceptance, but only performs the validation on creation (for new records).
def validate_acceptance_on_create(*attr_names)
validate_acceptance(*attr_names) { "validate_on_create" }
end
# Works like validate_acceptance, but only performs the validation on update (for existing records).
def validate_acceptance_on_update(*attr_names)
validate_acceptance(*attr_names) { "validate_on_update" }
end
def validate_presence(*attr_names)
error_message = attr_names.last.is_a?(String) ? attr_names.pop : "can't be empty"
validation_method = block_given? ? yield : "validate"
for attr_name in attr_names
class_eval(%(#{validation_method} %{errors.add_on_empty('#{attr_name}', "#{error_message}")}))
end
end
# Works like validate_presence, but only performs the validation on creation (for new records).
def validate_presence_on_create(*attr_names)
validate_presence(*attr_names) { "validate_on_create" }
end
# Works like validate_presence, but only performs the validation on update (for existing records).
def validate_presence_on_update(*attr_names)
validate_presence(*attr_names) { "validate_on_update" }
end
end
# The validation process on save can be skipped by passing false. The regular Base#save method is

View File

@ -7,6 +7,11 @@ require 'fixtures/developer'
class ValidationsTest < Test::Unit::TestCase
fixtures :topics, :developers
def teardown
Topic.write_inheritable_attribute("validate", [])
Topic.write_inheritable_attribute("validate_on_create", [])
end
def test_single_field_validation
r = Reply.new
r.title = "There's no content!"
@ -129,12 +134,10 @@ class ValidationsTest < Test::Unit::TestCase
t.title_confirmation = "We should be confirmed"
assert t.save
Topic.write_inheritable_attribute("validate_on_create", [])
end
def test_terms_of_service_agreement
Topic.validate_acceptance(:terms_of_service)
Topic.validate_acceptance_on_create(:terms_of_service)
t = Topic.create("title" => "We should be confirmed")
assert !t.save
@ -142,13 +145,11 @@ class ValidationsTest < Test::Unit::TestCase
t.terms_of_service = "1"
assert t.save
Topic.write_inheritable_attribute("validate_on_create", [])
end
def test_eula
Topic.validate_acceptance(:eula, "must be abided")
Topic.validate_acceptance_on_create(:eula, "must be abided")
t = Topic.create("title" => "We should be confirmed")
assert !t.save
@ -156,7 +157,19 @@ class ValidationsTest < Test::Unit::TestCase
t.eula = "1"
assert t.save
end
def test_validate_presences
Topic.validate_presence(:title, :content)
Topic.write_inheritable_attribute("validate_on_create", [])
t = Topic.create
assert !t.save
assert_equal "can't be empty", t.errors.on(:title)
assert_equal "can't be empty", t.errors.on(:content)
t.title = "something"
t.content = "another"
assert t.save
end
end