# 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))。
![config directory 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcf4a9b9a.png)图 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) 所示。
![raw home view 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcf4cb8ed.png)图 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) 所示。
![custom home page](https://box.kancloud.cn/2016-05-11_5732bcf4de2ae.png)图 3.3:修改后的“首页”![custom help page 3rd edition](https://box.kancloud.cn/2016-05-11_5732bcf50cf27.png)图 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 练习