# 7.4 注冊成功
處理完提交無效數據的情況,本節我們要完成注冊表單的功能,如果提交的數據有效,就把用戶存入數據庫。我們先嘗試保存用戶,如果保存成功,用戶的數據會自動存入數據庫,然后在瀏覽器中重定向,轉向新注冊用戶的資料頁面,頁面中還會顯示一個歡迎消息,構思圖如[圖 7.19](#fig-signup-success-mockup) 所示。如果保存用戶失敗了,就交由上一節實現的功能處理。
圖 7.19:注冊成功后顯示的頁面構思圖
## 7.4.1 完整的注冊表單
要完成注冊表單的功能,我們要把[代碼清單 7.17](#listing-create-action-strong-parameters) 中的注釋換成適當的代碼。現在,提交有效數據時也不能正確處理,如[圖 7.20](#fig-valid-submission-error) 所示,因為 Rails 動作的默認行為是渲染對應的視圖,而 `create` 動作不對應視圖。
圖 7.20:提交有效注冊信息后顯示的錯誤頁面
成功注冊后,我們不需要渲染頁面,而要重定向到另一個頁面。按照習慣,我們要重定向到新注冊用戶的資料頁面,不過轉到根地址也行。為此,在應用代碼中要使用 `redirect_to` 方法,如[代碼清單 7.23](#listing-user-create-action) 所示。
##### 代碼清單 7.23:`create` 動作的代碼,處理保存和重定向操作
app/controllers/users_controller.rb
```
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
redirect_to @user else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
```
注意,我們寫的是:
```
redirect_to @user
```
不過,也可以寫成:
```
redirect_to user_url(@user)
```
Rails 看到 `redirect_to @user` 后,知道我們想重定向到 `user_url(@user)`。
## 7.4.2 閃現消息
有了[代碼清單 7.23](#listing-user-create-action) 中的代碼后,注冊表單已經可以使用了。不過在提交有效數據注冊之前,我們要添加一個 Web 應用中經常使用的增強功能:訪問隨后的頁面時顯示一個消息(這里,我們要顯示一個歡迎新用戶的消息),如果再訪問其他頁面,或者刷新頁面,這個消息要消失。
在 Rails 中,短暫顯示一個消息使用“閃現消息”(flash message)實現。按照 Rails 的約定,操作成功時使用 `:success` 鍵表示,如[代碼清單 7.24](#listing-signup-flash) 所示。
##### 代碼清單 7.24:用戶注冊成功后顯示一個閃現消息
app/controllers/users_controller.rb
```
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
flash[:success] = "Welcome to the Sample App!" redirect_to @user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
```
把一個消息賦值給 `flash` 之后,我們就可以在重定向后的第一個頁面中將其顯示出來了。我們要遍歷 `flash`,在網站布局中顯示所有相關的消息。你可能還記得 [4.3.3 節](chapter4.html#hashes-and-symbols)在控制臺中遍歷哈希那個例子,當時我故意把變量命名為 `flash`:
```
$ rails console
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", danger: "It failed."}
>> flash.each do |key, value|
?> puts "#{key}"
?> puts "#{value}"
>> end
success
It worked!
danger
It failed.
```
按照上述方式,我們可以使用如下的代碼在網站的全部頁面中顯示閃現消息的內容:
```
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
```
(這段代碼很亂,混用了 HTML 和 ERb,[7.7 節](#sign-up-exercises)中有一題會要求你把它變得好看一些。)
其中
```
alert-<%= message_type %>
```
為各種類型的消息指定一個 CSS 類,因此,`:success` 消息的類是 `alert-success`。(`:success` 是個符號,ERb 會自動把它轉換成字符串 `"success"`,然后再插入模板。)
為不同類型的消息指定不同的 CSS 類,可以為不同類型的消息指定不同的樣式。例如,[8.1.4 節](chapter8.html#rendering-with-a-flash-message)會使用 `flash[:danger]` 顯示登錄失敗消息。[[11](#fn-11)](其實,在[代碼清單 7.19](#listing-errors-partial) 中為錯誤消息區域指定樣式時,已經用過 `alert-danger`。)Bootstrap 提供的 CSS 支持四種閃現消息樣式,分別為 `success`,`info`,`warning` 和 `danger`,在開發這個演示應用的過程中,我們會找機會全部使用一遍。
消息也會在模板中顯示,如下代碼:
```
flash[:success] = "Welcome to the Sample App!"
```
得到的完整 HTML 是:
```
<div class="alert alert-success">Welcome to the Sample App!</div>
```
把前面的 ERb 代碼放入網站的布局中,得到的布局如[代碼清單 7.25](#listing-layout-flash) 所示。
##### 代碼清單 7.25:在網站的布局中添加閃現消息
app/views/layouts/application.html.erb
```
<!DOCTYPE html>
<html>
.
.
.
<body>
<%= render 'layouts/header' %>
<div class="container">
<% flash.each do |message_type, message| %>
<div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
.
.
.
</body>
</html>
```
## 7.4.3 首次注冊
現在我們可以注冊一個用戶,看看到目前為止所實現的功能。用戶的名字使用“Rails Tutorial”,電子郵件地址使用“example@railstutorial.org”,如[圖 7.21](#fig-first-signup) 所示。注冊成功后,頁面中顯示了一個友好的歡迎消息,如[圖 7.22](#fig-signup-flash) 所示。消息的樣式是由 [5.1.2 節](chapter5.html#bootstrap-and-custom-css)集成的 Bootstrap 框架提供的 `.success` 類實現。(如果無法注冊,提示電子郵件地址已經使用,確保按照 [7.2 節](#signup-form)的說明,執行了 `db:migrate:reset` Rake 任務,而且重啟了開發服務器。)刷新用戶資料頁面后,閃現消息會消失,如[圖 7.23](#fig-signup-flash-reloaded) 所示。
圖 7.21:首次注冊時填寫的信息圖 7.22:注冊成功后顯示有閃現消息的頁面圖 7.23:刷新頁面后資料頁面中的閃現消息不見了
我們還可以檢查一下數據庫,確保真得創建了新用戶:
```
$ rails console
>> User.find_by(email: "example@railstutorial.org")
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org",
created_at: "2014-08-29 19:53:17", updated_at: "2014-08-29 19:53:17",
password_digest: "$2a$10$zthScEx9x6EkuLa4NolGye6O0Zgrkp1B6LQ12pTHlNB...">
```
## 7.4.4 注冊成功的測試
在繼續之前,我們要編寫測試,確認提交有效信息后應用的表現正常,并捕獲可能出現的回歸。和 [7.3.4 節](#a-test-for-invalid-submission)中注冊失敗的測試一樣,我們主要是檢查數據庫中的內容。這一次,我們要提交有效的數據,確認創建了一個用戶。類似[代碼清單 7.21](#listing-a-test-for-invalid-submission) 中使用的
```
assert_no_difference 'User.count' do
post users_path, ...
end
```
這里我們要使用對應的 `assert_difference` 方法:
```
assert_difference 'User.count', 1 do
post_via_redirect users_path, ...
end
```
和 `assert_no_difference` 一樣,`assert_difference` 的第一個參數是字符串 `'User.count'`,目的是比較塊中的代碼執行前后 `User.count` 的變化。第二個參數可選,指定變化的數量(這里是 1)。
把 `assert_difference` 加入[代碼清單 7.21](#listing-a-test-for-invalid-submission) 后,得到的測試如[代碼清單 7.26](#listing-a-test-for-valid-submission) 所示。注意,請求用戶的資料頁面時,使用的是 `post_via_redirect` 方法,目的是提交數據后繼續跟蹤重定向,渲染 `users/show` 模板。(針對閃現消息的測試留作[練習](#sign-up-exercises)。)
##### 代碼清單 7.26:注冊成功的測試 GREEN
test/integration/users_signup_test.rb
```
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
.
.
.
test "valid signup information" do
get signup_path
name = "Example User"
email = "user@example.com"
password = "password"
assert_difference 'User.count', 1 do
post_via_redirect users_path, user: { name: name,
email: email,
password: password,
password_confirmation: password }
end
assert_template 'users/show'
end
end
```
注意,這個測試還確認了成功注冊后會渲染 `show` 視圖。如果想讓測試通過,用戶資源的路由([代碼清單 7.3](#listing-users-resource)),用戶控制器中的 `show` 動作([代碼清單 7.5](#listing-user-show-action)),以及 `show.html.erb` 視圖([代碼清單 7.8](#listing-user-show-view-with-gravatar))都得能正常使用才行。所以,
```
assert_template 'users/show'
```
這一行代碼就能測試用戶資料頁面幾乎所有的相關功能。這種對應用中重要功能的端到端覆蓋展示了集成測試的重大作用。
- Ruby on Rails 教程
- 致中國讀者
- 序
- 致謝
- 作者譯者簡介
- 版權和代碼授權協議
- 第 1 章 從零開始,完成一次部署
- 1.1 簡介
- 1.2 搭建環境
- 1.3 第一個應用
- 1.4 使用 Git 做版本控制
- 1.5 部署
- 1.6 小結
- 1.7 練習
- 第 2 章 玩具應用
- 2.1 規劃應用
- 2.2 用戶資源
- 2.3 微博資源
- 2.4 小結
- 2.5 練習
- 第 3 章 基本靜態的頁面
- 3.1 創建演示應用
- 3.2 靜態頁面
- 3.3 開始測試
- 3.4 有點動態內容的頁面
- 3.5 小結
- 3.6 練習
- 3.7 高級測試技術
- 第 4 章 Rails 背后的 Ruby
- 4.1 導言
- 4.2 字符串和方法
- 4.3 其他數據類型
- 4.4 Ruby 類
- 4.5 小結
- 4.6 練習
- 第 5 章 完善布局
- 5.1 添加一些結構
- 5.2 Sass 和 Asset Pipeline
- 5.3 布局中的鏈接
- 5.4 用戶注冊:第一步
- 5.5 小結
- 5.6 練習
- 第 6 章 用戶模型
- 6.1 用戶模型
- 6.2 用戶數據驗證
- 6.3 添加安全密碼
- 6.4 小結
- 6.5 練習
- 第 7 章 注冊
- 7.1 顯示用戶的信息
- 7.2 注冊表單
- 7.3 注冊失敗
- 7.4 注冊成功
- 7.5 專業部署方案
- 7.6 小結
- 7.7 練習
- 第 8 章 登錄和退出
- 8.1 會話
- 8.2 登錄
- 8.3 退出
- 8.4 記住我
- 8.5 小結
- 8.6 練習
- 第 9 章 更新,顯示和刪除用戶
- 9.1 更新用戶
- 9.2 權限系統
- 9.3 列出所有用戶
- 9.4 刪除用戶
- 9.5 小結
- 9.6 練習
- 第 10 章 賬戶激活和密碼重設
- 10.1 賬戶激活
- 10.2 密碼重設
- 10.3 在生產環境中發送郵件
- 10.4 小結
- 10.5 練習
- 10.6 證明超時失效的比較算式
- 第 11 章 用戶的微博
- 11.1 微博模型
- 11.2 顯示微博
- 11.3 微博相關的操作
- 11.4 微博中的圖片
- 11.5 小結
- 11.6 練習
- 第 12 章 關注用戶
- 12.1 “關系”模型
- 12.2 關注用戶的網頁界面
- 12.3 動態流
- 12.4 小結
- 12.5 練習