Agile Web Development with Rails, Edition 5

14.2 Iteration I2: Authenticating Users 13.3 Playtime

14.1 Iteration I1: Adding Users

Ticket 23989 : Delivering mail causes tests to fail

Scaffold the user model

rails generate scaffold User name:string password:digest
      invoke  active_record
      create    db/migrate/20160307144312_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       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/controllers/users_controller_test.rb
      invoke    helper
      create      app/helpers/users_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.scss

uncomment out bcrypt

edit Gemfile
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'

Restart the server.

Run the migration

rails db:migrate
mv 20160307144312_create_users.rb 20160307000009_create_users.rb
== 20160307000009 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0025s
== 20160307000009 CreateUsers: migrated (0.0025s) =============================
 

Add validation, has_secure_password

edit app/models/user.rb
class User < ApplicationRecord
  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(user_params)
 
    respond_to do |format|
      if @user.save
        format.html { redirect_to users_url,
          notice: "User #{@user.name} was successfully created." }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
edit app/controllers/users_controller.rb
  def update
    respond_to do |format|
      if @user.update(user_params)
        format.html { redirect_to users_url,
          notice: "User #{@user.name} was successfully updated." }
        format.json { render :show, status: :ok, location: @user }
      else
        format.html { render :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)
  end

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 class="field">
    <%= f.label :name, 'Name:' %>
    <%= f.text_field :name, size: 40 %>
  </div>
 
  <div class="field">
    <%= f.label :password, 'Password:' %>
    <%= f.password_field :password, size: 40 %>
  </div>
 
  <div class="field">
    <%= f.label :password_confirmation, 'Confirm:' %>
    <%= f.password_field :password_confirmation, size: 40 %>
  </div>
 
  <div class="actions">
    <%= f.submit %>
  </div>
 
  </fieldset>
<% end %>
 
</div>

Demonstrate creating a new user

get /users

Users

Name

New User
get /users/new

New User

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

User dave was successfully created.

Users

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$nhwEmnPMXJoIjYTCkcI8B.hWj8h4hvSg0w6GXG374VGgrPXzKN0rC
     created_at = 2016-03-07 14:43:16.815922
     updated_at = 2016-03-07 14:43:16.815922

Update tests to reflect the changes in redirection and uniqueness

edit test/controllers/users_controller_test.rb
  test "should create user" do
    assert_difference('User.count') do
      post users_url, params: { user: { name: 'sam', password: 'secret',
        password_confirmation: 'secret' } }
    end
 
    assert_redirected_to users_path
  end
edit test/controllers/users_controller_test.rb
  test "should create user" do
    assert_difference('User.count') do
      post users_url, params: { user: { name: 'sam', password: 'secret',
        password_confirmation: 'secret' } }
    end
 
    assert_redirected_to users_path
  end

Make sure that all test names are unique

edit test/fixtures/users.yml
# Read about fixtures at
# http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
 
one:
  name: dave
  password_digest: <%= BCrypt::Password.create('secret') %>
 
two:
  name: susannah
  password_digest: <%= BCrypt::Password.create('secret') %>
rails test
Run options: --seed 60839
 
# Running:
 
.....................F
 
Failure:
OrdersControllerTest#test_should_destroy_order [/home/rubys/git/awdwr/edition4/work-223/depot/test/controllers/orders_controller_test.rb:60]:
"Order.count" didn't change by -1.
Expected: 1
  Actual: 2
 
bin/rails test test/controllers/orders_controller_test.rb:59
 
.F
 
Failure:
ProductsControllerTest#test_should_create_product [/home/rubys/git/awdwr/edition4/work-223/depot/test/controllers/products_controller_test.rb:29]:
"Product.count" didn't change by 1.
Expected: 4
  Actual: 3
 
bin/rails test test/controllers/products_controller_test.rb:28
 
....F
 
Failure:
ProductsControllerTest#test_should_destroy_product [/home/rubys/git/awdwr/edition4/work-223/depot/test/controllers/products_controller_test.rb:64]:
"Product.count" didn't change by -1.
Expected: 2
  Actual: 3
 
bin/rails test test/controllers/products_controller_test.rb:63
 
.....F
 
Failure:
UsersControllerTest#test_should_destroy_user [/home/rubys/git/awdwr/edition4/work-223/depot/test/controllers/users_controller_test.rb:54]:
"User.count" didn't change by -1.
Expected: 1
  Actual: 2
 
bin/rails test test/controllers/users_controller_test.rb:53
 
..F
 
Failure:
UsersControllerTest#test_should_create_user [/home/rubys/git/awdwr/edition4/work-223/depot/test/controllers/users_controller_test.rb:20]:
"User.count" didn't change by 1.
Expected: 3
  Actual: 2
 
bin/rails test test/controllers/users_controller_test.rb:19
 
..E
 
Error:
DslUserStoriesTest#test_two_people_buying:
ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked: INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?)
    app/controllers/concerns/current_cart.rb:9:in `rescue in set_cart'
    app/controllers/concerns/current_cart.rb:7:in `set_cart'
    test/integration/dsl_user_stories_test.rb:78:in `buys_a'
    test/integration/dsl_user_stories_test.rb:58:in `block in test_two_people_buying'
    test/integration/dsl_user_stories_test.rb:54:in `test_two_people_buying'
 
bin/rails test test/integration/dsl_user_stories_test.rb:53
 
E
 
Error:
DslUserStoriesTest#test_buying_a_product:
ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked: INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?)
    app/controllers/concerns/current_cart.rb:9:in `rescue in set_cart'
    app/controllers/concerns/current_cart.rb:7:in `set_cart'
    test/integration/dsl_user_stories_test.rb:78:in `buys_a'
    test/integration/dsl_user_stories_test.rb:43:in `block in test_buying_a_product'
    test/integration/dsl_user_stories_test.rb:39:in `test_buying_a_product'
 
bin/rails test test/integration/dsl_user_stories_test.rb:38
 
E
 
Error:
UserStoriesTest#test_buying_a_product:
ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked: INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?)
    app/controllers/concerns/current_cart.rb:9:in `rescue in set_cart'
    app/controllers/concerns/current_cart.rb:7:in `set_cart'
    test/integration/user_stories_test.rb:26:in `block in <class:UserStoriesTest>'
 
bin/rails test test/integration/user_stories_test.rb:12
 
.........
 
Finished in 16.541033s, 3.1437 runs/s, 6.4083 assertions/s.
 
52 runs, 106 assertions, 5 failures, 3 errors, 0 skips

14.2 Iteration I2: Authenticating Users 13.3 Playtime