Saturday, May 27, 2017

Ch 8 Completed

* I have completed chapter 8: Basic login.


* 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

1 comment:

How to kill an abandoned process in Linux/Unix

I remembered it, then I forgot, then I remembered it, and then I forgot again. In case of a Linux/Unit process hang, I have to figure out ...