* I created a branch named basic-login.
$ git checkout -b basic-login
* I generated a Sessions controller with a new action.
$ rails generate controller Sessions new
* I added the paths for Sessions controller in routes.rb
sample_app/config/routes.rb
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
* I updated the login_path for Sessions controller testing file.
sample_app/test/controllers/sessions_controller_test.rb
* I copied the codes for the login page from the book.
sample_app/app/views/sessions/new.html.erb
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
* I copied the new, create, and destroy methods to Sessions controller.
sample_app/app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out
redirect_to root_url
end
end
* I generated an integration test for Users login for the testing of incorrect flash (incorrect prompt) behavior.
$ rails generate integration_test users_login
* I copied the codes for Users login integration test.
sample_app/test/integration/users_login_test.rb
test "login with invalid information" do
get login_path
assert_template 'sessions/new'
post login_path, params: { session: { email: "", password: "" } }
assert_template 'sessions/new'
assert_not flash.empty?
get root_path
assert flash.empty?
end
* Then, I ran the test in the shell command window.
$ rails test test/integration/users_login_test.rb
* I added a line include SessionsHelper in the Application controller.
sample_app/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
* I copied the codes in Sessions helper.
sample_app/app/helpers/sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Returns the current logged-in user (if any).
def current_user
@current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Logs out the current user.
def log_out
session.delete(:user_id)
@current_user = nil
end
end
* I updated the _header.html.erb partial file to add new functions to display the User profile and the Logout button.
sample_app/app/views/layouts/_header.html.erb
<% if logged_in? %>
<li><%= link_to "Users", '#' %></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", '#' %></li>
<li class="divider"></li>
<li>
<%= link_to "Log out", logout_path, method: :delete %>
</li>
</ul>
</li>
<% else %>
<li><%= link_to "Log in", login_path %></li>
<% end %>
* I added the Bootstrap JavaScript library to application.js
sample_app/app/assets/javascripts/application.js
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require turbolinks
//= require_tree .
* I added a digest method for use in fixtures
sample_app/app/models/user.rb
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
* I created a fixture for testing user login.
sample_app/test/fixtures/users.yml
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
* I added a line to login a new user upon sign up.
sample_app/app/controllers/users_controller.rb
def create
@user = User.new(user_params)
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
redirect_to @user
else
render 'new'
end
end
* I added a method in test helper to test if a user is logged in.
sample_app/test/test_helper.rb
# Returns true if a test user is logged in.
def is_logged_in?
!session[:user_id].nil?
end
* I added the log_out method in the Sessions helper.
sample_app/app/helpers/sessions_helper.rb
# Logs out the current user.
def log_out
session.delete(:user_id)
@current_user = nil
end
* I copied the codes from the book for the destroy method in Sessions controller.
sample_app/app/controllers/sessions_controller.rb
def destroy
log_out
redirect_to root_url
end
* I copied the codes to test for user logout.
sample_app/test/integration/users_login_test.rb
test "login with valid information followed by logout" do
get login_path
post login_path, params: { session: { email: @user.email,
password: 'password' } }
assert is_logged_in?
assert_redirected_to @user
follow_redirect!
assert_template 'users/show'
assert_select "a[href=?]", login_path, count: 0
assert_select "a[href=?]", logout_path
assert_select "a[href=?]", user_path(@user)
delete logout_path
assert_not is_logged_in?
assert_redirected_to root_url
follow_redirect!
assert_select "a[href=?]", login_path
assert_select "a[href=?]", logout_path, count: 0
assert_select "a[href=?]", user_path(@user), count: 0
end
* To sum up the changes, I merged my changes back into the master branch.
$ rails test
$ git add -A
$ git commit -m "Implement basic login"
$ git checkout master
$ git merge basic-login
* Then, I pushed it onto Github.
$ rails test
$ git push
* The Github address for the sample_app project is:
https://github.com/jimmy2046/sample_app
Thank you.Well it was nice post and very helpful information on Ruby on Rails Online Training
ReplyDelete