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 命令 cdmkdir,如果你不熟悉这些命令,可以阅读旁注 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),移动和复制文件(mvcp),以及变换目录浏览文件系统(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 等资源文件变得异常简单。

directory structure rails 3rd edition图 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

cloud9 gemfile图 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.1coffee-rails。也就是说,>= 符号的意思是始终安装最新版;~> 4.0.0 的意思是只安装补丁版本号变化的版本(例如从 4.0.04.0.1),而不安装次版本或主版本的更新(例如从 4.04.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 $IPecho $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 节删除这个页面,替换成我们自己写的首页。

new terminal tab图 1.6:再打开一个终端选项卡rails server new tab图 1.7:在另一个选项卡中运行 Rails 服务器share workspace图 1.8:分享运行在云端工作空间中的本地服务器riding rails 3rd edition图 1.9:执行 rails server 命令后看到的 Rails 默认页面riding rails environment 3rd edition图 1.10:默认页面中显示应用的环境信息

1.3.3 模型-视图-控制器

在初期阶段,概览一下 Rails 应用的工作方式(图 1.11)多少会有些帮助。你可能已经注意到了,在 Rails 应用的标准文件夹结构中有一个文件夹叫 app/图 1.4),其中有三个子文件夹:modelsviewscontrollers。这暗示 Rails 采用了“模型-视图-控制器”(简称 MVC)架构模式,这种模式把“域逻辑”(domain logic,也叫“业务逻辑”(business logic))与图形用户界面相关的输入和表现逻辑强制分开。在 Web 应用中,“域逻辑”一般是“用户”、“文章”和“商品”等数据模型,GUI 则是浏览器中的网页。

mvc schematic图 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 所示。

hello world hello app图 1.12:在浏览器中查看显示“hello, world!”的页面