Agile Web Development with Rails, Edition 4

Agile Web Development with Rails, Edition 4

14.2 Iteration I2: Authenticating Users 13.3 Playtime

14.1 Iteration I1: Adding Users

Scaffold the user model

rails generate scaffold User name:string password_digest:string
      invoke  active_record
      create    db/migrate/20111029072029_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/unit/user_test.rb
      create      test/fixtures/users.yml
       route  resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/functional/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      create        test/unit/helpers/users_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.js.coffee
      invoke    scss
      create      app/assets/stylesheets/users.css.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.css.scss

uncomment out bcrypt-ruby

edit Gemfile
# source 'http://rubygems.org'
 
gem 'rails', :path => "/home/rubys/git/rails" # '3.2.0.beta'
gem "journey", :path => "/home/rubys/git/journey"
 
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
 
gem 'sqlite3'
 
 
# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   :path => "/home/rubys/git/sass-rails" # 'git://github.com/rails/sass-rails.git'
  gem 'coffee-rails', :path => "/home/rubys/git/coffee-rails" # 'git://github.com/rails/coffee-rails.git'
  gem 'uglifier', '>= 1.0.3'
end
 
gem 'jquery-rails'
 
# To use ActiveModel has_secure_password
gem 'bcrypt-ruby', '~> 3.0.0'
 
# Use unicorn as the web server
# gem 'unicorn'
 
# Deploy with Capistrano
# gem 'capistrano'
 
# To use debugger
# gem 'ruby-debug19', :require => 'ruby-debug'
gem 'will_paginate', '~> 3.0'

Restart the server.

Run the migration

rake db:migrate
mv 20111029072029_create_users.rb 20110711000009_create_users.rb
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0239s
==  CreateUsers: migrated (0.0240s) ===========================================
 

Add validation, has_secure_password

edit app/models/user.rb
class User < ActiveRecord::Base
  validates :name, presence: true, uniqueness: true
  has_secure_password
end

Avoid redirect after create, update operations

edit app/controllers/users_controller.rb
  def create
    @user = User.new(params[:user])
 
    respond_to do |format|
      if @user.save
        format.html { redirect_to users_url,
          notice: "User #{@user.name} was successfully created." }
        format.json { render json: @user,
          status: :created, location: @user }
      else
        format.html { render action: "new" }
        format.json { render json: @user.errors,
          status: :unprocessable_entity }
      end
    end
  end
edit app/controllers/users_controller.rb
  def update
    @user = User.find(params[:id])
 
    respond_to do |format|
      if @user.update_attributes(params[:user])
        format.html { redirect_to users_url,
          notice: "User #{@user.name} was successfully updated." }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @user.errors,
          status: :unprocessable_entity }
      end
    end
  end

Display users sorted by name

edit app/controllers/users_controller.rb
  def index
    @users = User.order(:name)
 
    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @users }
    end
  end

Add Notice, and remove password digest from view

edit app/views/users/index.html.erb
<h1>Listing users</h1>
  <% if notice %>
  <p id="notice"><%= notice %></p>
  <% end %>
 
<table>
  <tr>
    <th>Name</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
 
<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, confirm: 'Are you sure?',
       method: :delete %></td>
  </tr>
<% end %>
</table>
 
<br />
 
<%= link_to 'New User', new_user_path %>

Update form used to both create and update users

edit app/views/users/_form.html.erb
<div class="depot_form">
 
<%= form_for @user do |f| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %>
        prohibited this user from being saved:</h2>
      <ul>
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
 
  <fieldset>
  <legend>Enter User Details</legend>
 
  <div>
    <%= f.label :name %>:
    <%= f.text_field :name, size: 40 %>
  </div>
 
  <div>
    <%= f.label :password, 'Password' %>:
    <%= f.password_field :password, size: 40 %>
  </div>
 
  <div>
    <%= f.label :password_confirmation, 'Confirm' %>:
    <%= f.password_field :password_confirmation, size: 40 %>
  </div>
 
  <div>
    <%= f.submit %>
  </div>
 
  </fieldset>
<% end %>
 
</div>

Demonstrate creating a new user

get /users

Listing users

Name

New User
get /users/new

New user

Enter User Details
:
:
:
Back
post /users
You are being redirected.
get http://localhost:3000/users

Listing users

User dave was successfully created.

Name
dave Show Edit Destroy

New User

Show how this is stored in the database

sqlite3> select * from users
             id = 1
           name = dave
password_digest = $2a$10$nqouCRPPZbi/0y2RXJD2F.gaNuJ74Jdr2yYBL7KVDfnkRjvmLTIaa
     created_at = 2011-10-29 07:20:47.863311
     updated_at = 2011-10-29 07:20:47.863311
edit test/functional/users_controller_test.rb
require 'test_helper'
 
class UsersControllerTest < ActionController::TestCase
  setup do
    @input_attributes = {
      name:                  "sam",
      password:              "private",
      password_confirmation: "private"
    }
 
    @user = users(:one)
  end
  #...
  test "should create user" do
    assert_difference('User.count') do
      post :create, user: @input_attributes
    end
 
    assert_redirected_to users_path
  end
  #...
  test "should update user" do
    put :update, id: @user, user: @input_attributes
    assert_redirected_to users_path
  end
end

14.2 Iteration I2: Authenticating Users 13.3 Playtime