8.3 退出
8.1 节说过,我们要实现的认证系统会记住用户的登录状态,直到用户自行退出为止。本节,我们就要实现退出功能。退出链接已经定义好了(代码清单 8.16),所以我们只需编写一个正确的控制器动作,销毁用户会话。
目前为止,会话控制器的动作都遵从了 REST 架构,new
动作用于登录页面,create
动作完成登录操作。我们要继续使用 REST 架构,添加一个 destroy
动作,删除会话,实现退出功能。登录功能在代码清单 8.13 和代码清单 8.22 中都用到了,但退出功能不同,只在一处使用,所以我们会直接把相关的代码写在 destroy
动作中。8.4.6 节会看到,这么做(稍微重构后)易于测试认证系统。
退出要撤销 log_in
(代码清单 8.12)完成的操作,即从会话中删除用户的 ID。为此,我们要使用 delete
方法,如下所示:
session.delete(:user_id)
我们还要把当前用户设为 nil
。不过在现在这种情况下做不做这一步都没关系,因为退出后会立即转向根地址。[13]和 log_in
及相关的方法一样,我们要把 log_out
方法放在会话辅助方法模块中,如代码清单 8.26 所示。
代码清单 8.26:log_out
方法
app/helpers/sessions_helper.rb
module SessionsHelper
# 登入指定的用户
def log_in(user)
session[:user_id] = user.id
end
.
.
.
# 退出当前用户
def log_out
session.delete(:user_id) @current_user = nil end
end
然后,在会话控制器的 destroy
动作中调用 log_out
方法,如代码清单 8.27 所示。
代码清单 8.27:销毁会话(退出用户)
app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
log_in user
redirect_to user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out redirect_to root_url end
end
我们可以在代码清单 8.20 中的用户登录测试中添加一些步骤,测试退出功能。登录后,使用 delete
方法向退出地址(表 8.1)发起 DELETE
请求,然后确认用户已经退出,而且重定向到了根地址。我们还要确认出现了登录链接,而且退出和资料页面的链接消失了。测试中新加入的步骤如代码清单 8.28 所示。
代码清单 8.28:测试用户退出功能 GREEN
test/integration/users_login_test.rb
require 'test_helper'
class UsersLoginTest < ActionDispatch::IntegrationTest
.
.
.
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
end
(现在可以在测试中使用 is_logged_in?
了,所以向登录地址发送有效信息之后,我们添加了 assert is_logged_in?
。)
定义并测试了 destroy
动作之后,注册、登录和退出三大功能就都实现了。现在测试组件应该可以通过:
代码清单 8.29:GREEN
$ bundle exec rake test