From a65f791fbd8cf16d17a963f32c7b85a5659c04e0 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 9 Dec 2004 13:37:11 +0000 Subject: [PATCH] Added Base.validate_confirmation that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@95 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- activerecord/CHANGELOG | 17 ++++++++++ activerecord/lib/active_record/validations.rb | 31 +++++++++++++++++++ activerecord/test/validations_test.rb | 26 +++++++++++++--- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 16ec1a1e14267..7c12ad482e273 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,22 @@ *CVS* +* Added Base.validate_confirmation that encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: + + Model: + class Person < ActiveRecord::Base + validate_confirmation :password + end + + View: + <%= password_field "person", "password" %> + <%= 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. + * Added validation macros to make the stackable just like the lifecycle callbacks. Examples: class Person < ActiveRecord::Base diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 14e79cf68bb2b..25c43386fd40c 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -51,6 +51,37 @@ def self.append_features(base) # :nodoc: VALIDATIONS.each { |vd| base.class_eval("def self.#{vd}(*methods) write_inheritable_array(\"#{vd}\", methods - (read_inheritable_attribute(\"#{vd}\") || [])) end") } end + + base.extend(ClassMethods) + end + + module ClassMethods + # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: + # + # Model: + # class Person < ActiveRecord::Base + # validate_confirmation :password + # end + # + # View: + # <%= password_field "person", "password" %> + # <%= 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. + def validate_confirmation(*attr_names) + for attr_name in attr_names + attr_accessor "#{attr_name}_confirmation" + class_eval <<-EOC + validate_on_create(Proc.new { |record| + record.errors.add("#{attr_name}", "doesn't match confirmation") unless record.#{attr_name} == record.#{attr_name}_confirmation + }) +EOC + end + end end # The validation process on save can be skipped by passing false. The regular Base#save method is diff --git a/activerecord/test/validations_test.rb b/activerecord/test/validations_test.rb index 27a9b21c7d8f0..a6cdc0a706056 100755 --- a/activerecord/test/validations_test.rb +++ b/activerecord/test/validations_test.rb @@ -5,10 +5,7 @@ class ValidationsTest < Test::Unit::TestCase - def setup - @topic_fixtures = create_fixtures("topics") - @developers = create_fixtures("developers") - end + fixtures :topics, :developers def test_single_field_validation r = Reply.new @@ -124,3 +121,24 @@ def test_errors_on_boundary_breaking assert developer.save end end + + +class MacroValidationsTest < Test::Unit::TestCase + fixtures :topics, :developers + + def setup + Topic.validate_confirmation(:title) + end + + def teardown + Topic.write_inheritable_attribute("validate_on_create", []) + end + + def test_title_confirmation + t = Topic.create("title" => "We should be confirmed") + assert !t.save + + t.title_confirmation = "We should be confirmed" + assert t.save + end +end \ No newline at end of file