4.1 导言

从前一章得知,即使完全不懂 Ruby 语言,我们也可以创建 Rails 应用的骨架,以及编写测试。我们依赖于书中提供的测试代码,得到错误信息,然后让测试组件通过。但是我们不能总是这样,所以这一章暂时不讲网站开发,而要正视我们的短肋——Ruby 语言。

前一章末尾我们修改了几乎是静态内容的页面,让它们使用 Rails 布局,去除视图中的重复。我们使用的布局如代码清单 4.1 所示(和代码清单 3.32 一样)。

代码清单 4.1:演示应用的网站布局

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
 <head>
 <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
  <%= stylesheet_link_tag    'application', media: 'all',
                                              'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
 </head>
 <body>
  <%= yield %>
 </body>
</html>

我们把注意力集中在上述代码中的这一行:

<%= stylesheet_link_tag "application", media: "all",
                                       "data-turbolinks-track" => true %>

这行代码使用 Rails 内置的方法 stylesheet_link_tag(详细信息参见 Rails API 文档),在所有媒介类型中引入 application.css。对有经验的 Rails 开发者来说,这行代码看起来很简单,但是其中至少有四个 Ruby 知识点可能会让你困惑:内置的 Rails 方法,调用方法时不用括号,符号和哈希。这几点本章都会介绍。

Rails 除了提供很多内置的方法供我们在视图中使用之外,还允许我们自己定义。自己定义的方法叫辅助方法(helper)。为了说明如何自己定义辅助方法,我们来看看代码清单 4.1 中标题那一行:

<%= yield(:title) %> | Ruby on Rails Tutorial Sample App

这行代码要求每个视图都要使用 provide 方法定义标题,例如:

<% provide(:title, "Home") %>
<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>

那么,如果我们不提供标题会怎样呢?标题一般都包含一个公共部分,如果想更具体些,可以再加上变动的部分。我们在布局中用了个小技巧,基本上已经实现了这样的标题。如果在视图中不调用 provide 方法,也就是不提供变动的部分,那么得到的标题会变成:

 | Ruby on Rails Tutorial Sample App

标题中有公共部分,但前面还显示了竖线。

为了解决这个问题,我们要自定义一个辅助方法,命名为 full_title。如果视图中没有定义页面的标题,full_title 返回标题的公共部分,即“Ruby on Rails Tutorial Sample App”;如果定义了,则在变动部分后面加上一个竖线,如代码清单 4.2 所示。[1]

代码清单 4.2:定义 full_title 辅助方法

app/helpers/application_helper.rb

module ApplicationHelper

  # 根据所在的页面返回完整的标题
  def full_title(page_title = '')
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
end

现在,这个辅助方法定义好了,我们可以用它来简化布局。把下面这行:

<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>

改成:

<title><%= full_title(yield(:title)) %></title>

代码清单 4.3 所示。

代码清单 4.3:使用 full_title 辅助方法的网站布局 GREEN

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
 <head>
 <title><%= full_title(yield(:title)) %></title>
  <%= stylesheet_link_tag    'application', media: 'all',
                                              'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
 </head>
 <body>
  <%= yield %>
 </body>
</html>

为了让这个辅助方法起作用,我们要在首页的视图中把不必要的单词“Home”删掉,只保留标题的公共部分。首先,我们要修改测试代码,如代码清单 4.4 所示,确认标题中没有 "Home"

代码清单 4.4:修改首页的标题测试 RED

test/controllers/static_pages_controller_test.rb

require 'test_helper'

class StaticPagesControllerTest < ActionController::TestCase
  test "should get home" do
    get :home
    assert_response :success
 assert_select "title", "Ruby on Rails Tutorial Sample App"  end

  test "should get help" do
    get :help
    assert_response :success
    assert_select "title", "Help | Ruby on Rails Tutorial Sample App"
  end

  test "should get about" do
    get :about
    assert_response :success
    assert_select "title", "About | Ruby on Rails Tutorial Sample App"
  end
end

我们要运行测试组件,确认有一个测试失败:

代码清单 4.5:RED
$ bundle exec rake test
3 tests, 6 assertions, 1 failures, 0 errors, 0 skips

为了让测试通过,我们要把首页视图中的 provide 那行删除,如代码清单 4.6 所示。

代码清单 4.6:没定义页面标题的首页视图 GREEN

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>

现在测试应该可以通过了:

代码清单 4.7:GREEN
$ bundle exec rake test

(注意,之前运行 rake test 时都显示了通过和失败测试的数量,为了行文简洁,从这以后都会省略这些数据。)

和引入应用的样式表那行代码一样,代码清单 4.2 的内容对有经验的 Rails 开发者来说也很简单,但其中很多重要的 Ruby 知识:模块,方法定义,可选的方法参数,注释,本地变量赋值,布尔值,流程控制,字符串拼接和返回值。本章会一一介绍这些知识。