Agile Web Development with Rails, Edition 4

Agile Web Development with Rails, Edition 4

14.3 Iteration I3: Limiting Access 14.1 Iteration I1: Adding Users

14.2 Iteration I2: 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/destroy"
       route  get "sessions/create"
       route  get "sessions/new"
      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/functional/sessions_controller_test.rb
      invoke  helper
      create    app/helpers/sessions_helper.rb
      invoke    test_unit
      create      test/unit/helpers/sessions_helper_test.rb
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/functional/admin_controller_test.rb
      invoke  helper
      create    app/helpers/admin_helper.rb
      invoke    test_unit
      create      test/unit/helpers/admin_helper_test.rb

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.authenticate(params[:name], params[:password])
    if user 
      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_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
<div class="depot_form">
  <% if flash[:alert] %>
    <p id="notice"><%= flash[:alert] %></p>
  <% end %>
 
  <%= form_tag do %>
    <fieldset>
      <legend>Please Log In</legend>
 
      <div>
        <%= label_tag :name, 'Name:' %>
        <%= text_field_tag :name, params[:name] %>
      </div>
 
      <div>
        <%= label_tag :password, 'Password:' %>
        <%= password_field_tag :password, params[:password] %>
      </div>
  
      <div>
        <%= submit_tag "Login" %>
      </div>
    </fieldset>
  <% end %>
</div>

Create a landing page for the administrator

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

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
Depot::Application.routes.draw do
  get 'admin' => 'admin#index'
 
  controller :sessions do
    get  'login' => :new
    post 'login' => :create
    delete 'logout' => :destroy
  end
 
  resources :users
 
  resources :orders
 
  resources :line_items
 
  resources :carts
 
  get "store/index"
 
  resources :products do
    get :who_bought, :on => :member
  end
 
  # ...
 
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  # root :to => "welcome#index"
  root :to => 'store#index', :as => 'store'
  # ...
end

Do a login

get /login
Please Log In
post /login
You are being redirected.
get http://localhost:3000/admin

Welcome

It's Fri Jun 29 12:34:47 -0400 2012 We have 101 orders.

Fix the sessions controller test

edit test/functional/sessions_controller_test.rb
require 'test_helper'
 
class SessionsControllerTest < ActionController::TestCase
  test "should get new" do
    get :new
    assert_response :success
  end
 
  test "should login" do
    dave = users(:one)
    post :create, :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 :create, :name => dave.name, :password => 'wrong'
    assert_redirected_to login_url
  end
 
  test "should logout" do
    delete :destroy
    assert_redirected_to store_url
  end
 
end
edit test/fixtures/users.yml
<% SALT = "NaCl" unless defined?(SALT) %>
 
one:
  name: dave
  hashed_password: <%= User.encrypt_password('secret', SALT) %>
  salt: <%= SALT %>
 
two:
  name: MyString
  hashed_password: MyString
  salt: MyString
rake test
Loaded suite /home/rubys/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started
..........
Finished in 0.476053 seconds.
 
10 tests, 31 assertions, 0 failures, 0 errors
Loaded suite /home/rubys/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started
...............................................
Finished in 1.721401 seconds.
 
47 tests, 79 assertions, 0 failures, 0 errors
Loaded suite /home/rubys/.rvm/gems/ruby-1.8.7-p352/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started
...
Finished in 1.209654 seconds.
 
3 tests, 75 assertions, 0 failures, 0 errors

14.3 Iteration I3: Limiting Access 14.1 Iteration I1: Adding Users