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
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
      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
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/sessions.js.coffee
      invoke    scss
      create      app/assets/stylesheets/sessions.css.scss
rails generate controller Admin index
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
      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
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/admin.js.coffee
      invoke    scss
      create      app/assets/stylesheets/admin.css.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 and user.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_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
 
  # The priority is based upon order of creation:
  # first created -> highest priority.
 
  # Sample of regular route:
  #   match 'products/:id' => 'catalog#view'
  # Keep in mind you can assign values other than :controller and :action
 
  # Sample of named route:
  #   match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
  # This route can be invoked with purchase_url(:id => product.id)
 
  # Sample resource route (maps HTTP verbs to controller actions automatically):
  #   resources :products
 
  # Sample resource route with options:
  #   resources :products do
  #     member do
  #       get 'short'
  #       post 'toggle'
  #     end
  #
  #     collection do
  #       get 'sold'
  #     end
  #   end
 
  # Sample resource route with sub-resources:
  #   resources :products do
  #     resources :comments, :sales
  #     resource :seller
  #   end
 
  # Sample resource route with more complex sub-resources
  #   resources :products do
  #     resources :comments
  #     resources :sales do
  #       get 'recent', :on => :collection
  #     end
  #   end
 
  # Sample resource route within a namespace:
  #   namespace :admin do
  #     # Directs /admin/products/* to Admin::ProductsController
  #     # (app/controllers/admin/products_controller.rb)
  #     resources :products
  #   end
 
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  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 2014-02-04 15:01:25 -0500 We have 1 order.

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
rake test
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.2-p320/gems/rake-10.1.1/lib/rake/rake_test_loader
Started
 
CartTest:
     PASS add duplicate product (0.40s) 
     PASS add unique products (0.01s) 
 
ProductTest:
     PASS image url (0.02s) 
     PASS product attributes must not be empty (0.00s) 
     PASS product is not valid without a unique title (0.00s) 
     PASS product is not valid without a unique title - i18n (0.00s) 
     PASS product price must be positive (0.00s) 
 
Finished in 0.441249 seconds.
 
7 tests, 28 assertions, 0 failures, 0 errors, 0 skips
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.2-p320/gems/rake-10.1.1/lib/rake/rake_test_loader
Started
 
AdminControllerTest:
     PASS should get index (0.34s) 
 
CartsControllerTest:
     PASS should create cart (0.01s) 
     PASS should destroy cart (0.08s) 
     PASS should get edit (0.05s) 
     PASS should get index (0.01s) 
     PASS should get new (0.01s) 
     PASS should show cart (0.01s) 
     PASS should update cart (0.01s) 
 
LineItemsControllerTest:
     PASS should create line item (0.02s) 
     PASS should create line item via ajax (0.11s) 
     PASS should destroy line item (0.01s) 
     PASS should get edit (0.01s) 
     PASS should get index (0.01s) 
     PASS should get new (0.01s) 
     PASS should show line item (0.01s) 
     PASS should update line item (0.01s) 
 
OrderNotifierTest:
     PASS received (0.09s) 
     PASS shipped (0.06s) 
 
OrdersControllerTest:
     PASS requires item in cart (0.01s) 
     PASS should create order (0.02s) 
     PASS should destroy order (0.01s) 
     PASS should get edit (0.01s) 
     PASS should get index (0.01s) 
     PASS should get new (0.01s) 
     PASS should show order (0.00s) 
     PASS should update order (0.01s) 
 
ProductsControllerTest:
     PASS can't delete product in cart (0.01s) 
     PASS should create product (0.05s) 
     PASS should destroy product (0.01s) 
     PASS should get edit (0.01s) 
     PASS should get index (0.01s) 
     PASS should get new (0.01s) 
     PASS should show product (0.01s) 
     PASS should update product (0.01s) 
 
SessionsControllerTest:
     PASS should fail login (0.09s) 
     PASS should get new (0.04s) 
     PASS should login (0.09s) 
     PASS should logout (0.00s) 
 
StoreControllerTest:
     PASS markup needed for store.js.coffee is in place (0.02s) 
     PASS should get index (0.01s) 
 
UsersControllerTest:
     PASS should create user (0.09s) 
     PASS should destroy user (0.00s) 
     PASS should get edit (0.01s) 
     PASS should get index (0.01s) 
     PASS should get new (0.01s) 
     PASS should show user (0.01s) 
     PASS should update user (0.09s) 
 
Finished in 1.470911 seconds.
 
47 tests, 78 assertions, 0 failures, 0 errors, 0 skips
[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message.
Loaded suite /home/rubys/.rvm/gems/ruby-1.9.2-p320/gems/rake-10.1.1/lib/rake/rake_test_loader
Started
 
DslUserStoriesTest:
     PASS buying a product (0.65s) 
     PASS two people buying (0.14s) 
 
UserStoriesTest:
     PASS buying a product (0.07s) 
 
Finished in 0.864904 seconds.
 
3 tests, 47 assertions, 0 failures, 0 errors, 0 skips

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