Logging out

現在要來實作使用者登出。也就是要透過 Sessions controller 的 destroy action 來銷毀 session。

之前的登入功能 log_in 我們在兩個地方:app/controllers/users_controller.rbapp/controllers/sessions_controller.rb 都有用到,不過登出的話只需要放在一個地方就好,也就是直接在 destroy action 處理就可以了。

登出其實就是要解除 log_in 的操作,也就是從 session 中刪除使用者的 ID,所以使用 delete 方法:

session.delete(:user_id)

同時也把目前使用者設為 nil,不過其實在這種情況下有沒有這個步驟都沒差別,因為登出後會導向首頁(root_path),和 log_in 一樣,把 log_out 方法放在 Sessions 輔助方法裡:

app/helpers/sessions_helper.rb

module SessionsHelper

  # Logs in the given user.
  def log_in(user)
    session[:user_id] = user.id
  end
  .
  .
  .
  # Logs out the current user.
  def log_out
    session.delete(:user_id)
    @current_user = nil
  end
end

然後再把 log_out 輔助方法放進 destroy action 裡面:

app/controllers/sessions_controller.rb

def destroy
  log_out
  redirect_to root_url
end

然後可以在 test/integration/users_login_test.rb 增加測試登出的功能:

test/integration/users_login_test.rb

test "login with valid information followed by logout" do
    get login_path
    post login_path, session: { email: @user.email, password: 'password' }
    assert is_logged_in?
    assert_redirected_to @user
    follow_redirect!
    assert_template 'users/show'
    assert_select "a[href=?]", login_path, count: 0
    assert_select "a[href=?]", logout_path
    assert_select "a[href=?]", user_path(@user)
    delete logout_path
    assert_not is_logged_in?
    assert_redirected_to root_url
    follow_redirect!
    assert_select "a[href=?]", login_path
    assert_select "a[href=?]", logout_path,      count: 0
    assert_select "a[href=?]", user_path(@user), count: 0
  end

登入後,使用 delete 向 logout_path 發出 DELETE 請求,然後確認使用者已經登出,重新導向到 root_path,還要確認出現 login_path 連結,logout_path、profile 連結都消失。

現在可以在測試中使用 is_logged_in?,所以向 login_path 發送有效資訊之後,我們增加了 assert is_logged_in?

執行測試應該會通過(Green):

$ bundle exec rake test