Permalink
Browse files

User model with passwords, and user show page

  • Loading branch information...
sbob-sfdc committed Jan 15, 2012
1 parent 054b179 commit cf654832dc9eda329aa0130db737eb61838ce81a
View
@@ -6,8 +6,15 @@ gem 'ruby-debug19', :require => 'ruby-debug'
gem 'sqlite3-ruby', :require => 'sqlite3'
+gem 'gravatar_image_tag'
+
group :development do
gem 'rspec-rails'
+ # gem 'annotate-models' # generates errors
+end
+
+group :test do
+ gem 'factory_girl_rails'
end
group :development, :test do
View
@@ -35,6 +35,12 @@ GEM
diff-lcs (1.1.3)
erubis (2.6.6)
abstract (>= 1.0.0)
+ factory_girl (2.3.2)
+ activesupport
+ factory_girl_rails (1.4.0)
+ factory_girl (~> 2.3.0)
+ railties (>= 3.0.0)
+ gravatar_image_tag (1.0.0)
i18n (0.5.0)
linecache19 (0.5.12)
ruby_core_source (>= 0.1.4)
@@ -105,6 +111,8 @@ PLATFORMS
ruby
DEPENDENCIES
+ factory_girl_rails
+ gravatar_image_tag
rails (= 3.0.6)
rspec
rspec-rails
@@ -5,6 +5,7 @@ def new
def show
@user = User.find(params[:id])
+ @title = @user.name
end
end
@@ -1,2 +1,8 @@
module UsersHelper
+ def gravatar_for(user, options = {:size => 50})
+ gravatar_image_tag( user.email.downcase,
+ :class => "gravatar",
+ :alt => user.name,
+ :gravatar => options)
+ end
end
View
@@ -1,12 +1,48 @@
class User < ActiveRecord::Base
- attr_accessible :name, :email
+ attr_accessor :password
+ attr_accessible :name, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
- validates :name, :presence => true,
- :length => {:maximum => 50}
- validates :email, :presence => true,
- :format => {:with => email_regex},
- :uniqueness => { :case_sensitive => false }
+ validates :name, :presence => true,
+ :length => {:maximum => 50}
+ validates :email, :presence => true,
+ :format => {:with => email_regex},
+ :uniqueness => { :case_sensitive => false }
+
+ validates :password, :presence => true,
+ :confirmation => true,
+ :length => {:within => 6..40}
+
+ before_save :encrypt_password
+
+ def has_password?(submitted_password)
+ encrypted_password == encrypt(submitted_password)
+ end
+
+ def self.authenticate(email, submitted_password)
+ user = find_by_email(email)
+ return nil if user.nil?
+ return user if user.has_password?(submitted_password)
+ end
+
+ private
+
+ def encrypt_password
+ self.salt = make_salt if new_record?
+ self.encrypted_password = encrypt(password)
+ end
+
+ def encrypt(string)
+ secure_hash("#{salt}--#{string}")
+ end
+
+ def make_salt
+ secure_hash("#{Time.now.utc}--#{password}")
+ end
+
+ def secure_hash(string)
+ Digest::SHA2.hexdigest(string)
+ end
end
@@ -1 +1,13 @@
-<%= @user.name %>, <%= @user.email %>
+<table>
+ <tr>
+ <td class="main">
+ <h1>
+ <%= gravatar_for @user %> <%= @user.name %>
+ </h1>
+ </td>
+ <td class="sidebar round">
+ <strong>Name</strong> <%= @user.name %><br />
+ <strong>URL</strong> <%= link_to user_path(@user), @user %>
+ </td>
+ </tr>
+</table>
@@ -0,0 +1,9 @@
+class AddPasswordToUsers < ActiveRecord::Migration
+ def self.up
+ add_column :users, :encrypted_password, :string
+ end
+
+ def self.down
+ remove_column :users, :encrypted_password
+ end
+end
@@ -0,0 +1,9 @@
+class AddSaltToUsers < ActiveRecord::Migration
+ def self.up
+ add_column :users, :salt, :string
+ end
+
+ def self.down
+ remove_column :users, :salt
+ end
+end
View
@@ -10,13 +10,15 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20111229054534) do
+ActiveRecord::Schema.define(:version => 20120102022806) do
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
+ t.string "encrypted_password"
+ t.string "salt"
end
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
@@ -107,4 +107,28 @@ footer {
footer nav {
float: none;
+}
+
+/* User show page */
+
+table.profile {
+ width: 100%;
+ margin-bottom: 0;
+}
+
+td.main {
+ width: 70%;
+ padding: 1em;
+}
+
+td.sidebar {
+ width: 30%;
+ padding: 1em;
+ vertical-align: top;
+ background: #ffc;
+}
+
+.profile img.gravatar {
+ border: 1px solid #999;
+ margin-bottom: -15px;
}
@@ -14,5 +14,37 @@
response.should have_selector("title", :content => "Sign up")
end
end
+
+ describe "GET 'show'" do
+ before(:each) do
+ @user = Factory(:user)
+ end
+
+ it "should be successful" do
+ get :show, :id => @user
+ response.should be_success
+ end
+
+ it "should find the right user" do
+ get :show, :id => @user
+ assigns(:user) == @user
+ end
+
+ it "should have the right title" do
+ get :show, :id => @user
+ response.should have_selector("title", :content => @user.name)
+ end
+
+ it "should have the user's name" do
+ get :show, :id => @user
+ response.should have_selector("h1", :content => @user.name)
+ end
+
+ it "should have the user's profile image" do
+ get :show, :id => @user
+ response.should have_selector("h1>img", :class => "gravatar")
+ end
+
+ end
end
View
@@ -0,0 +1,6 @@
+Factory.define :user do |user|
+ user.name "Michael Hartl"
+ user.email "mhartl@example.com"
+ user.password "foobar"
+ user.password_confirmation "foobar"
+end
View
@@ -2,7 +2,10 @@
describe User do
before(:each) do
- @attr = {:name => "Example Name", :email => "name@example.com"}
+ @attr = { :name => "Example Name",
+ :email => "name@example.com",
+ :password => "foobar",
+ :password_confirmation => "foobar"}
end
it "should create a new instance given valid attributes" do
@@ -54,5 +57,73 @@
user_with_duplicate_email.should_not be_valid
end
+ describe "password validations" do
+
+ it "should require a password" do
+ User.new(@attr.merge(:password => "", :password_confirmation => "")).
+ should_not be_valid
+ end
+
+ it "should require a matching password confirmation" do
+ User.new(@attr.merge(:password_confirmation => "invalid")).
+ should_not be_valid
+ end
+
+ it "should reject a short password" do
+ short = "a" * 5
+ hash = @attr.merge(:password => short, :password_confirmation => short)
+ User.new(hash).should_not be_valid
+ end
+
+ it "should reject a long password" do
+ long = "a" * 41
+ hash = @attr.merge(:password => long, :password_confirmation => long)
+ User.new(hash).should_not be_valid
+ end
+ end
+
+ describe "password encryption" do
+
+ before(:each) do
+ @user = User.create!(@attr)
+ end
+
+ it "should have an encrypted password attribute" do
+ @user.should respond_to(:encrypted_password)
+ end
+
+ it "should set the encrypted password" do
+ @user.encrypted_password.should_not be_blank
+ end
+
+ describe "has_password? method" do
+
+ it "should be true if the passwords match" do
+ @user.has_password?(@attr[:password]).should be_true
+ end
+
+ it "should be false if the passwords don't match" do
+ @user.has_password?("invalid").should be_false
+ end
+ end
+
+ describe "authenticate method" do
+
+ it "should return nil on email/password mismatch" do
+ wrong_password_user = User.authenticate(@attr[:email], "wrongpass")
+ wrong_password_user.should be_nil
+ end
+
+ it "should return nil for an email address with no user" do
+ nonexistent_user = User.authenticate("bar@foo.com", @attr[:password])
+ nonexistent_user.should be_nil
+ end
+
+ it "should return the user on email/password match" do
+ matching_user = User.authenticate(@attr[:email], @attr[:password])
+ matching_user.should == @user
+ end
+ end
+ end
end

0 comments on commit cf65483

Please sign in to comment.