Rails Tutorialsで勉強のメモ　その3(静的なページ作成)

# やること

* 静的ページを作成
* `Capybara`を使って結合テスト

# 準備

* Railsプロジェクトの作成

```bash
$ cd rails_projects/

# --skip-test-unitでtestディレクトリを作成しない
# 別途rspecでテストを記載する
$ rails new sample_app --skip-test-unit

$ cd sample_app/

$ vim Gemfile
```

* Gemfileの編集

```rb
source 'https://rubygems.org'
ruby '2.0.0'

gem 'rails', '4.0.4'

group :development, :test do
  gem 'sqlite3', '1.3.8'
  gem 'rspec-rails', '2.13.1'
end

group :test do
  gem 'selenium-webdriver', '2.35.1'
  gem 'capybara', '2.1.0'
end

gem 'sass-rails', '4.0.2'
gem 'uglifier', '2.1.1'
gem 'coffee-rails', '4.0.1'
gem 'jquery-rails', '3.0.4'
gem 'turbolinks', '1.1.1'
gem 'jbuilder', '1.0.2'

group :doc do
  gem 'sdoc', '0.3.20', require: false
end

# Heroku用の設定
group :production do
  gem 'pg', '0.15.1'
  gem 'rails_12factor', '0.0.2'
end
```

* `gem`のインストール

```bash
$ bundle install --without production
$ bundle update
$ bundle install
```

* セッション変数の暗号化に使用するための、秘密トークンの設定
    * `.gitignore`で`.secret`をリポジトリで公開しないようにする

```rb:config/initializers/secret_token.rb
require 'securerandom'

def secure_token
  token_file = Rails.root.join('.secret')
  if File.exist?(token_file)
    # 既に.secretファイルが存在している場合はそのまま使用
    File.read(token_file).chomp
  else
    # 動的にトークンを生成
    token = SecureRandom.hex(64)
    File.write(token_file, token)
    token
  end
end

SampleApp::Application.config.secret_key_base = secure_token
```

* テストで使用するツールの変更
    * `Test::Unit`の代わりに`Rspec`を使用

```bash
# .rspecファイルとspecディレクトリ、spec/spec_helper.rbが作成される
$ rails generate rspec:install
```

* リポジトリの初期化

```bash
$ git init
$ git add .
$ git commit -m "Initial commit"
```

* READMEの変更
    * 変更内容は割愛
* READMEファイルをMarkdown形式に変更してコミット

```bash
$ git mv README.{rdoc,md}
$ git commit -am "Improve the README"
```

* サンプルアプリケーションをGitHubにpush
    * 事前にGitHub上にリポジトリ作成しておく

```bash
$ git remote add origin https://asam-3@github.com/ASAM-3/sample_app.git
$ git push -u origin master
```

* Herokuにデプロイ

```bash
$ heroku create
$ git push heroku master
$ heroku run rake db:migrate
```

* Herokuのデプロイに失敗した時の対応
    * ログの確認
    * プリコンパイルしてからpush

```bash
# ログの確認
$ heroku logs

# プリコンパイルしてからpush
$ rake assets:precompile
$ git add .
$ git commit -m "Add precompiled assets for Heroku"
$ git push heroku master
```

* 定期的なGitHubへのpushやHerokuへのデプロイをすべき

```bash
$ git push
$ git push heroku
$ heroku run rake db:migrate
```

# 静的ページ

* トピックブランチの作成

```bash
$ git checkout -b static-pages
```

* StaticPagesコントローラの作成
    * `home`と`help`というアクションを作成
    * 今回は`rspec`のテストをしないため、`--no-test-framework`オプションをつける

```bash
$ rails generate controller StaticPages home help --no-test-framework
      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  helper
      create    app/helpers/static_pages_helper.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/static_pages.js.coffee
      invoke    scss
      create      app/assets/stylesheets/static_pages.css.scss

# なお、下記コマンドでもstatic_pages_controller.rbを作成できる
# キャメル記法→スネークケースに変換される
$ rails generate controller static_pages

# 自動生成を取り消ししたい場合はrails destroyを使う
$ rails destroy controller StaticPages
```

* 自動生成されたRailsルータの確認
    * `home`アクションと`help`アクションのルーティングが記載されている
    * `get`はGETリクエストを表す
    * `/static_pages/home`、`/static_pages/help`でそれぞれアクセスできる

```rb:config/routes.rb
SampleApp::Application.routes.draw do
  get "static_pages/home"
  get "static_pages/help"
end
```

* 自動生成されたコントローラ
    * 生成時に指定した`home`アクションと`help`アクションが生成されている
    * `REST`ではない
    * 静的なページを扱う場合には向いている

* `REST`(REpresentational State Transfer)
    * インターネットやWebアプリケーションなどの、分散／ネットワーク化されたシステムやアプリケーションを構築するためのアーキテクチャの一つ
    * Railsアプリにおいては、アプリケーションを構成するコンポーネントをリソースとしてモデル化すること
        * その２で言うところの`user`や`micropost`
* RESTfulなスタイルを採用することで得られるメリット
    * 作成すべきコントローラやアクションの決定が容易になる

```rb:app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
  end

  def help
  end
end
```

* `home`メソッドと`help`メソッド
    * 実装が空だが、Railsの場合は`ApplicationController`クラスを継承しているので、Rails特有の振る舞いをする
    * `/static_pages/home`にアクセスした場合の振る舞い
        1. `home`メソッドに記述されているコードを実行
        2. アクションに対応するビューを出力
            * `/app/views/static_pages/home.html.erb`

* 自動生成されたビュー

```erb:app/views/static_pages/home.html.erb
<h1>StaticPages#help</h1>
<p>Find me in app/views/static_pages/help.html.erb</p>
```

* ここまででコミット

```bash
$ git add .
$ git commit -m "Add a StaticPages controller"
```

# テスト

* 振舞い駆動開発(Behavior-Driven Development, BDD)
    * アプリケーションの振舞いをテストしながら実装

* 結合テスト
    * ユーザがアプリケーションを使う際の一連のアクションをシミュレーション
    * アプリケーションの各ページが正常に動作するかテスト
    * 結合テストのためのツール
        * `Capybara`
        * `Cucumber`

* テスト駆動開発(TDD)
    * アプリケーションを開発する際に最初にテストを作成し、次にコードを作成

## テスト駆動開発

* テスト駆動開発の用語
    * `Red`：失敗するテスト
    * `Green`：テストにパスするコード
    * リファクタリング：動作を変えずにコードを改善、冗長なコードの削除
* 上記の順に開発を行っていく
    * このサイクルのことを`Red/Green/Refactor`という

* 結合テストの生成

```bash
$ rails generate integration_test static_pages
      invoke  rspec
      create    spec/requests/static_pages_spec.rb
```

* テストコードの記述
    * `describe`
        * テストの説明を引数として渡す
    * `visit`
        * `Capybara`の`visit`機能を使い、ブラウザでの`/static_pages/home`へのアクセスをシミュレート
    * `expect`
        * `Capybara`が提供する`page`変数を使用
        * `have_content`の引数がコンテンツに含まれているかをテスト

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end
  end
end
```

* `spec/spec_helper.rb`の変更
    * `Capybara DSL`を`RSpec`ヘルパーファイルに追加

```rb:spec/spec_helper.rb
RSpec.configure do |config|
  config.include Capybara::DSL
end
```

* テストの実行
    * 現時点ではエラーになる

```bash
$ bundle exec rspec spec/requests/static_pages_spec.rb
F

Failures:

  1) StaticPages Home page should have the content 'Sample App'
     Failure/Error: expect(page).to have_content('Sample App')
       expected #has_content?("Sample App") to return true, got false
     # ./spec/requests/static_pages_spec.rb:7:in `block (3 levels) in <top (required)>'

Finished in 0.68511 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/requests/static_pages_spec.rb:5 # StaticPages Home page should have the content 'Sample App'

Randomized with seed 27019
```

* `home.html.erb`を修正
    * 本文の中に`Sample App`という文言を追加

```rb:app/views/static_pages/home.html.erb
<h1>Sample App</h1>
<p>
    This is the home page for the
    <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
    sample application.
</p>
```

* テストを再実行
    * 今度は成功

```bash
$ bundle exec rspec spec/requests/static_pages_spec.rb
.

Finished in 0.10047 seconds
1 example, 0 failures

Randomized with seed 39422
```

* Helpページのテストコードを追加

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end
  end

  # ここから追加
  describe "Help page" do
    it "should have the content 'Help'" do
      visit '/static_pages/help'
      expect(page).to have_content('Help')
    end
  end
  # ここまで追加
end
```

* テストを実行
    * `Help`部分がエラー

```bash
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
F.
```

* `help.html.erb`を修正
    * 本文に`Help`を追加

```erb:app/views/static_pages/help.html.erb
<h1>Help</h1>
<p>
  Get help on the Ruby on Rails Tutorial at the
  <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
  To get help on this sample app. see the
  <a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
</p>
```

* 再度テストを実施
    * 今度は成功

```bash
$ bundle exec rspec spec/requests/static_pages_spec.rb
..
```

## ページの追加

*　Aboutページを作成
*　まずはテストコードを記載
*　そしてテスト実行
    *　`about`へのルートが設定されていないためエラー

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end
  end

  describe "Help page" do
    it "should have the content 'Help'" do
      visit '/static_pages/help'
      expect(page).to have_content('Help')
    end
  end

  # ここから追加
  describe "About page" do
    it "should have the content 'About Us'" do
      visit '/static_pages/about'
      expect(page).to have_content('About Us')
    end
  end
  # ここまで追加
end
```

```bash:テスト実行(ルートがないためエラー)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
F..

Failures:

  1) StaticPages About page should have the content 'About Us'
     Failure/Error: visit '/static_pages/about'
     ActionController::RoutingError:
       No route matches [GET] "/static_pages/about"
     # ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'
```

* `routes`ファイルに`/static_pages/about`へのルートを追加してテスト
    * `StaticPagesController`に`about`アクションがないためエラー

```rb:config/routes.rb
SampleApp::Application.routes.draw do
  get "static_pages/home"
  get "static_pages/help"
  ## ここから追加
  get "static_pages/about"
  ## ここまで追加
end
```

```bash:テスト実行(aboutアクションが存在しないためエラー)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
..F

Failures:

  1) StaticPages About page should have the content 'About Us'
     Failure/Error: visit '/static_pages/about'
     AbstractController::ActionNotFound:
       The action 'about' could not be found for StaticPagesController
     # ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'
```

* `StaticPagesController`に`about`アクションを追加
    * ビューなどのテンプレートが見つからない

```rb:app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
  end

  def help
  end

  # ここから追加
  def about
  end
  # ここまで追加
end
```

```bash:テスト実行(ビューなどのテンプレートが見つからないためエラー)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
F..

Failures:

  1) StaticPages About page should have the content 'About Us'
     Failure/Error: visit '/static_pages/about'
     ActionView::MissingTemplate:
       Missing template static_pages/about, application/about with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in:
         * "/home/asam-3/rails_projects/sample_app/app/views"
     # ./spec/requests/static_pages_spec.rb:20:in `block (3 levels) in <top (required)>'
```

* `about`ビューを追加
    * テスト成功

```rb:app/views/static_pages/about.html.erb
<h1>About Us</h1>
<p>
  The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
  is a project to make a book and screencasts to teach web development
  with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This
  is the sample application for the tutorial.
</p>
```

```bash:テスト実施(成功)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
...

Finished in 0.13243 seconds
3 examples, 0 failures
```

* リファクタリング
    * この時点では特になし

### 新規ページ追加時の手順

1. 新規ページのテストコード追加
2. `config/routes`にルーティングを追加
3. コントローラにアクションを追加
4. ビューを追加

## 動的なページ

* タイトル変更のテスト
    * ページ毎に異なるタイトルが表示されるように変更
* 作業手順
    1. タイトルのテストを作成
    2. タイトルを追加
    3. レイアウトファイルを使ったリファクタリングと重複の削除を実施

* レイアウトファイルのリネーム
    * 後でレイアウトファイルの役割を確認するため、一旦無効にする

```bash:レイアウトファイルのりネーム
$ mv app/views/layouts/application.html.erb{,_bk}
```

* タイトルは下記のように設定

| URL | タイトル |
| --- | --- |
| /static_pages/home | Ruby on Rails Tutorial Sample App - Home |
| /static_pages/help | Ruby on Rails Tutorial Sample App - Help |
| /static_pages/about | Ruby on Rails Tutorial Sample App - About Us|

* テストコードの記載とテスト実行
    * もちろんエラーになる

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do
  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end

    # ここから追加
    it "should have the title 'Home'" do
      visit '/static_pages/home'
      expect(page).to have_title("Home")
    end
    # ここまで追加
  end

  describe "Help page" do
    it "should have the content 'Help'" do
      visit '/static_pages/help'
      expect(page).to have_content('Help')
    end

    # ここから追加
    it "should have the title 'Help'" do
      visit '/static_pages/help'
      expect(page).to have_title("Help")
    end
    # ここまで追加
  end

  describe "About page" do
    it "should have the content 'About Us'" do
      visit '/static_pages/about'
      expect(page).to have_content('About Us')
    end
    
    # ここから追加
    it "should have the title 'About'" do
      visit '/static_pages/about'
      expect(page).to have_title("About")
    end
    # ここまで追加
  end
end
```

```bash:テスト実施(タイトル部分がエラー)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
.F.F.F
```

* 各ビューにタイトルを追加
    * テスト成功
    * ただ、同じようなコードが繰り返されているため、保守性が低い

```html:app/views/static_pages/home.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App - Home</title>
  </head>
  <body>
    <h1>Sample App</h1>
    <p>
        This is the home page for the
        <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
        sample application.
    </p>
  </body>
</html>
```

```html:app/views/static_pages/help.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App - Help</title>
  </head>
  <body>
    <h1>Help</h1>
    <p>
      Get help on the Ruby on Rails Tutorial at the
      <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
      To get help on this sample app. see the
      <a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
    </p>
  </body>
</html>
```

```html:app/views/static_pages/about.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App - About Us</title>
  </head>
  <body>
    <h1>About Us</h1>
    <p>
      The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
      is a project to make a book and screencasts to teach web development
      with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This
      is the sample application for the tutorial.
    </p>
  </body>
</html>
```

```bash:テスト実施(成功)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
......

Finished in 0.08417 seconds
6 examples, 0 failures
```

* リファクタリング
    * ページのタイトルがほぼ同じ
    * `Ruby on Rails Tutorial Sample App`が3つのタイトルで共通している
    * HTMLの構造全体が各ページで重複している
* DRY(`Don't Repeat Yourself`)に反している

* 埋め込みRubyの利用
    * Railsの`provide`関数を使用して、タイトルをページ毎に変更
    * `ERb`はWebページに動的な要素を加えるときに使うテンプレートシステム
* まずは`home.html.erb`だけ変更
    * 変更後にテストを実施して成功することを確認

```erb:app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %>
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App - <%= yield(:title) %></title>
  </head>
  <body>
    <h1>Sample App</h1>
    <p>
        This is the home page for the
        <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
        sample application.
    </p>
  </body>
</html>
```

* `provide`関数
    * `:title`というラベルに'Home'という文字列を関連付ける
* `yield`関数
    * テンプレートにタイトルを挿入

```bash:テスト実行(成功)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
......

Finished in 0.08411 seconds
6 examples, 0 failures
```

* `help`、`about`も同じような感じで記述できる
* 各ERbファイルの構成は下記のとおり
    * `html`、`head`の構成は全て共通
    * `body`の中身だけ異なる

```erb
<% provide(:title, 'タイトル') %>
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App - <%= yield(:title) %></title>
  </head>
  <body>
    中身
  </body>
</html>
```

* レイアウトファイルの変更
    * 元のファイルからタイトル部分だけ変更

```bash:レイアウトファイルのリネーム
$ mv app/views/layouts/application.html.erb{_bk,}
```

* `<% yield %>`
    * 各ページの内容をレイアウトに挿入
    * `/static_pages/home`にアクセスすると、`home.html.erb`の内容がHTMLに変換され、`<%= yield %>`の位置に挿入される
* `<%= stylesheet_link_tag ... %>`
    * スタイルシートをインクルード
    * `Asset Pipeline`の一部
* `<%= javascript_include_tag "application", ... %>`
    * JavaScriptをインクルード
    * `Asset Pipeline`の一部
* `<%= csrf_meta_tags %>`
    * クロスサイトリクエストフォージェリを防ぐために使用する`Rails`のメソッド

```erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
  <title>Ruby on Rails Tutorial Sample App - <%= 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>
```

* 各種ビューをレイアウトを利用した形に変更
* リファクタリング後にテスト実施

```erb:app/views/static_pages/home.html.erb
<% provide(:title, 'Home') %>
<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
  sample application.
</p>
```

```erb:app/views/static_pages/help.html.erb
<% provide(:title, 'Help') %>
<h1>Help</h1>
<p>
  Get help on the Ruby on Rails Tutorial at the
  <a href="http://railstutorial.jp/help">Rails Tutorial help page</a>.
  To get help on this sample app. see the
  <a href="http://railstutorial.jp/book">Rails Tutorial book</a>.
</p>
```

```erb:app/views/static_pages/about.html.erb
<% provide(:title, 'About Us') %>
<h1>About Us</h1>
<p>
  The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a>
  is a project to make a book and screencasts to teach web development
  with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This
  is the sample application for the tutorial.
</p>
```

```bash:テスト実行(成功)
$ bundle exec rspec spec/requests/static_pag
es_spec.rb
......

Finished in 0.16055 seconds
6 examples, 0 failures
```

# コミットとpush

* コミット
* `master`ブランチとマージ
* GitHubにアップロード
* herokuにアップロード

```bash
$ git add .
$ git commit -m "Finish static pages"
$ git checkout master
$ git merge static-pages
$ git push
$ git push heroku
```

# 演習

## Contactページを作成

1. テストコード作成
2. `config/routes.rb'に`contact`のルーティングを追加
3. `static_pages_controller.rb`に`contact`アクションを追加
4. `contact.html.erb`を追加

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do
  describe "Contact page" do
    it "should have the content 'Contact'" do
      visit '/static_pages/contact'
      expect(page).to have_content('Contact')
    end

    it "should have the title 'Contact'" do
      visit '/static_pages/contact'
      expect(page).to have_title("Contact")
    end
  end
end
```

```rb:config/routes.rb
SampleApp::Application.routes.draw do
  get "static_pages/home"
  get "static_pages/help"
  get "static_pages/about"
  get "static_pages/contact"
end
```

```rb:app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
  end

  def help
  end

  def about
  end

  def contact
  end
end
```

```rb:app/views/static_pages/contact.html.erb
<% provide(:title, 'Contact') %>
<h1>Contact</h1>
<p>
  Contact Ruby on Rails Tutorial about the sample app at the
  <a href="http://railstutorial.jp/contact">contact page</a>.
</p>
```

## specテストの重複部分を`let`を使ってまとめる

1. タイトルの文字列全体をテストするように変更
    * `let`で共通部分を定義
    * テスト部分で文字列連結してテスト

```rb:spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "StaticPages" do

  let(:base_title) { "Ruby on Rails Tutorial Sample App" }

  describe "Home page" do
    it "should have the content 'Sample App'" do
      visit '/static_pages/home'
      expect(page).to have_content('Sample App')
    end

    it "should have the title 'Home'" do
      visit '/static_pages/home'
      expect(page).to have_title("#{base_title} - Home")
    end
  end
  # 中略
end
```

# その他のセットアップ

## `bundle exec`を使わない

* `bundle exec`
    * 現在の環境に依存するgemをGemfileから読み込んでプログラムを実行するために指定が必要
    * `RVM`を使用している場合、RVMを最新化することにより、インストールされたgemが自動的に適切なBundlerの環境で実行される(1.11.x以降)
* `rake`や`rspec`を実行する際に指定していたが、今後は使わない

```bash:rvmの最新化
$ rvm get stable
$ rvm -v
```

```bash:rspecの実行
$ rspec spec/
```

## `Guard`によるテストの自動化

* `Guard`
    * テストを自動化する`gem`
    * ファイルシステムの変更を監視し、ファイルが変更されると自動実行するようにできる
        * specファイル：`*_spec.rb`
        * viewファイル：`*.html.erb`　など
* `Guard`のセットアップ
    1. `Gemfile`に`guard-rspec`を追加
    2. gemのインストール
    3. `Guard`の初期化
    4. `Guardfile`の編集

```rb:Gemfile
# 変更箇所の抜粋
group :development, :test do
  gem 'sqlite3', '1.3.8'
  gem 'rspec-rails', '2.13.1'
  # ここから追加
  gem 'guard-rspec', '2.5.0'
  # ここまで追加
end
```

```bash:gemのインストール、Guardの初期化
$ bundle install

# Guardfileが生成される
$ bundle exec guard init rspec
```

* `guard 'rspec', all_after_pass: false do`
    * 失敗したテストがある場合は以降のテストを実施しない

```rb:Guardfile(追加・変更点のみ)
require 'active_support/inflector'

guard 'rspec', all_after_pass: false do

  # 中略

  # Custom Rails Tutorial specs
  watch(%r{^app/controllers/(.+)_(controller)\.rb$}) do |m|
    ["spec/routing/#{m[1]}_routing_spec.rb",
     "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb",
     "spec/acceptance/#{m[1]}_spec.rb",
     (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
                       "spec/requests/#{m[1].singularize}_pages_spec.rb")]
  end

  watch(%r{^app/views/(.+)/}) do |m|
    (m[1][/_pages/] ? "spec/requests/#{m[1]}_spec.rb" :
                      "spec/requests/#{m[1].singularize}_pages_spec.rb")
  end

  watch(%r{^app/controllers/sessions_controller\.rb$}) do |m|
    "spec/requests/authentication_pages_spec.rb"
  end
end
```

## Sporkを使ったテストの高速化

* `rspec`はテスト開始までに時間がかかってしまう
    * `rspec`を実行するたびにRailsの環境全体を読み込みなおす必要があるため
* `Spork`テストサーバ
    * 環境を1回だけ読み込み、今後実行するテストのためのプロセスを管理する

* `Spork`のセットアップ手順
    1. Gemfileに`Spork`を追加
    2. `gem`のインストール
    3. `Spork`の設定に`bootstrap`を指定
    4. `RSpec`の設定を変更
        * 環境の読み込みを1回で済ますため、`spec/spec_helper.rb`の中で環境を`prefork`のブロックで読み込むようにする
    5. テストスイートを実行し、基準となる実行時間を測定
    6. `Spork`サーバを起動
    7. `.rspec`の設定を変更

```rb:Gemfile(変更箇所のみ抜粋)
group :development, :test do
  gem 'sqlite3', '1.3.8'
  gem 'rspec-rails', '2.13.1'
  gem 'guard-rspec', '2.5.0'
  gem 'spork-rails', '4.0.0'
  gem 'guard-spork', '1.5.0'
  gem 'childprocess', '0.3.6'
end
```

```bash:gemのインストール
$ bundle install
$ bundle update
```

```bash:Sporkの設定にbootstrapを指定
$ bundle exec spork --bootstrap
```

```rb:spec/spec_helper.rb(環境をpreforkのブロックで読み込む)
require 'rubygems'
require 'spork'

Spork.prefork do
  # 既存のコードをカット＆ペースト
  ENV["RAILS_ENV"] ||= 'test'

  # 中略

    config.include Capybara::DSL
  end
end

Spork.each_run do
  # This code will be run each time you run your specs.

end
```

```bash:rspecの実行時間を計測
$ time bundle exec rspec spec/requests/static_pages_spec.rb
........

Finished in 0.19846 seconds
8 examples, 0 failures

Randomized with seed 25711


real    0m4.395s # テストスイートの時間
user    0m3.818s 
sys     0m0.517s # テストの実行時間
```

```bash:別ターミナルでSporkサーバを起動
$ cd rails_projects/sample_app/

$ bundle exec spork
Using RSpec, Rails
Preloading Rails environment
Loading Spork.prefork block...
Spork is ready and listening on 8989!
```

```bash:--drbオプションをつけてrspecの実行時間を計測
$ time bundle exec rspec spec/requests/static_pages_spec.rb --drb
........

Finished in 0.32188 seconds
8 examples, 0 failures

Randomized with seed 8842


real    0m1.633s
user    0m1.037s
sys     0m0.104s
```

```bash:--drbオプションを明示的に指定しないように、.rspecに設定を追加
$ sed -i -e '/--drb/d' .rspec
$ echo "--drb" >> .rspec

# --drbなしでも高速で終了
$ time bundle exec rspec spec/requests/static_pages_spec.rb
........

Finished in 0.20508 seconds
8 examples, 0 failures

Randomized with seed 8842


real    0m1.218s
user    0m0.786s
sys     0m0.085s
```

### GuardにSporkを導入

```bash:SporkとGuardを併用
$ bundle exec guard init spork
16:23:28 - INFO - spork guard added to Guardfile, feel free to edit it
```

* `bundle exec guard init spork`を実行すると、`Guardfile`に設定が追加される
* `guard`に`--drb`の設定を追加
    * `rspec`が高速化

```rb:Guardfile
# 自動生成された部分
guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
  watch('config/application.rb')
  watch('config/environment.rb')
  watch('config/environments/test.rb')
  watch(%r{^config/initializers/.+\.rb$})
  watch('Gemfile')
  watch('Gemfile.lock')
  watch('spec/spec_helper.rb') { :rspec }
  watch('test/test_helper.rb') { :test_unit }
  watch(%r{features/support/}) { :cucumber }
end

# cli: '--drb'を追加
guard 'rspec', all_after_pass: false, cli: '--drb' do
  ...
end
```

```bash:GuardとSporkを同時に起動
$ bundle exec guard
```