Agile Web Development with Rails, Edition 5

15.3 Iteration J3: Limiting Access 15.1 Iteration J1: Adding Users

15.2 Iteration J2: Authenticating Users

Generate empty controllers for sessions and administration

rails generate controller Sessions new create destroy
      create  app/controllers/sessions_controller.rb
       route  get 'sessions/new'
get 'sessions/create'
get 'sessions/destroy'
      invoke  erb
      create    app/views/sessions
      create    app/views/sessions/new.html.erb
      create    app/views/sessions/create.html.erb
      create    app/views/sessions/destroy.html.erb
      invoke  test_unit
      create    test/controllers/sessions_controller_test.rb
      invoke  helper
      create    app/helpers/sessions_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/sessions.coffee
      invoke    scss
      create      app/assets/stylesheets/sessions.scss
rails generate controller Admin index
      create  app/controllers/admin_controller.rb
       route  get 'admin/index'
      invoke  erb
      create    app/views/admin
      create    app/views/admin/index.html.erb
      invoke  test_unit
      create    test/controllers/admin_controller_test.rb
      invoke  helper
      create    app/helpers/admin_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/admin.coffee
      invoke    scss
      create      app/assets/stylesheets/admin.scss

Implement login in and out by storing the user_id in the session

edit app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def new
  end
 
  def create
    user = User.find_by(name: params[:name])
    if user.try(:authenticate, params[:password])
      session[:user_id] = user.id
      redirect_to admin_url
    else
      redirect_to login_url, alert: "Invalid user/password combination"
    end
  end
 
  def destroy
    session[:user_id] = nil
    redirect_to store_index_url, notice: "Logged out"
  end
end

Create the view using form_for as there is no underlying model

edit app/views/sessions/new.html.erb
<section class="depot_form">
  <% if flash[:alert] %>
    <aside class="notice"><%= flash[:alert] %></aside>
  <% end %>
 
  <%= form_tag do %>
    <h2>Please Log In</h2>
    <div class="field">
      <%= label_tag :name, 'Name:' %>
      <%= text_field_tag :name, params[:name] %>
    </div>
 
    <div class="field">
      <%= label_tag :password, 'Password:' %>
      <%= password_field_tag :password, params[:password] %>
    </div>
 
    <div class="actions">
      <%= submit_tag "Login" %>
    </div>
  <% end %>
</section>

Create a landing page for the administrator

edit app/views/admin/index.html.erb
<h1>Welcome</h1>
 
<p>
  It's <%= Time.now %>.
  We have <%= pluralize(@total_orders, "order") %>.
</p>

Make the orders count available to the admin page

edit app/controllers/admin_controller.rb
class AdminController < ApplicationController
  def index
    @total_orders = Order.count
  end
end

Connect the routes to the controller actions

edit config/routes.rb

Do a login

get /login
The Pragmatic Bookshelf

Please Log In

post /login
You are being redirected.
get http://localhost:3000/admin
The Pragmatic Bookshelf

Welcome

It's 2017-11-13 10:47:44 -0500. We have 3 orders.

edit test/controllers/admin_controller_test.rb
require 'test_helper'
 
class AdminControllerTest < ActionDispatch::IntegrationTest
  test "should get index" do
    get admin_url
    assert_response :success
  end
 
end

Fix the sessions controller test

edit test/controllers/sessions_controller_test.rb
require 'test_helper'
 
class SessionsControllerTest < ActionDispatch::IntegrationTest
  test "should prompt for login" do
    get login_url
    assert_response :success
  end
 
  test "should login" do
    dave = users(:one)
    post login_url, params: { name: dave.name, password: 'secret' }
    assert_redirected_to admin_url
    assert_equal dave.id, session[:user_id]
  end
 
  test "should fail login" do
    dave = users(:one)
    post login_url, params: { name: dave.name, password: 'wrong' }
    assert_redirected_to login_url
  end
 
  test "should logout" do
    delete logout_url
    assert_redirected_to store_index_url
  end
 
end
rails test
Run options: --seed 35289
 
# Running:
 
.....................................................
 
Finished in 1.792816s, 29.5624 runs/s, 58.0093 assertions/s.
53 runs, 104 assertions, 0 failures, 0 errors, 0 skips

15.3 Iteration J3: Limiting Access 15.1 Iteration J1: Adding Users