# 3.2 靜態頁面
前一節的準備工作做好之后,我們可以開始開發這個演示應用了。本節,我們要向開發動態頁面邁出第一步:創建一些 Rails 動作和視圖,但只包含靜態 HTML。Rails 動作放在控制器中(MVC 中的 C,參見 [1.3.3 節](chapter1.html#model-view-controller)),其中的動作是為了實現相關的功能。[第 2 章](chapter2.html#a-toy-app)已經簡要介紹了控制器,全面熟悉 [REST 架構](http://en.wikipedia.org/wiki/Representational_State_Transfer)之后(從[第 6 章](chapter6.html#modeling-users)開始),你會更深入地理解控制器。回想一下 [1.3 節](chapter1.html#the-first-application)介紹的 Rails 項目文件夾結構([圖 1.4](chapter1.html#fig-directory-structure-rails)),會對我們有所幫助。這一節主要在 `app/controllers` 和 `app/views` 兩個文件夾中工作。
在 [1.4.4 節](chapter1.html#branch-edit-commit-merge)我們說過,使用 Git 時最好在單獨的主題分支中完成工作。如果你使用 Git 做版本控制,現在應該執行下述命令,切換到一個主題分支中,然后再創建靜態頁面:
```
$ git checkout master
$ git checkout -b static-pages
```
(第一個命令的作用是確保我們現在處于主分支中,這樣才能基于 `master` 分支創建 `static-pages` 分支。如果你當前就在主分支中,可以不執行這個命令。)
## 3.2.1 生成靜態頁面
下面我們要使用第 2 章用來生成腳手架的 `generate` 命令生成一個控制器,既然這個控制器用來處理靜態頁面,那就把它命名為 `StaticPages` 吧。可以看出,控制器的名字使用[駝峰式命名法](https://en.wikipedia.org/wiki/CamelCase)。我們計劃創建“首頁”,“幫助”頁面和“關于”頁面,對應的動作名分別為 `home`、`help` 和 `about`。`generate` 命令可以接收一個可選的參數列表,指定要創建的動作。我們要在命令行中指定“首頁”和“幫助”頁面的動作,但故意不指定“關于”頁面的動作,在 [3.3 節](#getting-started-with-testing)再介紹怎么添加這個動作。生成靜態頁面控制器的命令如[代碼清單 3.4](#listing-generating-pages) 所示。
##### 代碼清單 3.4:生成靜態頁面控制器
```
$ rails generate controller StaticPages home help
create app/controllers/static_pages_controller.rb
route get 'static_pages/help'
route get 'static_pages/home'
invoke erb
create app/views/static_pages
create app/views/static_pages/home.html.erb
create app/views/static_pages/help.html.erb
invoke test_unit
create test/controllers/static_pages_controller_test.rb
invoke helper
create app/helpers/static_pages_helper.rb
invoke test_unit
create test/helpers/static_pages_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/static_pages.js.coffee
invoke scss
create app/assets/stylesheets/static_pages.css.scss
```
順便說一下,`rails generate` 可以簡寫成 `rails g`。除此之外,Rails 還提供了幾個命令的簡寫形式,參見[表 3.1](#table-shortcuts)。為了表述明確,本書會一直使用命令的完整形式,但在實際使用中,大多數 Rails 開發者或多或少都會使用簡寫形式。
表 3.1:Rails 中一些命令的簡寫形式
| 完整形式 | 簡寫形式 |
| --- | --- |
| `$ rails server` | `$ rails s` |
| `$ rails console` | `$ rails c` |
| `$ rails generate` | `$ rails g` |
| `$ bundle install` | `$ bundle` |
| `$ rake test` | `$ rake` |
在繼續之前,如果你使用 Git,最好把靜態頁面控制器對應的文件推送到遠程倉庫:
```
$ git status
$ git add -A
$ git commit -m "Add a Static Pages controller"
$ git push -u origin static-pages
```
最后一個命令的意思是,把 `static-pages` 主題分支推送到 Bitbucket。以后再推送時,可以省略后面的參數,簡寫成:
```
$ git push
```
在現實的開發過程中,我一般都會先提交再推送,但是為了行文簡潔,從這往后我們會省略提交這一步。
注意,在[代碼清單 3.4](#listing-generating-pages) 中,我們傳入的控制器名使用駝峰式,創建的控制器文件名使用[蛇底式](https://en.wikipedia.org/wiki/Snake_case)。所以,傳入“StaticPages”得到的文件是 `static_pages_controller.rb`。這只是一種約定。其實在命令行中也可以使用蛇底式:
```
$ rails generate controller static_pages ...
```
這個命令也會生成名為 `static_pages_controller.rb` 的控制器文件。因為 Ruby 的類名使用駝峰式([4.4 節](chapter4.html#ruby-classes)),所以提到控制器時我會使用駝峰式,不過這是我的個人選擇。(因為 Ruby 文件名一般使用蛇底式,所以 Rails 生成器使用 [`underscore`](http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore) 方法把駝峰式轉換成蛇底式。)
順便說一下,如果在生成代碼時出現了錯誤,知道如何撤銷操作就很有用了。[旁注 3.1](#aside-undoing-things) 中介紹了一些如何在 Rails 中撤銷操作的方法。
##### 旁注 3.1:撤銷操作
即使再小心,在開發 Rails 應用的過程中也可能會犯錯。幸好 Rails 提供了一些工具能夠幫助我們還原操作。
舉例來說,一個常見的情況是,更改控制器的名字,這時你得刪除生成的文件。生成控制器時,除了控制器文件本身之外,Rails 還會生成很多其他文件(參見[代碼清單 3.4](#listing-generating-pages))。撤銷生成的文件不僅僅要刪除控制器文件,還要刪除一些輔助的文件。(在 [2.2 節](chapter2.html#the-users-resource)和 [2.3 節](chapter2.html#the-microposts-resource)我們看到,`rails generate` 命令還會自動修改 `routes.rb` 文件,因此我們也想自動撤銷這些修改。)在 Rails 中,我們可以使用 `rails destroy` 命令完成撤銷操作。一般來說,下面這兩個命令是相互抵消的:
```
$ rails generate controller StaticPages home help
$ rails destroy controller StaticPages home help
```
[第 6 章](chapter6.html#modeling-users)會使用下面的命令生成模型:
```
$ rails generate model User name:string email:string
```
這個操作可以使用下面的命令撤銷:
```
$ rails destroy model User
```
(在這個例子中,我們可以省略命令行中其余的參數。讀到[第 6 章](chapter6.html#modeling-users)時,看看你能否發現為什么可以這么做。)
對模型來說,還涉及到撤銷遷移。[第 2 章](chapter2.html#a-toy-app)已經簡要介紹了遷移,[第 6 章](chapter6.html#modeling-users)開始會深入介紹。遷移通過下面的命令改變數據庫的狀態:
```
$ bundle exec rake db:migrate
```
我們可以使用下面的命令撤銷前一個遷移操作:
```
$ bundle exec rake db:rollback
```
如果要回到最開始的狀態,可以使用:
```
$ bundle exec rake db:migrate VERSION=0
```
你可能猜到了,把數字 0 換成其他的數字就會回到相應的版本,這些版本數字是按照遷移執行的順序排列的。
知道這些技術,我們就可以得心應對開發過程中遇到的各種問題了。
[代碼清單 3.4](#listing-generating-pages) 中生成靜態頁面控制器的命令會自動修改路由文件(`config/routes.rb`)。我們在 [1.3.4 節](chapter1.html#hello-world)已經簡略介紹過路由文件,它的作用是實現 URL 和網頁之間的對應關系([圖 2.11](chapter2.html#fig-mvc-detailed))。路由文件在 `config` 文件夾中。Rails 在這個文件夾中存放應用的配置文件([圖 3.1](#fig-config-directory-rails))。
圖 3.1:演示應用 `config` 文件夾中的內容
因為生成控制器時我們指定了 `home` 和 `help` 動作,所以在路由文件中已經添加了相應的規則,如[代碼清單 3.5](#listing-pages-routes) 所示。
##### 代碼清單 3.5:靜態頁面控制器中 `home` 和 `help` 動作的路由
config/routes.rb
```
Rails.application.routes.draw do
get 'static_pages/home' get 'static_pages/help' .
.
.
end
```
如下的規則
```
get 'static_pages/home'
```
把發給 /static_pages/home 的請求映射到靜態頁面控制器中的 `home` 動作上。另外,`get` 表明這個路由響應 GET 請求。GET 是 HTTP(超文本傳輸協議,Hypertext Transfer Protocol)支持的基本請求方法之一([旁注 3.2](#aside-get-etc))。在這個例子中,當我們在靜態頁面控制器中生成 `home` 動作時,就自動在 /static_pages/home 地址上獲得了一個頁面。若想查看這個頁面,按照 [1.3.2 節](chapter1.html#rails-server)中的方法,啟動 Rails 開發服務器:
```
$ rails server -b $IP -p $PORT # 如果在自己的電腦中,只需執行 `rails server`
```
然后訪問 [/static_pages/home](http://localhost:3000/static_pages/home),如[圖 3.2](#fig-raw-home-view) 所示。
圖 3.2:簡陋的首頁([/static_pages/home](http://localhost:3000/static_pages/home))
##### 旁注 3.2:GET 等
超文本傳輸協議([HTTP](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods))定義了幾個基本操作,`GET`、`POST`、`PATCH` 和 `DELETE`。這四個動詞表示客戶端電腦(通常安裝了一種瀏覽器,例如 Chrome、Firefox 或 Safari)和服務器(通常會運行一個 Web 服務器,例如 Apache 或 Nginx)之間的操作。(有一點很重要,你要知道,在本地電腦中開發 Rails 應用時,客戶端和服務器在同一臺物理設備中,但是二者是不同的概念。)受 REST 架構影響的 Web 框架(包括 Rails)都很重視對 HTTP 動詞的實現,我們在[第 2 章](chapter2.html#a-toy-app)已經簡要介紹了 REST,從[第 7 章](chapter7.html#sign-up)開始會做更詳細的介紹。
`GET` 是最常用的 HTTP 操作,用來讀取網絡中的數據。它的意思是“讀取一個網頁”,當你訪問 [http://www.google.com](http://www.google.com) 或 [http://www.wikipedia.org](http://www.wikipedia.org) 時,瀏覽器發送的就是 `GET` 請求。`POST` 是第二種最常用的操作,當你提交表單時瀏覽器發送的就是 `POST` 請求。在 Rails 應用中,`POST` 請求一般用來創建某個東西(不過 HTTP 也允許 `POST` 執行更新操作)。例如,提交注冊表單時發送的 `POST` 請求會在網站中創建一個新用戶。另外兩個動詞,`PATCH` 和 `DELETE`,分別用來更新和銷毀服務器上的某個東西。這兩個操作沒 `GET` 和 `POST` 那么常用,因為瀏覽器沒有內建對這兩種請求的支持,不過有些 Web 框架(包括 Rails)通過一些聰明的處理方式,讓它看起來就像是瀏覽器發出的一樣。所以,這四種請求類型 Rails 都支持。
要想弄明白這個頁面是怎么來的,我們先在文本編輯器中看一下靜態頁面控制器文件。你應該會看到類似[代碼清單 3.6](#listing-static-pages-controller) 所示的內容。你可能注意到了,不像[第 2 章](chapter2.html#a-toy-app)中的用戶和微博控制器,靜態頁面控制器沒使用標準的 REST 動作。這對靜態頁面來說是很常見的,畢竟 REST 架構不能解決所有問題。
##### 代碼清單 3.6:代碼清單 3.4 生成的靜態頁面控制器
app/controllers/static_pages_controller.rb
```
class StaticPagesController < ApplicationController
def home
end
def help
end
end
```
從上面代碼中的 `class` 可以看出,`static_pages_controller.rb` 文件中定義了一個類,名為 `StaticPagesController`。類是一種組織函數(也叫方法)的有效方式,例如 `home` 和 `help` 動作就是方法,使用 `def` 關鍵字定義。[2.3.4 節](chapter2.html#inheritance-hierarchies)說過,尖括號 `<` 表示 `StaticPagesController` 繼承自 `ApplicationController` 類,這就意味著我們定義的頁面擁有了 Rails 提供的大量功能。(我們會在 [4.4 節](chapter4.html#ruby-classes)更詳細的介紹類和繼承。)
在本例中,靜態頁面控制器中的兩個方法默認都是空的:
```
def home
end
def help
end
```
如果是普通的 Ruby 代碼,這兩個方法什么也做不了。不過在 Rails 中就不一樣了,`StaticPagesController` 是一個 Ruby 類,但是因為它繼承自 `ApplicationController`,其中的方法對 Rails 來說就有了特殊意義:訪問 /static_pages/home 時,Rails 會在靜態頁面控制器中尋找 `home` 動作,然后執行該動作,再渲染相應的視圖(MVC 中的 V,參見 [1.3.3 節](chapter1.html#model-view-controller))。在本例中,`home` 動作是空的,所以訪問 /static_pages/home 后只會渲染視圖。那么,視圖是什么樣子,怎么才能找到它呢?
如果你再看一下[代碼清單 3.4](#listing-generating-pages) 的輸出,或許能猜到動作和視圖之間的對應關系:`home` 動作對應的視圖是 `home.html.erb`。[3.4 節](#slightly-dynamic-pages)會告訴你 `.erb` 是什么意思。看到 `.html` 你或許就不奇怪了,這個文件基本上就是 HTML,如[代碼清單 3.7](#listing-raw-home-view) 所示。
##### 代碼清單 3.7:為“首頁”生成的視圖
app/views/static_pages/home.html.erb
```
<h1>StaticPages#home</h1>
<p>Find me in app/views/static_pages/home.html.erb</p>
```
`help` 動作的視圖類似,如[代碼清單 3.8](#listing-raw-help-view) 所示。
##### 代碼清單 3.8:為“幫助”頁面生成的視圖
app/views/static_pages/help.html.erb
```
<h1>StaticPages#help</h1>
<p>Find me in app/views/static_pages/help.html.erb</p>
```
這兩個視圖都只是占位用的,它們的內容中都有一個一級標題(`h1` 標簽)和一個顯示視圖文件完整路徑的段落(`p` 標簽)。
## 3.2.2 修改靜態頁面中的內容
我們會在 [3.4 節](#slightly-dynamic-pages)添加一些簡單的動態內容。現在,這些靜態內容的存在是為了強調一件很重要的事:Rails 的視圖可以只包含靜態的 HTML。所以我們甚至無需了解 Rails 就可以修改“首頁”和“幫助”頁面的內容,如[代碼清單 3.9](#listing-custom-home-page) 和 [代碼清單 3.10](#listing-custom-help-page) 所示。
##### 代碼清單 3.9:修改“首頁”的 HTML
app/views/static_pages/home.html.erb
```
<h1>Sample App</h1>
<p>
This is the home page for the
<a href="http://www.railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</p>
```
##### 代碼清單 3.10:修改“幫助”頁面的 HTML
app/views/static_pages/help.html.erb
```
<h1>Help</h1>
<p>
Get help on the Ruby on Rails Tutorial at the
<a href="http://www.railstutorial.org/#help">Rails Tutorial help section</a>.
To get help on this sample app, see the
<a href="http://www.railstutorial.org/book"><em>Ruby on Rails Tutorial</em>
book</a>.
</p>
```
修改之后,這兩個頁面顯示的內容如[圖 3.3](#fig-custom-home-page) 和 [圖 3.4](#fig-custom-help-page) 所示。
圖 3.3:修改后的“首頁”圖 3.4:修改后的“幫助”頁面
- 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 練習