1.3 第一个应用
按照计算机编程领域长期沿用的传统,第一个应用的目的是编写一个“hello, world”程序。具体说来,我们要创建一个简单的应用,在网页中显示字符串“hello, world!”,在开发环境(1.3.4 节)和线上网站中(1.5 节)都是如此。
Rails 应用一般都从 rails new
命令开始,这个命令会在你指定的文件夹中创建一个 Rails 应用骨架。如果没使用1.2.1 节推荐的 Cloud9 IDE,首先你要新建一个文件夹,命名为 workspace
,然后进入这个文件夹,如代码清单 1.2 所示。(代码清单 1.2 中使用了 Unix 命令 cd
和 mkdir
,如果你不熟悉这些命令,可以阅读旁注 1.3。)
代码清单 1.2:为 Rails 项目新建一个文件夹,命名为 workspace
(在云端环境中不用这一步)
$ cd # 进入家目录
$ mkdir workspace # 新建 workspace 文件夹
$ cd workspace/ # 进入 workspace 文件夹
旁注 1.3:Unix 命令行速成课
使用 Windows 和 Mac OS X(数量较少,但增长势头迅猛)的用户可能对 Unix 命令行不熟悉。如果使用推荐的云端环境,很幸运,这个环境提供了 Unix(Linux)命令行——在标准的 shell 命令行界面)中运行的 Bash)。
命令行的基本思想很简单:使用简短的命令执行很多操作,例如创建文件夹(mkdir
),移动和复制文件(mv
和 cp
),以及变换目录浏览文件系统(cd
)。主要使用图形化界面(Graphical User Interface,简称 GUI)的用户可能觉得命令行落后,其实是被表象蒙蔽了:命令行是开发者最强大的工具之一。其实,你经常会看到经验丰富的开发者开着多个终端窗口,运行命令行 shell。
这是一门很深的学问,但在本书中只会用到一些最常用的 Unix 命令行命令,如表 1.1所示。若想更深入地学习 Unix 命令行,请阅读 Mark Bates 写的《Conquering the Command Line》(可以免费在线阅读,也可以购买电子书和视频)。
表 1.1:一些常用的 Unix 命令
作用 | 命令 | 示例 |
---|---|---|
列出内容 | ls | $ ls -l |
新建文件夹 | mkdir <dirname> | $ mkdir workspace |
变换目录 | cd <dirname> | $ cd workspace/ |
进入上层目录 | $ cd .. | |
进入家目录 | $cd ~ 或 $ cd | |
进入家目录中的文件夹 | $ cd ~/workspace/ | |
移动文件(重命名) | mv <source> <target> | $ mv README.rdoc README.md |
复制文件 | cp <source> <target> | $ cp README.rdoc README.md |
删除文件 | rm <file> | $ rm README.rdoc |
删除空文件夹 | rmdir <directory> | $ rmdir workspace/ |
删除非空文件夹 | rm -rf <directory> | $ rm -rf tmp/ |
连结并显示文件的内容 | cat <file> | $ cat ~/.ssh/id_rsa.pub |
不管在本地环境,还是在云端 IDE 中,下一步都是使用代码清单 1.3中的命令创建第一个应用。注意,在这个代码清单中,我们明确指定了 Rails 版本(4.2.2
)。这么做的目的是,确保使用代码清单 1.1中安装的 Rails 版本来创建这个应用的文件夹结构。(执行代码清单 1.3 中的命令时,如果返回“Could not find ’railties”这样的错误,说明你没安装正确的 Rails 版本,再次确认你安装 Rails 时执行的命令和代码清单 1.1 一模一样。)
代码清单 1.3:执行 rails new
命令(明确指定版本号)
$ cd ~/workspace $ rails _4.2.2_ new hello_app
create
create README.rdoc
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/assets/javascripts/application.js
create app/assets/stylesheets/application.css
create app/controllers/application_controller.rb
.
.
.
create test/test_helper.rb
create tmp/cache
create tmp/cache/assets
create vendor/assets/javascripts
create vendor/assets/javascripts/.keep
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.keep
run bundle install
Fetching gem metadata from https://rubygems.org/..........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.3.2
Using i18n 0.6.11
.
.
.
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
run bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted
如代码清单 1.3 所示,执行 rails new
命令生成所有文件之后,会自动执行 bundle install
命令。我们会在 1.3.1 节说明这个命令的作用。
留意一下 rails new
命令创建的文件和文件夹。这个标准的文件夹结构(如图 1.4)是 Rails 的众多优势之一——让你从零开始快速的创建一个可运行的简单应用。而且,所有 Rails 应用都使用这种文件夹结构,所以阅读他人的代码时很快就能理清头绪。这些文件的作用如表 1.2 所示,在本书的后续内容中将介绍其中大多数文件和文件夹。从 5.2.1 节开始,我们将介绍 app/assets
文件夹,它是 Asset Pipeline 的一部分。Asset Pipeline 把组织、部署 CSS 和 JavaScript 等资源文件变得异常简单。
图 1.4:新建 Rails 应用的文件夹结构
表 1.2:Rails 文件夹结构简介
文件/文件夹 | 作用 |
---|---|
app/ | 应用的核心文件,包含模型、视图、控制器和辅助方法 |
app/assets | 应用的资源文件,例如层叠样式表(CSS)、JavaScript 和图片 |
bin/ | 可执行二进制文件 |
config/ | 应用的配置 |
db/ | 数据库相关的文件 |
doc/ | 应用的文档 |
lib/ | 代码库模块文件 |
lib/assets | 代码库的资源文件,例如 CSS、JavaScript 和图片 |
log/ | 应用的日志文件 |
public/ | 公共(例如浏览器)可访问的文件,例如错误页面 |
bin/rails | 生成代码、打开终端会话或启动本地服务器的程序 |
test/ | 应用的测试 |
tmp/ | 临时文件 |
vendor/ | 第三方代码,例如插件和 gem |
vendor/assets | 第三方资源文件,例如 CSS、JavaScript 和图片 |
README.rdoc | 应用简介 |
Rakefile | 使用 rake 命令执行的实用任务 |
Gemfile | 应用所需的 gem |
Gemfile.lock | gem 列表,确保这个应用的副本使用相同版本的 gem |
config.ru | Rack 中间件的配置文件 |
.gitignore | Git 忽略的文件 |
1.3.1 Bundler
创建完一个新的 Rails 应用后,下一步是使用 Bundler 安装和包含该应用所需的 gem。在 1.3 节简单提到过,执行 rails new
命令时会自动运行 Bundler(通过 bundle install
命令)。不过这一节,我们要修改应用默认使用的 gem,然后再次运行 Bundler。首先,在文本编辑器中打开文件 Gemfile
,虽然具体的版本号和内容或许有所不同,但大概与代码清单 1.4 和图 1.5 差不多。(这个文件中的内容是 Ruby 代码,现在先不关心句法,第 4 章会详细介绍 Ruby。)如果你没看到如图 1.5 所示的文件和文件夹,点击文件浏览器中的齿轮图标,然后选择“Refresh File Tree”(刷新文件树)。(如果没出现某个文件或文件夹,就可以刷新文件树。)
代码清单 1.4:hello_app
中默认生成的 Gemfile
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '4.2.2'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# Use CoffeeScript for .js.coffee assets and views
gem 'coffee-rails', '~> 4.1.0'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Use jquery as the JavaScript library
gem 'jquery-rails'
# Turbolinks makes following links in your web application faster. Read more:
# https://github.com/rails/turbolinks
gem 'turbolinks'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.0'
# bundle exec rake doc:rails generates the API under doc/api.
gem 'sdoc', '~> 0.4.0', group: :doc
# Use ActiveModel has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Unicorn as the app server
# gem 'unicorn'
# Use Capistrano for deployment
# gem 'capistrano-rails', group: :development
group :development, :test do
# Call 'debugger' anywhere in the code to stop execution and get a
# debugger console
gem 'byebug'
# Access an IRB console on exceptions page and /console in development
gem 'web-console', '~> 2.0'
# Spring speeds up development by keeping your application running in the
# background. Read more: https://github.com/rails/spring
gem 'spring'
end
图 1.5:在文本编辑器中打开默认生成的 Gemfile
其中很多行代码都用 #
符号注释掉了,这些代码放在这是为了告诉你一些常用的 gem,也是为了展示 Bundler 的句法。现在,除了这些默认的 gem 之外,我们还不需要其他的 gem。
如果没在 gem
指令中指定版本号,Bundler 会自动最新版。下面就是一例:
gem 'sqlite3'
还有两种常用的方法,用来指定 gem 版本的范围,一定程度上控制 Rails 使用的版本。首先看下面这行代码:
gem 'uglifier', '>= 1.3.0'
这行代码的意思是,安装版本号大于或等于 1.3.0
的最新版 uglifier
(作用是压缩 Asset Pipeline 中的文件),就算是 7.2
版也会安装。第二种方法如下所示:
gem 'coffee-rails', '~> 4.0.0'
这行代码的意思是,安装版本号大于 4.0.0
,但小于 4.1
的 coffee-rails
。也就是说,>=
符号的意思是始终安装最新版;~> 4.0.0
的意思是只安装补丁版本号变化的版本(例如从 4.0.0
到 4.0.1
),而不安装次版本或主版本的更新(例如从 4.0
到 4.1
)。不过,经验告诉我们,即使是补丁版本的升级也可能导致错误,所以在本教程中我们基本上会为所有的 gem 都指定精确的版本号。你可以使用任何 gem 的最新版本,还可以在 Gemfile
中使用 ~>
(一般推荐有经验的用户使用),但事先提醒你,这可能会导致本教程开发的应用表现异常。
修改代码清单 1.4 中的 Gemfile
,换用精确的版本号,得到的结果如代码清单 1.5 所示。注意,借此机会我们还变动了 sqlite3
的位置,只在开发环境和测试环境(7.1.1 节)中安装,避免和 Heroku 所用的数据库冲突(1.5 节)。
代码清单 1.5:每个 Ruby gem 都使用精确版本号的 Gemfile
source 'https://rubygems.org'
gem 'rails', '4.2.2'
gem 'sass-rails', '5.0.2'
gem 'uglifier', '2.5.3'
gem 'coffee-rails', '4.1.0'
gem 'jquery-rails', '4.0.3'
gem 'turbolinks', '2.3.0'
gem 'jbuilder', '2.2.3'
gem 'sdoc', '0.4.0', group: :doc
group :development, :test do
gem 'sqlite3', '1.3.9'
gem 'byebug', '3.4.0'
gem 'web-console', '2.0.0.beta3'
gem 'spring', '1.1.3'
end
把代码清单 1.5 中的内容写入应用的 Gemfile
文件之后,执行 bundle install
命令[7]安装这些 gem:
$ cd hello_app/ $ bundle install Fetching source index for https://rubygems.org/
.
.
.
bundle install
命令可能要执行一会儿,不过结束后我们的应用就可以运行了。
1.3.2 rails server
运行完 1.3 节中的 rails new
命令和 1.3.1 节 中的 bundle install
命令之后,我们的应用就可以运行了,但是怎么运行呢?Rails 自带了一个命令行程序(或叫脚本),可以运行一个本地服务器,协助我们的开发工作。这个命令具体怎么执行,取决于你使用的环境:在本地系统中,直接执行 rails server
命令就行(代码清单 1.6);而在 Cloud9 中,还要指定绑定的 IP 地址和端口号,告诉 Rails 服务器外界可以通过哪个地址访问应用(代码清单 1.7)。[8](Cloud9 使用特殊的环境变量 $IP
和 $PORT
动态指定 IP 地址和端口号。如果想查看这两个环境变量的值,可以在命令行中输入 echo $IP
和 echo $PORT
。)如果系统提示缺少 JavaScript 运行时,访问 execjs 在 GitHub 中的项目主页,查看解决方法。我非常推荐安装 Node.js。
代码清单 1.6:在本地设备中运行 Rails 服务器
$ cd ~/workspace/hello_app/ $ rails server => Booting WEBrick
=> Rails application starting on http://localhost:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
代码清单 1.7:在云端 IDE 中运行 Rails 服务器
$ cd ~/workspace/hello_app/ $ rails server -b $IP -p $PORT => Booting WEBrick
=> Rails application starting on http://0.0.0.0:8080
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
不管使用哪种环境,我都建议你在另一个终端选项卡中执行 rails server
命令,这样你就可以继续在第一个选项卡中执行其他命令了,如图 1.6 和图 1.7 所示。(如果你已经在第一个选项卡中启动了服务器,可以按 Ctrl-C 键关闭服务器。)在本地环境中,在浏览器中打开 http://localhost:3000/;在云端 IDE 中,打开“Share”(分享)面板,点击“Application”后的地址即可打开应用(如图 1.8)。在这两种环境中,显示的页面应该都和图 1.9 类似。
点击“About your application’s environment”可以查看应用的信息。你看到的版本号可能和我的不一样,但和图 1.10 差不多。当然了,从长远来看,我们不需要这个 Rails 默认页面,不过现在看到这个页面说明 Rails 可以正常运行了。我们会在1.3.4 节删除这个页面,替换成我们自己写的首页。
图 1.6:再打开一个终端选项卡图 1.7:在另一个选项卡中运行 Rails 服务器图 1.8:分享运行在云端工作空间中的本地服务器图 1.9:执行 rails server
命令后看到的 Rails 默认页面图 1.10:默认页面中显示应用的环境信息
1.3.3 模型-视图-控制器
在初期阶段,概览一下 Rails 应用的工作方式(图 1.11)多少会有些帮助。你可能已经注意到了,在 Rails 应用的标准文件夹结构中有一个文件夹叫 app/
(图 1.4),其中有三个子文件夹:models
、views
和 controllers
。这暗示 Rails 采用了“模型-视图-控制器”(简称 MVC)架构模式,这种模式把“域逻辑”(domain logic,也叫“业务逻辑”(business logic))与图形用户界面相关的输入和表现逻辑强制分开。在 Web 应用中,“域逻辑”一般是“用户”、“文章”和“商品”等数据模型,GUI 则是浏览器中的网页。
图 1.11:MVC 架构图解
和 Rails 应用交互时,浏览器发出一个请求(request),Web 服务器收到这个请求之后将其传给 Rails 应用的控制器,由控制器决定下一步该做什么。某些情况下,控制器会立即渲染视图(view),生成 HTML,然后发送给浏览器。对于动态网站来说,更常见的是控制器和模型(model)交互。模型是一个 Ruby 对象,表示网站中的一个元素(例如一个用户),并且负责和数据库通信。和模型交互后,控制器再渲染视图,并把生成的 HTML 返回给浏览器。
如果你觉得这些内容有点抽象,不用担心,后面会经常讲到 MVC。在1.3.4 节中,首次使用 MVC 架构编写应用;在 2.2.2 节中,会以一个应用为例较为深入地讨论 MVC;在最后那个演示应用中会使用完整的 MVC 架构。从 3.2 节开始,介绍控制器和视图;从 6.1 节开始,介绍模型;7.1.2 节则把这三部分放在一起使用。
1.3.4 Hello, world!
接下来我们要对这个使用 MVC 框架开发的第一个应用做些小改动——添加一个控制器动作,渲染字符串“hello, world!”。(从 2.2.2 节开始会更深入的介绍控制器动作。)这一节的目的是,使用显示“hello, world!”的页面替换 Rails 默认的首页(图 1.9)。
从“控制器动作”这个名字可以看出,动作在控制器中定义。我们要在 ApplicationController
中定义这个动作,并将其命名为 hello
。其实,现在我们的应用只有 ApplicationController
这一个控制器。执行下面的命令可以验证这一点(从 第 2 章开始,我们会创建自己的控制器。):
$ ls app/controllers/*_controller.rb
hello
动作的定义体如代码清单 1.8 所示,调用 render
函数返回文本“hello, world!”。(现在先不管 Ruby 的句法,第 4 章会详细介绍。)
代码清单 1.8:在 ApplicationController
中添加 hello
动作
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def hello
render text: "hello, world!" end
end
定义好返回所需字符串的动作之后,我们要告诉 Rails 使用这个动作,不再显示默认的首页(图 1.10)。为此,我们要修改 Rails 路由。路由在控制器之前(图 1.11),决定浏览器发给应用的请求由哪个动作处理。(简单起见,图 1.11 中省略了路由,从 2.2.2 节开始会详细介绍路由。)具体而言,我们要修改默认的首页,也就是根路由。这个路由决定根 URL 显示哪个页面。根 URL 是 http://www.example.com/ 这种形式,所以一般简化使用 /(斜线)表示。
如代码清单 1.9 所示,Rails 应用的路由文件(config/routes.rb
)中有一行注释,说明如何编写根路由。其中,“welcome”是控制器名,“index”是这个控制器中的动作名。去掉这行前面的 #
号,解除注释,这样根路由就可以定义了,然后再把内容替换成代码清单 1.10 中的代码,告诉 Rails 把根路由交给 ApplicationController
中的 hello
动作处理。(在 1.1.2 节说过,竖排的点号表示省略的代码,不要直接复制。)
代码清单 1.9:生成的默认根路由(在注释中)
config/routes.rb
Rails.application.routes.draw do
.
.
.
# You can have the root of your site routed with "root"
# root 'welcome#index' .
.
.
end
代码清单 1.10:设定根路由
config/routes.rb
Rails.application.routes.draw do
.
.
.
# You can have the root of your site routed with "root"
root 'application#hello' .
.
.
end
有了代码清单 1.8 和代码清单 1.10 中的代码,根路由就会按照我们的要求显示“hello, world!”了,如图 1.12 所示。
图 1.12:在浏览器中查看显示“hello, world!”的页面