[PATCH] An account must have a unique email address, password and display name.
by Darryl L. Pierce
Created the Account class.
Added unit tests to ensure that an email address is present,
well-formed, and unique.
Password must be present, and must be confirmed if changed.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
src/.gitignore | 3 +
src/app/models/account.rb | 71 ++++++++++++++++
src/db/migrate/20100613135618_create_accounts.rb | 43 ++++++++++
src/test/fixtures/accounts.yml | 9 ++
src/test/unit/account_test.rb | 96 ++++++++++++++++++++++
5 files changed, 222 insertions(+), 0 deletions(-)
create mode 100644 src/.gitignore
create mode 100644 src/app/models/account.rb
create mode 100644 src/db/migrate/20100613135618_create_accounts.rb
delete mode 100644 src/log/development.log
delete mode 100644 src/log/production.log
delete mode 100644 src/log/server.log
delete mode 100644 src/log/test.log
create mode 100644 src/test/fixtures/accounts.yml
create mode 100644 src/test/unit/account_test.rb
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..f7d7430
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,3 @@
+db/*sqlite3
+db/schema.rb
+log/*log
diff --git a/src/app/models/account.rb b/src/app/models/account.rb
new file mode 100644
index 0000000..5752320
--- /dev/null
+++ b/src/app/models/account.rb
@@ -0,0 +1,71 @@
+# account.rb
+# Copyright (C) 2010, Darryl L. Pierce.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see <http://www.gnu.org/licenses>.
+
+require 'digest/sha1'
+
+# Account represents a single user in the system.
+#
+# An account has a login name and password hash, used for authenticating
+# the user with the system.
+#
+# An account has a display name which is used to identify the user wherever
+# needed.
+class Account < ActiveRecord::Base
+ attr_protected :id, :password_salt
+
+ validates_presence_of :email
+ validates_uniqueness_of :email
+ validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
+
+ attr_accessor :password_confirmation
+ validates_presence_of :password_confirmation if :password_changed?
+ validates_confirmation_of :password, :message => "Passwords do not match."
+
+ def self.random_string(len)
+ #generate a random password consisting of strings and digits
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ newpass = ""
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
+ return newpass
+ end
+
+ def password=(pass)
+ @password = pass
+
+ self.password_salt = Account.random_string(10) if !self.password_salt
+ self.password_hash = Account.encrypt_password(pass, self.password_salt)
+ end
+
+ def password # virtual field
+ @password
+ end
+
+ private
+
+ def self.random_string(len)
+ #generate a random password consisting of strings and digits
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
+ newpass = ""
+ 1.upto(len) { |i| newpass << chars[rand(chars.size-1)] }
+ return newpass
+ end
+
+ def self.encrypt_password(password, salt)
+ string_to_hash = password + "farkle" + salt
+ Digest::SHA1.hexdigest(string_to_hash)
+ end
+
+end
diff --git a/src/db/migrate/20100613135618_create_accounts.rb b/src/db/migrate/20100613135618_create_accounts.rb
new file mode 100644
index 0000000..ffad1db
--- /dev/null
+++ b/src/db/migrate/20100613135618_create_accounts.rb
@@ -0,0 +1,43 @@
+# 20100613135618_create_accounts.rb
+# Copyright (C) 2010, Darryl L. Pierce.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see <http://www.gnu.org/licenses>.
+
+class CreateAccounts < ActiveRecord::Migration
+ def self.up
+ create_table :accounts do |t|
+ t.string :email,
+ :unique => true,
+ :null => false,
+ :limit => 255
+
+ t.string :password_hash,
+ :null => false
+
+ t.string :password_salt,
+ :null => false
+
+ t.string :display_name,
+ :unique => false,
+ :null => false,
+ :limit => 50
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :accounts
+ end
+end
diff --git a/src/log/development.log b/src/log/development.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/log/production.log b/src/log/production.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/log/server.log b/src/log/server.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/log/test.log b/src/log/test.log
deleted file mode 100644
index e69de29..0000000
diff --git a/src/test/fixtures/accounts.yml b/src/test/fixtures/accounts.yml
new file mode 100644
index 0000000..1d66f0e
--- /dev/null
+++ b/src/test/fixtures/accounts.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+<% SALT = "NaCl" unless defined?(SALT) %>
+
+normal_user:
+ email: user(a)projxp.org
+ display_name: Normal User
+ password_hash: <%= Account.encrypt_password('normal', SALT) %>
+ password_salt: <%= SALT %>
\ No newline at end of file
diff --git a/src/test/unit/account_test.rb b/src/test/unit/account_test.rb
new file mode 100644
index 0000000..c8bbfd2
--- /dev/null
+++ b/src/test/unit/account_test.rb
@@ -0,0 +1,96 @@
+# account_test.rb
+# Copyright (C) 2010, Darryl L. Pierce.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, see <http://www.gnu.org/licenses>.
+
+require 'test_helper'
+
+class AccountTest < ActiveSupport::TestCase
+ def setup
+ @new_account = Account.new(:email => 'mcpierce(a)gmail.com',
+ :display_name => 'Darryl L. Pierce')
+ end
+
+ # ensures that an email address is required by an account
+ test "test_email_is_required" do
+ @new_account.email = nil
+
+ assert(! @new_account.valid?, "An email address must be required.")
+ end
+
+ # ensures that an email address must be well-formed.
+ test "test_email_must_be_well_formed" do
+ @new_account.email = "foo"
+
+ assert(! @new_account.valid?, "An email address must be well-formed.")
+ end
+
+ # ensures that an email address is unique.
+ test "test_email_must_be_unique" do
+ @new_account.email = accounts(:normal_user).email
+
+ assert(! @new_account.valid?, "An email address must be unique.")
+ end
+
+ # ensures that a password must be confirmed.
+ test "test_password_is_confirmed" do
+ @new_account.password = 'test'
+ @new_account.password_confirmation = 'tset'
+
+ assert(! @new_account.valid?, "The password must be confirmed.")
+ end
+
+ # ensures that a password is required.
+ test "test_password_is_required" do
+ @existing_account = accounts(:normal_user)
+ @existing_account.password = ""
+
+ assert(! @existing_account.valid?, "The password cannot be blank.")
+ end
+
+ # ensures that passwords are changed only if the confirmation works
+ test "test_password_change_requires_confirmation" do
+ @existing_account = accounts(:normal_user)
+ @existing_account.password = "abc123"
+
+ assert(! @existing_account.valid?,
+ "The password cannot be changed unconfirmed.")
+ end
+
+ # ensures that passwords are changed when confirmed.
+ test "test_password_change_works" do
+ @existing_account = accounts(:normal_user)
+ @existing_account.password = "abc123"
+ @existing_account.password_confirmation = "abc123"
+
+ assert((a)existing_account.valid?,
+ "Password is confirmed, so this is good.")
+ end
+
+ # ensures that an account has to have a display name
+ test "test_new_account_requires_display_name" do
+ @new_account.display_name = nil
+
+ assert(! @new_account.valid?, "The display name is required.");
+ end
+
+ # ensures that an existing account has to have a display name
+ test "test_account_requires_display_name" do
+ @existing_account = accounts(:normal_user)
+ @existing_account.display_name = ''
+
+ assert(! @existing_account.valid?, "The display name is required.")
+ end
+
+end
--
1.7.0.1
13 years, 10 months