Skip to content

Commit 62bd420

Browse files
committed
Make a User model with secure passwords
1 parent 5a0ea51 commit 62bd420

9 files changed

+162
-1
lines changed

Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ gem 'bcrypt-ruby', '3.1.2'
88
gem 'faker', '1.1.2'
99
gem 'will_paginate', '3.0.4'
1010
gem 'bootstrap-will_paginate', '0.0.9'
11+
gem 'hirb'
1112

1213
group :development, :test do
1314
gem 'sqlite3', '1.3.8'

Gemfile.lock

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ GEM
7979
gherkin (2.12.2)
8080
multi_json (~> 1.3)
8181
hike (1.2.3)
82+
hirb (0.7.1)
8283
i18n (0.6.9)
8384
jbuilder (1.0.2)
8485
activesupport (>= 3.0.0)
@@ -189,6 +190,7 @@ DEPENDENCIES
189190
database_cleaner!
190191
factory_girl_rails (= 4.2.0)
191192
faker (= 1.1.2)
193+
hirb
192194
jbuilder (= 1.0.2)
193195
jquery-rails (= 3.0.4)
194196
pg (= 0.15.1)

app/models/user.rb

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class User < ActiveRecord::Base
2+
before_save { self.email = email.downcase }
3+
validates :name, presence: true, length: { maximum: 50 }
4+
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
5+
validates :email, presence: true,
6+
format: { with: VALID_EMAIL_REGEX },
7+
uniqueness: { case_sensitive: false }
8+
has_secure_password
9+
validates :password, length: { minimum: 6 }
10+
end

config/application.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
module SampleApp
1515
class Application < Rails::Application
1616
config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif)
17-
17+
I18n.enforce_available_locales = true
1818
# Settings in config/environments/* take precedence over those specified here.
1919
# Application configuration should go into files in config/initializers
2020
# -- all .rb files in that directory are automatically loaded.
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class CreateUsers < ActiveRecord::Migration
2+
def change
3+
create_table :users do |t|
4+
t.string :name
5+
t.string :email
6+
7+
t.timestamps
8+
end
9+
end
10+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddIndexToUsersEmail < ActiveRecord::Migration
2+
def change
3+
add_index :users, :email, unique: true
4+
end
5+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddPasswordDigestToUsers < ActiveRecord::Migration
2+
def change
3+
add_column :users, :password_digest, :string
4+
end
5+
end

db/schema.rb

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# encoding: UTF-8
2+
# This file is auto-generated from the current state of the database. Instead
3+
# of editing this file, please use the migrations feature of Active Record to
4+
# incrementally modify your database, and then regenerate this schema definition.
5+
#
6+
# Note that this schema.rb definition is the authoritative source for your
7+
# database schema. If you need to create the application database on another
8+
# system, you should be using db:schema:load, not running all the migrations
9+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
10+
# you'll amass, the slower it'll run and the greater likelihood for issues).
11+
#
12+
# It's strongly recommended that you check this file into your version control system.
13+
14+
ActiveRecord::Schema.define(version: 20140311183803) do
15+
16+
create_table "users", force: true do |t|
17+
t.string "name"
18+
t.string "email"
19+
t.datetime "created_at"
20+
t.datetime "updated_at"
21+
t.string "password_digest"
22+
end
23+
24+
add_index "users", ["email"], name: "index_users_on_email", unique: true
25+
26+
end

spec/models/user_spec.rb

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
require 'spec_helper'
2+
3+
describe User do
4+
5+
before do
6+
@user = User.new(name: "Example User", email: "[email protected]",
7+
password: "foobar", password_confirmation: "foobar")
8+
end
9+
10+
subject { @user }
11+
12+
it { should respond_to(:name) }
13+
it { should respond_to(:email) }
14+
it { should respond_to(:password_digest) }
15+
it { should respond_to(:password) }
16+
it { should respond_to(:password_confirmation) }
17+
it { should respond_to(:authenticate) }
18+
19+
20+
it { should be_valid }
21+
describe "when name is not present" do
22+
before { @user.name = " " }
23+
it { should_not be_valid }
24+
end
25+
26+
describe "when email is not present" do
27+
before { @user.email = " " }
28+
it { should_not be_valid }
29+
end
30+
31+
describe "when name is too long" do
32+
before { @user.name = "a" * 51 }
33+
it { should_not be_valid }
34+
end
35+
36+
describe "when email format is invalid" do
37+
it "should be invalid" do
38+
addresses = %w[user@foo,com user_at_foo.org example.user@foo.
39+
foo@bar_baz.com foo@bar+baz.com]
40+
addresses.each do |invalid_address|
41+
@user.email = invalid_address
42+
expect(@user).not_to be_valid
43+
end
44+
end
45+
end
46+
47+
describe "when email format is valid" do
48+
it "should be valid" do
49+
50+
addresses.each do |valid_address|
51+
@user.email = valid_address
52+
expect(@user).to be_valid
53+
end
54+
end
55+
end
56+
57+
describe "when email address is already taken" do
58+
before do
59+
user_with_same_email = @user.dup
60+
user_with_same_email.email = @user.email.upcase
61+
user_with_same_email.save
62+
end
63+
64+
it { should_not be_valid }
65+
end
66+
67+
describe "when password is not present" do
68+
before do
69+
@user = User.new(name: "Example User", email: "[email protected]",
70+
password: " ", password_confirmation: " ")
71+
end
72+
it { should_not be_valid }
73+
end
74+
75+
describe "when password doesn't match confirmation" do
76+
before { @user.password_confirmation = "mismatch" }
77+
it { should_not be_valid }
78+
end
79+
80+
describe "with a password that's too short" do
81+
before { @user.password = @user.password_confirmation = "a" * 5 }
82+
it { should be_invalid }
83+
end
84+
85+
describe "return value of authenticate method" do
86+
before { @user.save }
87+
let(:found_user) { User.find_by(email: @user.email) }
88+
89+
describe "with valid password" do
90+
it { should eq found_user.authenticate(@user.password) }
91+
end
92+
93+
describe "with invalid password" do
94+
let(:user_for_invalid_password) { found_user.authenticate("invalid") }
95+
96+
it { should_not eq user_for_invalid_password }
97+
specify { expect(user_for_invalid_password).to be_false }
98+
end
99+
end
100+
101+
102+
end

0 commit comments

Comments
 (0)