Ruby on Rails チュートリアル

実例を使って Rails を学ぼう

第2版 目次

  1. 第1章 ゼロからデプロイまで
    1. 1.1はじめに
      1. 1.1.1読者の皆さまへ
      2. 1.1.2 Railsとスケールについて
      3. 1.1.3この本における取り決め
    2. 1.2 さっそく動作させる
      1. 1.2.1開発環境
        1. IDE
        2. テキストエディタとコマンドライン
        3. ブラウザ
        4. 使用するツールについて
      2. 1.2.2Ruby、RubyGems、Rails、Git
        1. Railsインストーラ (Windows)
        2. Gitのインストール
        3. Rubyのインストール
        4. RubyGemsのインストール
        5. Railsのインストール
      3. 1.2.3最初のアプリケーション
      4. 1.2.4 Bundler
      5. 1.2.5 rails server
      6. 1.2.6Model-view-controller (MVC)
    3. 1.3Gitによるバージョン管理
      1. 1.3.1インストールとセットアップ
        1. 初めてのシステムセットアップ
        2. 初めてのリポジトリセットアップ
      2. 1.3.2追加とコミット
      3. 1.3.3Gitのメリット
      4. 1.3.4 GitHub
      5. 1.3.5ブランチ (branch)、変更 (edit)、 コミット (commit)、マージ (merge)
        1. ブランチ (branch)
        2. 変更 (edit)
        3. コミット (commit)
        4. マージ (merge)
        5. プッシュ (push)
    4. 1.4デプロイする
      1. 1.4.1 Herokuのセットアップ
      2. 1.4.2 Herokuにデプロイする (1)
      3. 1.4.3 Herokuにデプロイする (2)
      4. 1.4.4 Heroku コマンド
    5. 1.5 最後に
  2. 第2章デモアプリケーション
    1. 2.1 アプリの計画
      1. 2.1.1ユーザーのモデル設計
      2. 2.1.2マイクロポストのモデル設計
    2. 2.2Users リソース
      1. 2.2.1ユーザーページを探検する
      2. 2.2.2 MVCの挙動
      3. 2.2.3Users リソースの欠点
    3. 2.3Microposts リソース
      1. 2.3.1マイクロポストのページを探検する
      2. 2.3.2マイクロポストをマイクロにする
      3. 2.3.3ユーザーとマイクロポストをhas_manyで関連づける
      4. 2.3.4継承の階層
      5. 2.3.5デモアプリケーションのデプロイ
    4. 2.4最後に
  3. 第3章ほぼ静的なページの作成
    1. 3.1静的ページ
      1. 3.1.1「本当に」静的なページ
      2. 3.1.2Railsによる静的なページ
    2. 3.2最初のテスト
      1. 3.2.1テスト駆動開発
      2. 3.2.2ページの追加
        1. 赤 (Red)
        2. 緑 (Green)
        3. リファクタリング
    3. 3.3少しだけ動的なページ
      1. 3.3.1タイトル変更をテストする
      2. 3.3.2タイトルのテストをパスさせる
      3. 3.3.3埋め込みRuby
      4. 3.3.4レイアウトを使って重複を解消する
    4. 3.4最後に
    5. 3.5演習
    6. 3.6高度なセットアップ
      1. 3.6.1bundle execを追放する
        1. RVM Bundler の統合
        2. binstubsオプション
      2. 3.6.2Guardによるテストの自動化
      3. 3.6.3Spork を使ったテストの高速化
        1. GuardにSporkを導入する
      4. 3.6.4Sublime Text上でテストする
  4. 第4章 Rails風味のRuby
    1. 4.1動機
    2. 4.2文字列(string)とメソッド
      1. 4.2.1コメント
      2. 4.2.2文字列
        1. 出力
        2. シングルクォート内の文字列
      3. 4.2.3オブジェクトとメッセージ受け渡し
      4. 4.2.4メソッドの定義
      5. 4.2.5 title ヘルパー、再び
    3. 4.3他のデータ構造
      1. 4.3.1配列と範囲演算子
      2. 4.3.2ブロック
      3. 4.3.3ハッシュとシンボル
      4. 4.3.4 CSS、再び
    4. 4.4 Ruby におけるクラス
      1. 4.4.1コンストラクタ
      2. 4.4.2クラス継承
      3. 4.4.3組込みクラスの変更
      4. 4.4.4コントローラクラス
      5. 4.4.5ユーザークラス
    5. 4.5最後に
    6. 4.6演習
  5. 第5章レイアウトを作成する
    1. 5.1構造を追加する
      1. 5.1.1ナビゲーション
      2. 5.1.2BootstrapとカスタムCSS
      3. 5.1.3パーシャル (partial)
    2. 5.2SassとAsset Pipeline
      1. 5.2.1Asset Pipeline
        1. アセットディレクトリ
        2. マニフェストファイル
        3. プリプロセッサエンジン
        4. 本番環境での効率性
      2. 5.2.2素晴らしい構文を備えたスタイルシート
        1. ネスト
        2. 変数
    3. 5.3レイアウトのリンク
      1. 5.3.1 ルートのテスト
      2. 5.3.2 Railsのルート
      3. 5.3.3名前付きルート
      4. 5.3.4RSpecを洗練させる
    4. 5.4ユーザー登録: 最初のステップ
      1. 5.4.1ユーザーコントローラ
      2. 5.4.2ユーザー登録URI
    5. 5.5最後に
    6. 5.6演習
  6. 第6章ユーザーのモデルを作成する
    1. 6.1 Userモデル
      1. 6.1.1データベースの移行
      2. 6.1.2modelファイル
        1. モデル注釈
        2. アクセス可能な属性
      3. 6.1.3ユーザーオブジェクトを作成する
      4. 6.1.4ユーザーオブジェクトを検索する
      5. 6.1.5ユーザーオブジェクトを更新する
    2. 6.2ユーザーを検証する
      1. 6.2.1最初のユーザーテスト
      2. 6.2.2プレゼンスを検証する
      3. 6.2.3長さを検証する
      4. 6.2.4フォーマットを検証する
      5. 6.2.5一意性を検証する
        1. 一意性の警告
    3. 6.3セキュアなパスワードを追加する
      1. 6.3.1暗号化されたパスワード
      2. 6.3.2パスワードと確認
      3. 6.3.3ユーザー認証
      4. 6.3.4ユーザーがセキュアなパスワードを持っている
      5. 6.3.5ユーザーを作成する
    4. 6.4最後に
    5. 6.5演習
  7. 第7章ユーザー登録
    1. 7.1ユーザーを表示する
      1. 7.1.1デバッグとRails環境
      2. 7.1.2ユーザーリソース
      3. 7.1.3ファクトリーを使用してユーザー表示ページをテストする
      4. 7.1.4gravatar画像とサイドバー
    2. 7.2ユーザー登録フォーム
      1. 7.2.1ユーザー登録のためのテスト
      2. 7.2.2form_forを使用する
      3. 7.2.3フォームHTML
    3. 7.3ユーザー登録失敗
      1. 7.3.1正しいフォーム
      2. 7.3.2ユーザー登録のエラーメッセージ
    4. 7.4ユーザー登録成功
      1. 7.4.1登録フォームの完成
      2. 7.4.2flash
      3. 7.4.3実際のユーザー登録
      4. 7.4.4 SSLを導入して本番環境をデプロイする
    5. 7.5最後に
    6. 7.6演習
  8. 第8章サインイン、サインアウト
    1. 8.1セッション、サインインの失敗
      1. 8.1.1Sessionコントローラ
      2. 8.1.2サインインをテストする
      3. 8.1.3サインインのフォーム
      4. 8.1.4確認フォームを送信する
      5. 8.1.5フラッシュメッセージを表示する
    2. 8.2、サインイン成功
      1. 8.2.1[このアカウント設定を保存する]
      2. 8.2.2正しいsign_inメソッド
      3. 8.2.3現在のユーザー
      4. 8.2.4レイアウトリンクを変更する
      5. 8.2.5ユーザー登録と同時にサインインする
      6. 8.2.6サインアウトする
    3. 8.3Cucumberの紹介 (オプション)
      1. 8.3.1インストールと設定
      2. 8.3.2フィーチャーとステップ
      3. 8.3.3対比: RSpecのカスタムマッチャー
    4. 8.4最後に
    5. 8.5演習
  9. 第9章 ユーザーの更新・表示・削除
    1. 9.1ユーザーを更新する
      1. 9.1.1編集フォーム
      2. 9.1.2編集の失敗
      3. 9.1.3編集の成功
    2. 9.2認可
      1. 9.2.1ユーザーのサインインを要求する
      2. 9.2.2正しいユーザーを要求する
      3. 9.2.3フレンドリーフォワーディング
    3. 9.3すべてのユーザーを表示する
      1. 9.3.1ユーザーインデックス
      2. 9.3.2サンプルのユーザー
      3. 9.3.3ページネーション
      4. 9.3.4パーシャルのリファクタリング
    4. 9.4ユーザーを削除する
      1. 9.4.1管理ユーザー
        1. attr_accessible属性再び
      2. 9.4.2 destroyアクション
    5. 9.5最後に
    6. 9.6演習
  10. 第10章ユーザーのマイクロポスト
    1. 10.1Micropostモデル
      1. 10.1.1基本的なモデル
      2. 10.1.2Accessible属性と最初の検証
      3. 10.1.3User/Micropostの関連付け
      4. 10.1.4マイクロポストを改良する
        1. デフォルトのスコープ
        2. Dependent: destroy
      5. 10.1.5コンテンツの検証
    2. 10.2マイクロポストを表示する
      1. 10.2.1ユーザー表示ページの拡張
      2. 10.2.2マイクロポストのサンプル
    3. 10.3マイクロポストを操作する
      1. 10.3.1アクセス制御
      2. 10.3.2マイクロポストを作成する
      3. 10.3.3フィードの原型
      4. 10.3.4マイクロポストを削除する
    4. 10.4最後に
    5. 10.5演習
  11. 第11章ユーザーをフォローする
    1. 11.1Relationshipモデル
      1. 11.1.1データモデルの問題 (および解決策)
      2. 11.1.2User/relationshipの関連付け
      3. 11.1.3検証
      4. 11.1.4フォローしているユーザー
      5. 11.1.5フォロワー
    2. 11.2フォローしているユーザー用のWebインターフェイス
      1. 11.2.1フォローしているユーザーのサンプルデータ
      2. 11.2.2統計とフォロー用フォーム
      3. 11.2.3「フォローしているユーザー」ページと「フォロワー」ページ
      4. 11.2.4[フォローする] ボタン (標準的な方法)
      5. 11.2.5[フォローする] ボタン (Ajax)
    3. 11.3ステータスフィード
      1. 11.3.1動機と計画
      2. 11.3.2フィードを初めて実装する
      3. 11.3.3サブセレクト
      4. 11.3.4新しいステータスフィード
    4. 11.4最後に
      1. 11.4.1サンプルアプリケーションの機能を拡張する
        1. 返信機能
        2. メッセージ機能
        3. フォロワーの通知
        4. パスワードリマインダー
        5. ユーザー登録の確認
        6. RSSフィード
        7. REST API
        8. 検索機能
      2. 11.4.2読み物ガイド
    5. 11.5演習
  12. 第12章Rails 4.0へのアップグレード
    1. 12.1Rails 3.2からRails 4.0へのアップグレード
      1. 12.1.1Rails 4.0のセットアップ
      2. 12.1.2テストをパスするようにする
      3. 12.1.3特定の問題
        1. モデル
        2. コントローラとビュー
      4. 12.1.4仕上げ
      5. 12.1.5追加機能
    2. 12.2Strong parameters
    3. 12.3セキュリティのアップデート
      1. 12.3.1秘密鍵
      2. 12.3.2記憶トークンを暗号化する

前書き

私が前にいた会社 (CD Baby) は、かなり早い段階で Ruby on Rails に一度乗り換えたのですが、残念ながらまた PHP に戻ってしまいました (詳細は私の名前を Google で検索してみてください)。そんな私ですが、Michael Hartl 氏の本を強く勧められたので、その本を使ってもう一度試してみた結果、今度は無事に Rails に乗り換えることができました。それがこの Ruby on Rails チュートリアルという本です。

私は多くの Rails 関連の本を参考にしてきましたが、真の決定版と呼べるものは本書をおいて他にありません。本書では、あらゆる手順が Rails 流で行われています。最初のうちは慣れるまでに時間がかかりましたが、この本を終えた今、ついにこれこそが自然な方式だと感じられるまでになりました。また、本書は Rails 関連の本の中で唯一、多くのプロが推奨するテスト駆動開発 (TDD: Test Driven Development) を、全編を通して実践しています。実例を使ってここまで分かりやすく解説された本は、本書が初めてでしょう。極めつけは、Git や GitHub、Heroku の実例に含めている点です。このような、実際の開発現場で使わているツールもチュートリアルに含まれているため、読者は、まるで実際のプロジェクトの開発プロセスを体験しているかのような感覚が得られるはずです。それでいて、それぞれの実例が独立したセクションになっているのではなく、そのどれもがチュートリアルの内容と見事に一体化しています。

本書は、筋道だった一本道の物語のようになっています。私自身、章の終わりにある練習問題もやりながら、この Rails チュートリアルを3日間かけて一気に読破しました。最初から最後まで、途中を飛ばさずに読んでください。それが、最も有益な本書の読み方です。

それでは、楽しんでお読みください!

デレックシバーズ (Derek Sivers) (sivers.org)
CD Baby 創始者

謝辞

Ruby on Rails チュートリアルは、私の以前の著書「RailsSpace」と、その時の共著者の Aurelius Prochazka から多くのことを参考にさせてもらっています。Aure には、RailsSpace での協力と本書への支援も含め、感謝したいと思います。また、RailsSpaceRails チュートリアルの両方の編集を担当して頂いた Debra Williams Cauley 氏にも謝意を表したく思います。彼女が野球の試合に連れて行ってくれる限り、私は本を書き続けるでしょう。

私にインスピレーションと知識を与えてくれた Rubyist の方々にも感謝したいと思います: David Heinemeier Hansson、 Yehuda Katz、 Carl Lerche、 Jeremy Kemper、 Xavier Noria、 Ryan Bates、 Geoffrey Grosenbach、 Peter Cooper、 Matt Aimonetti、 Gregg Pollack、 Wayne E. Seguin、 Amy Hoy、 Dave Chelimsky、 Pat Maddox、 Tom Preston-Werner、 Chris Wanstrath、 Chad Fowler、 Josh Susser、 Obie Fernandez、 Ian McFarland、 Steven Bristol、 Pratik Naik、 Sarah Mei、 Sarah Allen、 Wolfram Arnold、 Alex Chaffee、 Giles Bowkett、 Evan Dorn、 Long Nguyen、 James Lindenbaum、 Adam Wiggins、 Tikhon Bernstam、 Ron Evans、 Wyatt Greene、 Miles Forrest、 Pivotal Labs の方々、 Heroku の方々、 thoughtbot の方々、 そして GitHub の方々、ありがとうございました。最後に、ここに書ききれないほど多くの読者からバグ報告や提案を頂きました。彼ら/彼女らのおかげで、本書を可能な限り良い本に仕上げることが出来ました。

著者

マイケルハートル (Michael Hartl) は、「Ruby on Rails チュートリアル」という、Ruby on Rails を使って初めて Web アプリケーションを開発する際に、最もよく参考にされる本の著者です。以前は、(今ではすっかり古くなってしまいましたが)「RailsSpace」という本の執筆および開発に携わったり、また、 一時人気を博した Ruby on Rails ベースのソーシャルネットワーキングプラットフォーム「Insoshi」の開発にも携わっていました。なお、2011年には、Rails コミュニティへの高い貢献が認められて、Ruby Hero Award を受賞しました。ハーバード大学卒業後、カリフォルニア工科大学物理学博士号を取得し、起業プログラム Y Combinator の卒業生でもあります。

著作権とライセンス

Ruby on Rails チュートリアル: 実例を使って Rails を学ぼう. Copyright © 2010 by Michael Hartl.Ruby on Rails チュートリアル内の全てのソースコードは、MIT ライセンスおよび Beerware ライセンスの元で提供されています。

(The MIT License)

Copyright (c) 2012 Michael Hartl

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Michael Hartl wrote this code.As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day,
 * and you think this stuff is worth it, you can buy me a beer in return.
 * ----------------------------------------------------------------------------
 */

第3章ほぼ静的なページの作成

本章では、今後のチュートリアルを楽に理解できるように、簡単なサンプルアプリケーションを開発してみます。本書を通して開発するアプリケーションは、最終的にはユーザやマイクロポスト、ログイン/ログアウトなどの認証機能を持ちますが、まずは簡単なトピックである「静的なページの作成」から始めます。非常にシンプルなページではありますが、静的なページを作成することは良い経験になり、また、多くの示唆も得られます。私達がこれから開発するアプリケーションにとって、最高のスタート地点といえるでしょう。

Rails はデータベースと連携して動的なWebサイトを開発するように設計されていますが、HTMLファイルだけで構成されている静的なページを作ることもできます。実際、静的なページをRailsで作ることのメリットもあります。たとえば、あとでほんの少し動的なコンテンツを追加することができます。本章では、このような静的なページの作成について学んでいきます。まずは、自動化テストの雰囲気を掴んでいきます。自動化テストは、私達のコードが正しく動いているという自信を与えてくれます。さらに、良いテストを書くことで、自信をもってリファクタリングを行うことができます。たとえば、フォームの振る舞いを変更せずに、フォーム内で使われている関数を書き換えたいときに便利です。

本章には多くのサンプルコードがあります。特に 3.23.3 で多くのコードを紹介していますが、Rubyが初めての方は、コードを隅々まで理解しなければならないのだろうかと心配する必要はありません。1.1.1 で紹介したように、まずはテストをコピー&ペーストしてみて、アプリケーションがうまく動くかどうか検証してみると良いでしょう。この時点では、テストどのようにして動くかは気にしなくても大丈夫です。また、第4章ではRubyについて解説します。そこでは、Rubyの書き方やコンセプトについて学びます。最後に、本書ではRSpecを使ったテストを繰り返し実施していきます。ですから、もし途中でよく分からないテストがあったとしても、読み飛ばして先に進むことをお勧めします。1、2章先を読み進めた後に読み返してみると、あのときはよく分からなかったテストが、実はとてもシンプルであることを理解できるはずです。

では、第2章でもやったように、まずはRailsプロジェクトを作りましょう。今回はsample_appというプロジェクトを作ります。

$ cd rails_projects
$ rails new sample_app --skip-test-unit
$ cd sample_app

ここで使ったrails--skip-test-unitというオプションは、Test::Unitフレームワークと関連しているtestディレクトリを作成しないようにするオプションです。これは「テストを書かないから」という理由ではありません。そうではなく、3.2以降では、もう1つのテストフレームワークであるRSpecを使ってテストを書くからです。

次は、2.1と同じように、テキストエディタを使ってGemfileに必要なgemを書き足していきます。今回は、2つの新しいgemを使います。RSpecのためのgem と、RSpecのライブラリのためのgemです。これらのgemをGemfileに追加すると、リスト3.1のようになります(: もしサンプルアプリケーションの開発で必要になるgemをすべて知りたい場合は、リスト9.49を参照してください。これが最終的なGemfileになります)。

リスト 3.1 サンプルアプリケーションで使うGemfile
source 'https://rubygems.org'

gem 'rails', '3.2.14'

group :development, :test do
  gem 'sqlite3', '1.3.5'
  gem 'rspec-rails', '2.11.0'
end

# assetsでは使うが、
# 本番環境ではデフォルトで不要なGem
group :assets do
  gem 'sass-rails',   '3.2.5'
  gem 'coffee-rails', '3.2.2'
  gem 'uglifier', '1.2.3'
end

gem 'jquery-rails', '2.0.2'

group :test do
  gem 'capybara', '1.1.2'
end

group :production do
  gem 'pg', '0.12.2'
end

このGemfileでは、開発環境とテスト環境でrspec-railsを使うようにしています。このため、開発環境ではRSpec固有のジェネレーターにアクセスすることができます。同様に、テスト環境ではRSpecを使用してテストを実行できるようになります。Gemfileに記述したrspec-railsが依存関係を解決してくれるため、 個々の環境にRSpec自身を手動でインストールする必要がなくなり、自動的にインストールされるようになります。同様の理由で、CapybaraもGemfileに記述しています。これは、英語に近い文法を使って、ユーザーの対話的操作をシミュレーションできるGemです1。また、第2章でも扱ったように、HerokuにデプロイするときにはPostgreSQLのgemを本番環境に追加します。

group :production do
  gem 'pg', '0.12.2'
end

Herokuは、開発環境とプロダクション環境とで同じデータベースを使うことを推奨していますが、今回開発するサンプルアプリケーションでは、データベースが異なっていても特に問題はありません。また、SQLiteはPostgreSQLに比べて極めて簡単に セットアップできます。なお、ローカルの開発マシンへのPostgreSQLのインストールと構築については、3.5の演習課題として取り上げています。

Gemfileに新しく追加したgemを実際にインストールするには、bundle updatebundle installを実行します。

$ bundle update
$ bundle install --without production

1.4.1第2章でも説明したように、--without productionオプションを追加することで、本番環境のgemのみインストールしないようにすることができます。注: このオプションは “remembered option” と呼ばれるもので、このオプションを一度実行するとコマンドに保存され、今後Bundlerを実行するときにオプションを追加する必要がなくなります。このため、今後は単にbundle installを実行するだけで、自動的に本番環境用gemをスキップできるようになります2

次に、Test::Unitの代わりにRSpecを使うように、Railsの設定を変更します。これを行うには、rails generate rspec:install を実行します。

$ rails generate rspec:install

(JavaScriptランタイムがインストールされていないというエラーが表示された場合は、GitHubのexecjsページにあるインストール可能なランタイムの一覧から入手してください。個人的にはNode.jsをお勧めします。)

ここまで進めたら、後はGitリポジトリを初期化するだけです3

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

まずは、リスト 3.2のように、アプリケーションのルートディレクトリに置かれているREADMEファイルをわかりやすく書き直してみましょう。

リスト 3.2 サンプルアプリケーション向けに改善したREADMEファイル。
# Ruby on Rails チュートリアル:サンプルアプリケーション

これは、以下のためのサンプルアプリケーションです。
[*Ruby on Rails Tutorial: Learn Rails by Example*](http://railstutorial.jp/)
by [Michael Hartl](http://www.michaelhartl.com/).

次に、拡張子を .md に変更し、Markdownファイルとして認識できるようにします。その後、これらの変更をコミットします。

$ git mv README.rdoc README.md
$ git commit -a -m "Improve the README file"
create_repository_new
図3.1サンプルアプリケーションのリポジトリをGitHub上に作成する。(拡大)

本書では今後、このサンプルアプリケーションを使っていくことになるので、GitHub上にリポジトリを作成し (図 3.1)、プッシュしておくと良いでしょう。

$ git remote add origin git@github.com:<ユーザ名>/sample_app.git
$ git push -u origin master

ここまで作業を進めると、GitHubに上がっているRailsチュートリアルのサンプルアプリケーションのようになります (名前は若干異なりますが)4

もちろん、お望みであれば、この時点でHerokuにデプロイすることもできます。

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

# もし Heroku のデプロイに失敗したときは、次のコマンドを試してみてください。
$ rake assets:precompile
$ git add .
$ git commit -m "Add precompiled assets for Heroku"
$ git push heroku master

なお、本書を進める間、アプリケーションを定期的にGitHubにプッシュしたり、Herokuにデプロイすることをお勧めします。

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

これにより、リモート環境にバックアップを置くことができ、本番環境で発生するエラーをなるべく早期に発見することができます。なお、Herokuにデプロイするときにエラーが発生した場合は、以下のコマンドを実行して本番環境のログを取得してください。このログは、問題を特定するときに役立ちます。

$ heroku logs

ここまでの準備が完了したら、いよいよサンプルアプリケーションの開発を始めましょう。

3.1静的ページ

Railsで静的なページを作成する場合、主に次の2つの方法があります。1つは、生のHTMLファイルを含む本当に静的なページを表示する方法です。もう1つは、生のHTMLを含むビュー (view) を定義することです。ビューを定義する方法では、Railsがビューを出力 (rendering) し、その後Webサーバーがその結果をブラウザに送信します。

現在どのディレクトリで作業しているかがわからなくなった場合は、1.2.3 (図 1.2)を再度参照して、Rails のディレクトリ構造を確認してください。この節では、主にapp/controllersディレクトリやapp/viewsディレクトリ内で作業を進めます (なお 3.2 では、新しいディレクトリをさらに1つ追加します)。

この節からは、Railsのすべてのディレクトリを一覧できるテキストエディタまたはIDEが使用できると非常に便利です。残念ながら、テキストエディタや IDEの細かな使用法はそれぞれ異なりますが、どのツールを使ってもRailsアプリケーションのディレクトリを開くことができます。Unix系のシステムでは、ドット.でカレントディレクトリを表現できるので、コマンドライン上で以下を実行してRailsアプリケーションの現在のディレクトリを開き、使用するエディタを呼び出してみてください。

$ cd ~/rails_projects/sample_app
$ <エディタ名> .

たとえば、Sublime Text でサンプルアプリケーションを開く場合は、以下を実行します。

$ subl .

Vimの場合は、種類に応じてvim .gvim .mvim .などでエディタを開けます。

3.1.1「本当に」静的なページ

最初に、本当に静的なページを作成してみましょう。1.2.5で、railsスクリプトを使用して作成した最小限の機能が、あらゆるRailsアプリケーションで利用できることを思い出してください。なお、デフォルトのwelcomeページはhttp://localhost:3000/ (図 1.3) でアクセスできます。

public_index_rails_3
図3.2public/index.htmlファイルの内容。(拡大)

このページがどこから来ているかを学ぶために、public/index.html (図 3.2) ファイルを見てみてください。このファイルは、スタイルシートの情報を含んでいるので少し煩雑ですが、このファイルだけでページを描画することができています。これは、Railsはデフォルトでpublicディレクトリ内にあるファイルを直接ブラウザに送信しているからです5。またデフォルトでは、今回使われているindex.htmlファイルについてはURLでindex.htmlと指定する必要はありません。たとえば、URLにhttp://localhost:3000/と入力しても、http://localhost:3000/index.htmlと入力しても、表示される内容は同じです。

もうお気付きかもしれませんが、静的なHTMLファイルを作成してpublicディレクトリ内に置けば、index.htmlと同じように表示することができます。まずは、"Hello, world!" と表示するファイル (リスト3.3) を作ってみましょう6

$ subl public/hello.html
リスト 3.3 HTMLファイルの典型的な "Hello, World!"。
public/hello.html
<!DOCTYPE html>
<html>
  <head>
    <title>Greeting</title>
  </head>
  <body>
    <p>Hello, world!</p>
  </body>
</html>

リスト3.3は、典型的な HTML ドキュメントの構造です。たとえば、1行目のdocument type (doctype とも呼ぶ) は、使用しているHTMLのバージョンをブラウザに宣言するもので、今回の例ではHTML5を使っていることを宣言しています7。またheadセクションでは、titleタグで "Greeting" と表示しています。最後にbodyセクションでは、p (paragraph) タグを使って “Hello, world!” と表示しています (なお、HTMLではスペースやタブは無視されるので、インデントはあってもなくても大丈夫です。ただし、インデントがある方がHTMLのデータ構造を理解しやすくなります)。

では、次のコマンドを入力して、ローカル環境でサーバーを起動してみましょう。

$ rails server

次にブラウザでhttp://localhost:3000/hello.htmlにアクセスしてみると、先ほど説明したとおり、先ほどpublicディレクトリ以下に作成した静的なHTMLページがRailsによって表示されます (図 3.3)。なお、図3.3の上部中央にある "Greeting" という文字は、 先ほど title タグで囲った文字列です。

"hello world"
図3.3新しい静的な HTML ファイル。(拡大)

この静的ファイルはRailsの振る舞いを確かめるためのものなので、サンプルアプリケーションを開発するために必要なファイルではありません。誤って./public/hello.html が参照されてしまうことのないように、以下のように削除しておきましょう。

$ rm public/hello.html

ただし、index.htmlファイルは後で使うので、当面残しておいてください。もちろん、サンプルアプリケーションのルートURLにアクセスした際に表示されてしまうと困るので、最終的にはこのファイルも削除することになります (図 1.3)。具体的には、5.3で、http://localhost:3000/にアクセスした際にpublic/index.html以外を参照するようにします。

3.1.2Railsによる静的なページ

静的なHTMLファイルを返す機能はもちろん必要ですが、動的なWebアプリケーションを作成するにはこれだけでは不足です。この節では、一連のURIを定義する、アクションというRailsの強力なツールを使用して動的なページを作成します8。Railsのアクションは、コントローラ (1.2.6で紹介した MVCの Cに該当) の中に置かれる機能で、共通の目的を持つ一連のアクションをコントローラ内にまとめることができます。コントローラについては第2章でも簡単に触れましたが、第6章で説明するREST アーキテクチャを読むと、より深く理解することができます。一言で言うと、コントローラとは (基本的に動的な) Webページの集合を束ねるコンテナのことです。

さっそく静的なページを作ってみましょう。まずは、1.3.5で紹介したように、Gitを使ってトピックブランチを作ります。今回のように新しい機能やページを作成するときは、masterブランチではなくトピックブランチで作業するのがよいでしょう。Git でバージョン管理をしている場合は、次のコマンドでトピックブランチを作成してください。

$ git checkout -b static-pages

Railsにはgenerateというスクリプトがあり、このスクリプトにコントローラ名を入力するだけで、この魔法のようなスクリプトがコントローラを作成してくれます。本章の冒頭でRSpecをインストールしていなかった場合は、このgenerateを使って以下のようにRSpecをインストールします。

$ rails generate rspec:install

これより、複数の静的なページを取り扱うStaticPagesコントローラを作成します。具体的には、HomeページとHelpページ、Aboutページで使用するアクションを作ってみます。generateスクリプトは、任意の数のアクションを引数に取ることができます。まずは、上記の3つのアクションのうち、最初の2つのアクションを作ってみます (リスト 3.4)。

リスト 3.4 StaticPagesコントローラを生成する。
$ 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

今回はRSpecのテストを使わないため、--no-test-frameworkというオプションを付け加えることで、RSpecのテストを自動的に生成しないようにしています。代わりに、3.2からは手動でテストを作成します。また、リスト 3.4に示したように、今回は意図的にaboutアクションをコマンドライン引数から取り除きました。 これは、同じく3.2 から、テスト駆動開発 (Test-Driven Development, TDD) という開発手法を学ぶためです。

リスト 3.4では、コントローラ名をキャメルケース (訳注: 単語の頭文字を大文字にしてつなぎ合わせた名前) で渡していることに注目してください。こうすると、StaticPagesコントローラは、スネークケース (訳注: 単語間にアンダースコアを加えて繋ぎ合わせた名前) のファイル static_pages_controller.rb を自動的に生成します。ただし、上のような命名は単なる慣習に過ぎません。実際、コマンドライン上で以下のようなスネークケースのコントローラ名を入力しても、

$ rails generate controller static_pages ...

先ほどと同様にstatic_pages_controller.rbというコントローラが生成されます。これは、Rubyがクラス名にキャメルケースを使う慣習があり (詳細は4.4で説明します)、また、キャメルケースの名前を使うことが好まれているためです。これらの慣習に必ず従わなければいけないということではありません。(同様にRubyでは、ファイル名をスネークケースで記述する慣習があります。このため Railsのgenerateスクリプトでは、 underscoreメソッドを使ってキャメルケースをスネークケースに変換しています。)

ところで、自動生成に失敗するようなことがあれば、元に戻す処理を学ぶ良い機会になります。以下のコラム 3.1で元に戻す方法を紹介しています。

リスト 3.4のように、StaticPagesコントローラを生成すると、config/routes.rbファイルが自動的に更新されます。Railsは、この routesファイルに記述されている内容 (ルーティング) に従って、 複数のURIとWebページを対応付けます。ここで、初めてconfigディレクトリについて触れます。このディレクトリは図 3.4のような構成をしています。configディレクトリという名前のとおり、このディレクトリ内にあるファイルは、Railsがアプリケーションの設定を読み込む時に必要になります。

config_directory_rails_3
図3.4サンプルアプリケーションのconfigディレクトリの内容。(拡大)

先ほどhomeアクションとhelpアクションを生成したので、次のリスト 3.5 に示されるように、routesファイルにはそれぞれのアクションで使用されるルールが定義されています。

リスト3.5 StaticPagesコントローラ内のhomeアクションとhelpアクションで使用するルーティング。
config/routes.rb
SampleApp::Application.routes.draw do
  get "static_pages/home"
  get "static_pages/help"
  .
  .
  .
end

ここで以下のルールに注目してみましょう。

get "static_pages/home"

このルールは、/static_pages/homeに対するリクエストを、StaticPagesコントローラのhomeアクションと結びつけています。具体的には、getと書くことで、GETリクエストに対して該当するアクションを結びつけています。なお、ここでいう GETリクエストとは、HTTP (HyperText Transfer Protocol) (コラム 3.2) が対応しているメソッドの1つです。今回の場合は、StaticPagesコントローラ内にhomeアクションを追加したので、/static_pages/homeにアクセスすることでページを取得 (GET) できるようになりました。/static_pages/homeにアクセスして結果を表示します (図3.5)。

raw_home_view_31
図3.5/static_pages/homeにアクセスした結果。(拡大)

このページがどのようにして表示されるのかを理解するために、まずはテキストエディタでStaticPagesコントローラを開いてみましょう。リスト3.6のような内容になっているはずです。ここで、第2章のUsersコントローラやMicropostsコントローラとは異なり、StaticPagesコントローラは一般的なRESTアクションに対応していないことに注意してください。これは、静的なページの集合に対しては、適切なアクションと言えます。言い換えると、RESTアーキテクチャは、あらゆる問題に対して最適な解決方法であるとは限らないということです。

リスト3.6 リスト3.4で生成されたStaticPagesコントローラ。
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
  end

  def help
  end
end

リスト 3.6classというキーワードから、static_pages_controller.rbStaticPagesControllerというクラスを定義していることがわかります。クラスは、関数 (メソッドとも呼ばれます) をまとめるときに便利な手法です。今回の例では、defというキーワードを使って、homeアクションやhelpアクションを定義しています。また、山括弧<は、StaticPagesControllerApplicationControllerというRailsのクラスを継承していることを示しています。この後も説明しますが、今回作成したページには、Rails特有の機能が多数使用されています (具体的なクラスや継承については、4.4で詳しく説明します)。

今回のStaticPagesコントローラにあるメソッドは、以下のようにどちらも最初は空になっています。

def home
end

def help
end

純粋なRuby言語であれば、これらのメソッドは何も実行しません。しかし、Railsでは動作が異なります。StaticPagesControllerはRuby のクラスですが、ApplicationControllerクラスを継承しているため、StaticPagesControllerのメソッドはRails特有の振る舞いをします。具体的には、/static_pages/homeというURIにアクセスすると、RailsはStaticPagesコントローラを参照し、homeアクションに記述されているコードを実行します。その後、そのアクションに対応するビュー (1.2.6で説明したMVCのVに相当) を出力します。今回の場合、homeアクションが空になっているので、/static_pages/homeにアクセスしても、単に対応するビューが出力されるだけになります。では、ビューはどのように出力されるのでしょうか。また、どのビューが表示されるのでしょうか。

リスト3.4を注意深く読んでみると、アクションとビューの関係性について理解できるでしょう。homeアクションは、home.html.erbというビューに対応しています。.erbの詳細については3.3で説明しますが、ファイル名に.htmlが含まれていることから推測できるとおり、基本的にはHTMLと同じような構造になっています (リスト3.7)。

リスト3.7 Homeページを出力する、生成されたビュー
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)。

リスト3.8 Helpページを出力する、生成されたビュー
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.3からは、(ほんの少しだけ) 動的なコンテンツを追加しますが、ここで重要なのは「Railsのビューは静的なHTMLで構成されている」という点です。ブラウザから見えるのは常にHTMLなので、ブラウザの側から見れば、3.1.1のような生のHTMLファイルも、コントローラやアクションを使用して生成されたページも、違いはありません。

以後本章では、HomeページとHelpページのコンテンツを少しだけカスタマイズします。また、3.1.2で先送りにしたAbout ページにもコンテンツを追加していきます。それが終わったら、ページごとに異なるタイトルを表示する、ほんの少しだけ動的なコンテンツを追加します。

次に進む前に、StaticPagesコントローラファイルをGitリポジトリに追加しておきましょう。

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

3.2最初のテスト

Railsチュートリアル では、一字一句間違えることなく最初から正確に実装するのではなく、アプリケーションの振る舞いをテストしながら実装する直観的な手法を採用しています。この開発手法は、テスト駆動開発 (Test-Driven Develpment, TDD) から派生した振舞駆動開発 (Behavior-Driven Development, BDD) として知られています。本書でこれから頻繁に使うツールは、結合テスト (integration test) 単体テスト (unit test) の2つです。結合テストについてはこの節から、単体テストについては第6章から解説していきます。結合テスト (RSpec では リクエストspec と呼んでいます) は、ユーザがアプリケーションを使う際の一連のアクションをシミュレーションします。結合テストは、アプリケーションの各ページが正常に動作するかどうかをテストしてくれる強力なツールです。手動でブラウザを操作してテストする必要がなくなり、Capybaraを併用すれば自然言語 (英語) に近い文法でテストを記述する事もできます (Capybara以外にも、Cucumberという振舞駆動開発用ツールが有名です。詳細については8.3で紹介します)。

テスト駆動開発の定義とは、アプリケーションを開発するときに最初にテストを作成し、次にコードを作成することです。この開発手法に慣れるまでには多少時間がかかるかもしれませんが、しかし一度慣れてしまえば大きなメリットを得られます。失敗するテストを最初に書き、テストにパスするコードを次に実装することで、しかるべき振る舞いがテストによって正しく検証されている、という自信が付きます。さらに、この「失敗-実装-成功」という開発サイクルは、「フロー体験」を誘発します。フローに入ることで、コーディングが楽しくなり、開発の生産性も向上します。また、テストはアプリケーションのコードに対してクライアントとして振る舞うので、ソフトウェア設計の改良につながることも多くなるでしょう。

ただし、テスト駆動開発がどんな仕事に対しても常に正しい手法であるとは限りません。このことは十分に理解しておいてください。「最初にテストを書くべきである」、「テストはひとつひとつの機能を完全にカバーするべきである」、「すべての箇所をテストすべきである」などのような教条的な主張を正当化できる理由はどこにもありません。たとえば、与えられた課題の解決法に今ひとつ確信が持てないときは、(テストを書かずに) まず試しにアプリケーションコードだけを書いてみて、どんな解決方法があるのか模索してみる方が良い結果を得られることもあります (エクストリーム・プログラミング (Extreme Programming) という開発手法では、この模索段階をスパイク (spike) と呼んでいます)。そして解決策が明確になった段階で、テスト駆動開発でコードを清書するという方法もありえます。

この節では、RSpec gemによって提供されるrspecコマンドを使ってテストを実行します。この節は素直なつくりになっていますが、物足りないと思う方もいるかもしれません。上級開発者であれば、先に3.6を読んでシステム設定を完了しておくことをお勧めします。

3.2.1テスト駆動開発

テスト駆動開発で最初に書く、失敗するテストのことを、一般的なテストツールでは「赤色 (Red)」と表現します (失敗時に表示が赤くなるツールが多いため)。同様に、次に書く、テストにパスするコードのことを「緑色 (Green)」と表現します。最後に、必要に応じてコードをリファクタリング (例えば、動作を変えずにコードを改善したり、冗長なコードを削除したりすること) します。このサイクルのことを「Red/Green/Refactor」と呼びます。

それでは、テスト駆動開発でいくつかのコンテンツをHomeページに追加してみましょう。トップレベルの見出し (<h1>) に "Sample App" という語を追加する作業もこの中に含まれます。 まず、静的なページに対する結合テスト (request spec) を生成するところから始めましょう。

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

これにより、spec/requestsディレクトリにstatic_pages_spec.rbが生成されます。自動生成される他のコードと同様に、最初のコードは完全ではありません。そこで、テキストエディタでstatic_pages_spec.rb を開き、リスト 3.9のようにコードを書いてみましょう。

リスト3.9 Homeページの内容をテストするコード。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

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

リスト 3.9にあるコードはすべてRubyで書かれています。しかし、Rubyを勉強したことがある人にとっては見慣れない書き方に見えるかもしれません。これは、Rubyの柔軟性の高さを応用して、RSpecがテスト用の独自言語 (Domain-Specific Language, DSL) を定義しているからです。ここで重要なのは、RSpecを使うためにRSpec独自の文法を理解する必要はないということです。最初のうちは魔法のように見えるかもしれませんが、RSpecやCapybaraは英語に近い形で読めるように設計されています。したがって、generateスクリプトで生成されるテスト例や、本チュートリアルで取り上げるテスト例を読み進めるだけで、英語圏の方ならRSpecの文法を楽に扱えるようになります。

リスト3.9にはdescribeというブロックがあり、そのブロック内には it "…" doで始まるテスト例があります。

describe "Home page" do

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

最初の行では、Homeページに対するテストであることを記述しています。これは単なる文字列であり、好きな文字列を使用できます。RSpecはこの文字列を解釈しないので、人間にとってわかりやすい説明をここに書くようにします。次の行では、「/static_pages/homeのHomeページにアクセスしたとき、“Sample App”という語が含まれていなければならない」と記述しています。最初の行と同様で、RSpec はダブルクォート (") で囲まれた文字列を無視しますので、ここにも人間にとってわかりやすい説明文を書きましょう (訳注: 英語で should have...と書くことで、メソッドのitと整合性が取れます)。その次の行について説明します。

visit '/static_pages/home'

上の行は、Capybaraのvisit機能を使って、ブラウザでの/static_pages/homeURIへのアクセスをシミュレーションします。

page.should have_content('Sample App')

その次の行 (上のコード) では、Capybaraのpage変数を使って、アクセスした結果のページに正しいコンテンツが表示されていることをテストしています。

テストではさまざまなオプションが使用でき、さらに高度で便利なツールもあります。詳細については3.6で説明します。今回は、コマンドラインでrspecコマンドを実行してみてましょう (なお、bundle execをこのコマンドの前に置くことで、Gemfile内で定義された環境でRSpecが実行されるように、明示的に指示することができます9)。

$ bundle exec rspec spec/requests/static_pages_spec.rb

上のコマンドを実行すると、「テストが失敗した」という結果が返ってきます。 結果の表示はシステムによって異なりますが、著者のシステムでは、図3.6のように失敗したテストが赤く表示されます10 (このスクリーンショットを撮った時点ではGitのブランチ戦略を取り入れていなかったので、static-pagesではなくmasterと表示されていますが、特に深い意図はありません)。

red_failing_spec
図3.6赤く表示されている (失敗した) テスト。(拡大)

テストにパスするために、自動生成されたHomeページのHTMLをリスト3.10のように書き換えてみましょう。

リスト3.10 テストにパスするHomeページ用コード。
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>

トップレベルの見出し (<h1>) がSample Appに変更されたため、上のコードはテストにパスします。また、アンカータグ a を使って、指定したURIにジャンプする以下のリンクを追加しました (ちなみにアンカータグ内の “href” は “hypertext reference” と読みます)。

<a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>

結果を見るために、もう一度テストを実行してみましょう。

$ bundle exec rspec spec/requests/static_pages_spec.rb

著者のシステムでは、図3.7のように表示されました。

green_passing_spec
図3.7緑色で表示されている (成功した) テスト。(拡大)

Helpページについても、Homeページの例を参考にして、同じようなテストとアプリケーションコードを使用できることが推測できます。最初に、文字列を ’Help’ に書き換えたテストを追加してみましょう (リスト 3.11)。

リスト3.11 Helpページの内容をテストするコードを追加する。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

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

  describe "Help page" do

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

上のコードでテストを実行してみます。

$ bundle exec rspec spec/requests/static_pages_spec.rb

テストのうち、1つは失敗するはずです。(注: 執筆作業軽減のため、今後はRSpecの出力結果を掲載しません。画面表示の内容はシステムによって大きく異なるうえ、各段階での出力画面数を把握してメンテナンスし続けるのが極めて困難なためです。ご了承ください。)

リスト3.12に示したように、テストにパスするアプリケーションコード (現時点では生のHTML) はリスト3.10と同じようなコードになります。

リスト3.12 テストにパスする、Helpページ用のコード。
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>

これでテストにパスするはずです。

$ bundle exec rspec spec/requests/static_pages_spec.rb

3.2.2ページの追加

先ほどのテスト駆動開発のシンプルなテスト例を参考に、もう少し複雑なタスクを実行するページを新規追加してみましょう。具体的には、3.1.2で意図的に先送りにしたAboutページを新しく追加します。段階ごとにテストを作成してRSpecを実行することで、テスト駆動開発によってアプリケーション開発を進める方法を理解できるようになるでしょう。

赤 (Red)

赤色から緑色にするために、最初にAboutページ用の失敗するテストを書き、赤色にしましょう。 リスト3.11を参考にすることで、正しいテストを推測できるでしょう(リスト3.13)。

リスト3.13 Aboutページのテストを追加する。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

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

  describe "Help page" do

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

  describe "About page" do

    it "should have the content 'About Us'" do 
      visit '/static_pages/about'
      page.should have_content('About Us')
    end
  end
end

緑 (Green)

3.1.2で触れたように、Railsでは、アクションと、それに対応するページ名を持つビューを作成することで、静的なページを生成することができます。今回の場合、 Aboutページを使用できるようにするには、aboutアクションをStaticPagesコントローラの中に追加する必要があります。最初に失敗するテストを書き、次にそのテストにパスするように実装することで、正常に動作するAboutページを作成できたという実感を得ることができます。

先ほど実装したRSpecのテストを実行します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

上を実行した出力結果の中に、以下のような警告が含まれているはずです。

No route matches [GET] "/static_pages/about"

このメッセージは、/static_pages/aboutというルートをroutesファイルに追加する必要があるということを示しています。リスト3.5のパターンに従って、routesファイルをリスト3.14のように変更することでこの問題は解決します。

リスト3.14 about用のルートを追加する。
config/routes.rb
SampleApp::Application.routes.draw do
  get "static_pages/home"
  get "static_pages/help"
  get "static_pages/about"
  .
  .
  .
end

再び以下を実行します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

今度は以下のエラーメッセージが発生します。

The action 'about' could not be found for StaticPagesController

この問題を解決するために、リスト 3.6homeアクションや helpアクションのパターンに従って、StaticPagesコントローラの中にaboutアクションを追加します (リスト 3.15)。

リスト3.15 aboutアクションが追加されたStaticPagesコントローラ。
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
  end

  def help
  end

  def about
  end
end

再び以下を実行します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

今度は、ビューなどの "テンプレート" が見当たらないというエラーメッセージが表示されます。

ActionView::MissingTemplate:
  Missing template static_pages/about

これは、aboutビューを追加することで解決します。具体的には、app/views/static_pagesディレクトリの中にabout.html.erbというファイルを作成し、リスト 3.16の内容を書き込みます。

リスト3.16 Aboutページのコード
app/views/static_pages/about.html.erb
<h1>About Us</h1>
<p>
  The <a href="http://railstutorial.jp/">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>

今度は、RSpecを実行すると緑色になるはずです。

$ bundle exec rspec spec/requests/static_pages_spec.rb

もちろん、実際にブラウザを起動して、テストが正しく動いているかどうかを確かめることもできます (図3.8)。

about_us_2nd_edition
図3.8作成したAboutページ (/static_pages/about)。(拡大)

リファクタリング

テストが緑色になったので、安心してコードをリファクタリングできるようになりました。多くの場合、コードを書き進めるうちに肥大化したり繰り返しが増えたりして、いつしか「悪臭を放つ」醜悪なコードになりはてるものです。コードが醜くてもコンピュータはそのことを気にかけませんが、開発者にとってはそうはいきません。だからこそ、頻繁にリファクタリングを実施し、コードを清潔な状態に保ち続けることが重要です。この点において、良いテストコードがあるということは非常に貴重です。リファクタリングする際にバグが混入する可能性を劇的に小さくしてくれるからです。

サンプルアプリケーションはかなり小規模なので、今のところリファクタリングの余地はありません。しかしコードの「悪臭」はやがてあらゆる箇所からただよいはじめ、そのうちにリファクタリングが必要になることでしょう。実際、3.3.4で早くもリファクタリングに取りかかることになります。

3.3少しだけ動的なページ

以上で、静的ページのアクションとビューを作成しました。次は、ページの内容を反映したタイトルを持ち、ページごとに内容が変化する、少しだけ動的なページを作成してみましょう。 タイトルを変えることが本当に動的コンテンツかどうかは議論の余地があると思いますが、いずれにしろこのページは、第7章で紹介する真に動的なコンテンツの基礎となります。

3.2のテスト駆動開発の説明を読んでいない場合は、リスト3.14リスト3.15リスト3.16のコードを使ってAboutページを必ず作成しておいてください。

3.3.1タイトル変更をテストする

ここでの目標は、Home、Help、Aboutページをそれぞれ編集し、ページごとに異なるタイトルが表示されるようにすることです。ここではビューの<title>タグの内容を変更します。titleタグは、ほとんどのブラウザでウィンドウの上に表示されます (Google Chromeは例外ですが)。titleタグは、検索エンジン最適化 (SEO) においても重要な役割を担っています。最初にタイトルのテストを作成し、次にタイトルを追加し、さらにレイアウトファイルを使ってリファクタリングと重複の排除を行います。

レイアウトファイルは、rails newコマンドを実行していれば既に作成されているはずです。レイアウトファイルの役割についてはこの後説明しますが、まずは作業開始前にレイアウトファイルのファイル名を変更しておきましょう。

$ mv app/views/layouts/application.html.erb foobar   # 一時的な変更

(mvはUnixのコマンドです。Windowsでファイル名を変更するには、ファイルブラウザから行うか、renameコマンドを使ってください)。実際のアプリケーション開発時には、上のような操作を行うことはおそらくないでしょう。ここでは、レイアウトファイルの役割をよりわかりやすく説明するために、最初にレイアウトファイルを無効にしました。

ページURI基本タイトル追加タイトル
Home/static_pages/home"Ruby on Rails Tutorial Sample App""Home"
Help/static_pages/help"Ruby on Rails Tutorial Sample App""Help"
About/static_pages/about"Ruby on Rails Tutorial Sample App""About"
表3.1サンプルアプリケーションの (ほぼ) 静的なページ。

この節が完了するまでに、3つの静的ページのすべてのタイトルが、“Ruby on Rails Tutorial Sample App | Home”のように、最後の単語のみテーブル3.1に従ってページごとに変更されるようになります。最初に、リスト3.13のテストを元に、モデルに応じたタイトル表示のテスト (リスト3.17) を追加します。

リスト3.17 タイトルのテスト。
it "should have the right title" do
  visit '/static_pages/home'
  page.should have_selector('title',
                    :text => "Ruby on Rails Tutorial Sample App | Home")
end

このテストではhave_selectorメソッドを使っています。これは与えられた内容の中にHTML要素 (セレクタ) があることをチェックしています。つまり、以下のコードは、

page.should have_selector('title',
                  :text => "Ruby on Rails Tutorial Sample App | Home")

titleタグの内容が以下のとおりになっていることを確認します。

"Ruby on Rails Tutorial Sample App | Home"

(:text => "…"のように、ハッシュのキーにシンボルを使う記法については4.3.3で解説します)。ここで注意していただきたいのは、与えられた内容に対して完全一致する文字列を使用しなくてはならないということではなく、以下のように部分文字列を指定するだけでもよいということです。

page.should have_selector('title', :text => " | Home")

上のコードでもタイトル全体とマッチします。

リスト 3.17ではhave_selectorを2行に分けていることに注目してください。これは、改行の有無に影響されないというRubyの記法を生かしたものです11。行を分割した理由は、1行を80字以内に収めてソースコードを読みやすくするためです12。現状では、それでもこのコードは読みにくいままですが、3.5でより見やすくなるようにリファクタリングをします。また、5.3.4で最新版のRspecの機能を使ってStaticPagesのテストを完全に書き換えます。

リスト3.17と同じ要領で、StaticPagesのテストに3つの静的ページのテストを追加します (リスト3.18)。

リスト3.18 タイトルのテストを含むStaticPagesコントローラのspecファイル。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

  describe "Home page" do

    it "should have the h1 'Sample App'" do
      visit '/static_pages/home'
      page.should have_selector('h1', :text => 'Sample App')
    end

    it "should have the title 'Home'" do
      visit '/static_pages/home'
      page.should have_selector('title',
                        :text => "Ruby on Rails Tutorial Sample App | Home")
    end
  end

  describe "Help page" do

    it "should have the h1 'Help'" do
      visit '/static_pages/help'
      page.should have_selector('h1', :text => 'Help')
    end

    it "should have the title 'Help'" do
      visit '/static_pages/help'
      page.should have_selector('title',
                        :text => "Ruby on Rails Tutorial Sample App | Help")
    end
  end

  describe "About page" do

    it "should have the h1 'About Us'" do
      visit '/static_pages/about'
      page.should have_selector('h1', :text => 'About Us')
    end

    it "should have the title 'About Us'" do
      visit '/static_pages/about'
      page.should have_selector('title',
                    :text => "Ruby on Rails Tutorial Sample App | About Us")
    end
  end
end

上のコードでは、have_contenthave_selector(’h1’, …)に変更していることに注目してください。変更した理由を考えてみてください (ヒント: もしタイトルに 'Help' という文字が含まれていて、h1のタグの内容が 'Helf' だったら何が起こるでしょうか)。

リスト 3.18の内容を反映後、以下を実行します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

今度はテストが赤色 (テストが失敗する) になるはずです。

3.3.2タイトルのテストをパスさせる

今度は、タイトルのテストがパスするようにし、それと同時にWebページを正しく表示させるためのHTMLをすべて追加しましょう。リスト3.3の "hello" ページにある基本的なHTMLの骨組みを使って、リスト3.19のHomeページを作るところから始めましょう。

リスト3.19 完全なHTML構造を備えたHomeページのビュー
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>

リスト 3.19ではリスト 3.18にある以下のテスト済みのタイトルを使います。

<title>Ruby on Rails Tutorial Sample App | Home</title>

従って、Homeページのテストはパスするはずです。HelpとAboutのテストはまだ赤色 (失敗) の状態ですので、リスト 3.20リスト 3.21のようにコードを追加してテストを緑色 (成功) にします。

リスト3.20 完全なHTML構造を備えたHelpページのビュー
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>
リスト3.21 完全なHTML構造を備えたAboutページのビュー
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.jp/">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>

3.3.3埋め込みRuby

この節ではこれまでに、Railsのコントローラとアクションを使って3つの有効なページを生成することでさまざまなことを達成しました。しかしそれらは単純な静的ページであり、またRailsの能力を十分に発揮できていません。しかも、コードが甚だしく重複しています。

  • ページのタイトルがどれもほぼ同じ (完全にではないが)。
  • “Ruby on Rails Tutorial Sample App” が3つのタイトルで共通している。
  • HTMLの構造全体が各ページで重複している。

このようなコードの重複は、「重複してはならない」(Don’t Repeat Yourself, DRY) という重要な原則に反しています。以後、この節と次の節では重複を取り除き、コードをDRYにしていきます。

上の話と一見矛盾するようですが、最初に、現在のほとんど同じページのタイトルを、完全に同じになるようなコードを追加していきます。こうすることで、すべての重複を一気に取り除くことがより簡単になるからです。

重複を取り除くテクニックの一つとして、ビューで埋め込みRuby (Embedded Ruby) を使用できます。Home、Help、Aboutページには可変の要素があるので、Railsのprovide関数を使用してタイトルをページごとに異なるようにします。それでは、home.html.erbビューのコードを、リスト3.22のように、タイトルに含まれる"Home"という文字を置き換え、動作を確認しましょう。

リスト3.22 タイトルにRubyを埋め込んだHomeページのビュー
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>

リスト3.22は、ERbと呼ばれている、Rubyの埋め込みの最初の例です (これで、HTMLビューのファイルの拡張子が.html.erbとなっている理由をおわかりいただけたと思います)。 ERbはWebページに動的な要素を加えるときに使うテンプレートシステムです13

<% provide(:title, 'Home') %>

上のコードに<% ... %>と書かれていることに注目してください。Railsはこの中でprovide関数を呼び出し、:titleというラベルに ’Home’ という文字列を関連付けます14。次に、以下のコードでRubyのyield関数を使用し、それを<%= ... %>表記 (上と微妙に異なることに注目してください) で囲むことにより、テンプレートにタイトルを挿入します15

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

(この2つの埋め込みRubyの違いは次のとおりです。<% ... %>と書くと、中に書かれたコードを単に実行し、何も出力しませんが、<%= ... %>と等号を追加して書くと、中のコードが実行され、その結果がテンプレートに挿入されます)。この変更を行なっても、表示されるページの内容は以前とまったく同じです。タイトルの可変部分がERbによって動的に生成されている点だけが異なります。

3.3.1のテストを実行することで、変更が正しく行われたことを確認できます。以下を実行してテストがパスすることを確認しましょう。

$ bundle exec rspec spec/requests/static_pages_spec.rb

続いて、HelpページとAboutページも同様に変更します (リスト3.23リスト3.24)。

リスト3.23 タイトルで埋め込みRubyを使用したHelpページのビュー
app/views/static_pages/help.html.erb
<% provide(:title, 'Help') %>
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></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>
リスト3.24 タイトルで埋め込みRubyを使用したAboutページのビュー
app/views/static_pages/about.html.erb
<% provide(:title, 'About Us') %>
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>
  </head>
  <body>
    <h1>About Us</h1>
    <p>
      The <a href="http://railstutorial.jp/">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>

3.3.4レイアウトを使って重複を解消する

タイトルの可変部分をERbを使って置き換えたので、現在それぞれのページはだいたい以下のようになっています。

<% provide(:title, 'Foo') %>
<!DOCTYPE html>
<html>
  <head>
    <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>
  </head>
  <body>
      Contents
  </body>
</html>

これを見ると、唯一の例外であるbodyタグの内容を除き、すべてのページで (titleタグの内容を含め) 同じ構造になっていることがわかります。

Railsには、共通の構造をまとめるためのapplication.html.erbという特別なレイアウトファイルがあります。このファイル名は3.3.1で変更してあったので、以下のように元に戻します。

$ mv foobar app/views/layouts/application.html.erb

このレイアウトファイルを有効にするには、デフォルトのタイトル部分を以下の埋め込みRubyのコードに差し替えます。

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

その結果、レイアウトファイルはリスト3.25のようになります。

リスト3.25 サンプルアプリケーションのレイアウトファイル。
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" %>
    <%= javascript_include_tag "application" %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

上のコードにある、以下の特殊なコードに注目してください。

<%= yield %>

このコードは、各ページの内容をレイアウトに挿入するためのものです。ここでは、このコードの詳細な動作を正確に理解することは重要ではありません。レイアウトを使用するうえでは、/static_pages/homeにアクセスすると、home.html.erbの内容がHTMLに変換され、<%= yield %>の位置に挿入されるということだけ理解しておけば問題ありません。

Railsのデフォルトのレイアウトには、以下の行が追加されていることにも注目してください。

<%= stylesheet_link_tag    "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>

これらのコードはそれぞれ、スタイルシート、JavaScript、csrf_meta_tagsメソッドをページにインクルードするためのものです。スタイルシートとJavaScriptは、Asset Pipeline (5.2.1) の一部です。csrf_meta_tagsは、Web攻撃手法のひとつであるクロスサイトリクエストフォージェリー (cross-site request forgery, CSRF)を防ぐために使われるRailsのメソッドです。

もちろん、リスト3.22リスト3.23リスト3.24のビューには、レイアウトと重複するHTMLがまだ残っているので、それらを削除して、内部のコンテツだけ残します。これにより、 リスト3.26リスト3.27リスト .28にそれぞれ示したように、ビューのコードが美しく簡潔なものになります。

リスト3.26 HTML構造を削除したHomeページ。
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>
リスト3.27 HTML構造を削除したHelpページ。
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>
リスト3.28 HTML構造を削除したAboutページ。
app/views/static_pages/about.html.erb
<% provide(:title, 'About Us') %>
<h1>About Us</h1>
<p>
  The <a href="http://railstutorial.jp/">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>

上のように定義されたビューは、Home、Help、Aboutページの表示は以前と変わりませんが、コードの重複が大きく削減されました。最後に、このリファクタリングが正常に行われたことを確認するために、リファクタリング前と同様にテストにパスすることを確認します。

$ bundle exec rspec spec/requests/static_pages_spec.rb

3.4最後に

傍からは、この章では静的ページをほぼ静的ページに変えただけで、ほとんど何もしていないように見えることでしょう。しかし、見た目に反して、Railsのコントローラ、アクション、ビューは大きく改善されており、動的なコンテンツを自由にサイトに追加することができるようになりました。皆さんに残されている今後の課題は、このチュートリアルをいかに最後までやりぬくか、それだけであると言ってよいでしょう。

次の章に進む前に、差分をコミットしてマスターブランチにマージしておきましょう。3.1.2では、静的ページの開発のためのGitブランチを用意しました。ここまでの作業内容をコミットしていなければ、きりのよい中継点に達したことがわかるようにコミットします。

$ git add .
$ git commit -m "Finish static pages"

次にmasterブランチに移動し、1.3.5と同じ要領で差分をマージします。

$ git checkout master
$ git merge static-pages

このように中継点まで達したら、コードをリモートリポジトリにアップロードしておくとよいでしょう (1.3.4の手順に従っていれば、リモートリポジトリはGitHubになるでしょう)。

$ git push

好みに応じて、更新したアプリケーションをHerokuにデプロイしても構いません。

$ git push heroku

3.5演習

  1. サンプルアプリケーションにContact (問い合わせ先) ページを作成してください。リスト3.18に従い、最初に、h1の内容が正しいかどうかを確認するテストを作成することで、URIが/static_pages/contactのページが存在することを確認するテストを作成してください。次に、タイトルが “Ruby on Rails Tutorial Sample App | Contact” となっていることを確認するテストを作成してください。次にこれらのテストにパスするようにしてください。それが終わったら、リスト 3.29の内容をContactページに反映させましょう。(この演習の解答は5.3の説明に含まれています)。
  2. お気付きの方もいると思いますが、リスト3.18に示されている、StaticPagesコントローラのspecテストには若干の重複があります。特に、“Ruby on Rails Tutorial Sample App”というタイトルはそれぞれのタイトルのテストで繰り返し記述されています。引数として渡されたシンボルと同名の変数にブロックの評価値を格納する、RSpecのlet関数を使い、リスト 3.30のようにテストを変更し、テストにパスするようにしましょう。リスト3.30では文字列の式展開 (string interpolation) が行われていますが、これについては4.2.2で紹介します。
  3. (上級者向け)The Twelve-Factor App」のページでも説明されているように、互換性の問題によるエラーを最小限にとどめるために、開発/テスト/本番環境で共通のデータベースを使うことは良いことです。「Heroku instructions for local PostgreSQL installation」には、PostgreSQLをローカル環境にインストールする手順が紹介されてます。PostgreSQLを使う場合は、 リスト3.31にあるように、Gemfileからsqlite3 gemを削除し、pg gemのみを使うようにしてください。さらに、config/database.ymlファイルと、PostgreSQLをローカル環境で動作させる方法を学ぶ必要があります。以上の情報を元に、PostgreSQLを使用して開発データベースとテストデータベースを作成し、設定を行うことが、この課題のゴールです。警告: この演習はかなり難易度が高いので、上級者にのみお勧めします。もし行き詰まってしまったら、すぐにこの演習を飛ばして次の作業に進んでください。なお、既に説明したとおり、このチュートリアルで開発しているサンプルアプリケーションは、SQLiteとPostgreSQLのどちらについても完全に互換性があります。
リスト3.29 Contactページで使用するコンテンツのコード。
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>
リスト3.30 基本となるタイトルを含むStaticPagesコントローラのspec。
spec/requests/static_pages_spec.rb
require 'spec_helper'

describe "Static pages" do

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

  describe "Home page" do

    it "should have the h1 'Sample App'" do
      visit '/static_pages/home'
      page.should have_selector('h1', :text => 'Sample App')
    end

    it "should have the title 'Home'" do
      visit '/static_pages/home'
      page.should have_selector('title', :text => "#{base_title} | Home")
    end
  end

  describe "Help page" do

    it "should have the h1 'Help'" do
      visit '/static_pages/help'
      page.should have_selector('h1', :text => 'Help')
    end

    it "should have the title 'Help'" do
      visit '/static_pages/help'
      page.should have_selector('title', :text => "#{base_title} | Help")
    end
  end

  describe "About page" do

    it "should have the h1 'About Us'" do
      visit '/static_pages/about'
      page.should have_selector('h1', :text => 'About Us')
    end

    it "should have the title 'About Us'" do
      visit '/static_pages/about'
      page.should have_selector('title', :text => "#{base_title} | About Us")
    end
  end

  describe "Contact page" do

    it "should have the h1 'Contact'" do
      visit '/static_pages/contact'
      page.should have_selector('h1', :text => 'Contact')
    end

    it "should have the title 'Contact'" do
      visit '/static_pages/contact'
      page.should have_selector('title', :text => "#{base_title} | Contact")
    end
  end
end
リスト3.31 SQLiteの代わりにPostgreSQLを使う場合のGemfile
source 'https://rubygems.org'

gem 'rails', '3.2.14'
gem 'pg', '0.12.2'

group :development, :test do
  gem 'rspec-rails', '2.11.0'
end

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '3.2.5'
  gem 'coffee-rails', '3.2.2'
  gem 'uglifier', '1.2.3'
end

gem 'jquery-rails', '2.0.2'

group :test do
  gem 'capybara', '1.1.2'
end

3.6高度なセットアップ

3.2でも簡単に説明しましたが、rspecコマンドを直接実行することは実用的ではありません。この節では、最初にbundle execを毎回コマンドに入力せずに済む方法を紹介します。次に、3.6.2でGuardを使用したテストスイートの自動化方法を紹介し、さらに3.6.3でSporkを使用したテストスイートの自動化方法も紹介します。最後に、Sublime Text上で直接テストを実行する方法を紹介します。このテクニックは、特にSporkと併用すると非常に便利です。

この節は、ほとんどが上級者向けの内容になっており、この節を飛ばしても次の章以降には何の影響もありません。この節の内容は先進的ですが、その分、本書の他の内容よりも陳腐化しやすいので、ご利用のシステムでこの節の例が完全に動作するとは限りません。この点をご了承願います。完全に動作させるには、Googleで最新情報を検索して調べることが必要になるでしょう。

3.6.1bundle execを追放する

3.2.1でも簡単に紹介しましたが、rakerspecコマンドを実行するときには、現在の環境に依存するgemをGemfileから読み込んでプログラムを実行するために、bundle execをコマンドの前に追加する必要があります (技術的な理由により、railsコマンドだけは例外です)。この節の作業はかなり厄介です。また、bundle execの入力を省略する方法を2とおりの方法で説明します。

RVM Bundler の統合

最初に、お勧めの方法としてRVMを使う方法を紹介します。RVMはバージョン1.11以降からBundlerとの統合が含まれています。最初に、以下に従ってRVMのバージョンを最新にします。

$ rvm get head && rvm reload
$ rvm -v

rvm 1.15.6 (master)

バージョン1.11.x以降のRVMを使用していれば、インストールされたgemは自動的に適切なBundlerの環境で実行されますので、それ以上何もしなくても以下のようにbundle execを省略してで実行できます。

$ rspec spec/

bundle execを省略することができました。 このとおりにできた場合は、この節の残りはスキップしてください。

新しいバージョンのRVMを使うことができない場合は、Ruby Version Managerがローカル環境で自動的に設定する適切な実行ファイルがあれば、RVM Bundler integration16bundle execを省略することができます。一見奇妙ですが、実行方法は簡単です。最初に以下の2つのコマンドを実行します。

$ rvm get head && rvm reload
$ chmod +x $rvm_path/hooks/after_cd_bundler

次に以下のコマンドを実行します。

$ cd ~/rails_projects/sample_app
$ bundle install --without production --binstubs=./bundler_stubs

魔法のように見えますが、これらのコマンドでRVMとBundlerを統合できます。そして、rakerspecなどのコマンドを適切な環境で自動的に実行してくれます。bundler_stubsディレクトリを.gitignoreファイルに追加します (リスト3.32)。これらのファイルはローカル環境でしか使用しないためです。

リスト3.32 .gitignoreファイルにbundler_stubsを追加する。
# Ignore bundler config
/.bundle

# Ignore the default SQLite database.
/db/*.sqlite3

# Ignore all logfiles and tempfiles.
/log/*.log
/tmp

# Ignore other unneeded files.
doc/
*.swp
*~
.project
.DS_Store
bundler_stubs/

3.6.2guardなど、他の実行ファイルを追加する場合は、bundle installコマンドを再度実行してください。

$ bundle install --binstubs=./bundler_stubs

binstubsオプション

RVMを使用していない場合でも、 rubygems-bundlerというgemを使うことで、bundle execを省略することができます。

$ gem install rubygems-bundler
$ gem regenerate_binstubs

上記の2つ目のコマンドを実行すると、ローカル環境に必要な全ての設定を自動で作成してくれます。このため、今後は以下のようにテストスイートを実行できます。

$ rspec spec/

同様にrakeなども以下のように実行できます。

$ rake db:migrate

3.6.2guardなど、他の実行ファイルを追加した場合は、gem regenerate_binstubsコマンドを再実行する必要があるかもしれません (ほとんどの場合、再実行しなくても問題ないとは思いますが、念のため)。

本書の以後の章では、この節をスキップする方に配慮して明示的にbundle execを与えてコマンドを実行するようにしています。もちろん、ご利用のシステムが適切に設定されていれば、bundle execを省略しても構いません。

3.6.2Guardによるテストの自動化

rspecコマンドは、テストのたびにコマンドラインに移動して手動でコマンドを実行しなければならない点が面倒です (もうひとつ、テストスイートの起動が遅いという問題がありますが、これは3.6.3で対応します)。この節では、テストを自動化するGuardというgemの使い方を紹介します。Guardはファイルシステムの変更を監視し、たとえばstatic_pages_spec.rbファイルを変更すると自動的にテストを実行します。さらに、home.html.erbファイルが変更されると static_pages_spec.rbが自動的に実行されるようにGuardを設定することもできます。

最初に、リスト3.33のように、guard-rspecGemfileに追加します。

リスト3.33 サンプルアプリケーションのGemfileにGuardを追加する。
source 'https://rubygems.org'

gem 'rails', '3.2.14'

group :development, :test do
  gem 'sqlite3', '1.3.5'
  gem 'rspec-rails', '2.11.0'
  gem 'guard-rspec', '1.2.1'
end

# Gems used only for assets and not required
# in production environments by default.
group :assets do
  gem 'sass-rails',   '3.2.5'
  gem 'coffee-rails', '3.2.2'
  gem 'uglifier', '1.2.3'
end

gem 'jquery-rails', '2.0.2'

group :test do
  gem 'capybara', '1.1.2'
  # System-dependent gems
end

group :production do
  gem 'pg', '0.12.2'
end

次に、システムに依存するgemを以下のようにtestグループに追加します (OS Xユーザーの場合、Growlとgrowlnotifyもインストールする必要があります)。

# Test gems on Macintosh OS X
group :test do
  gem 'capybara', '1.1.2'
  gem 'rb-fsevent', '0.9.1', :require => false
  gem 'growl', '1.0.3'
end 
# Test gems on Linux
group :test do
  gem 'capybara', '1.1.2'
  gem 'rb-inotify', '0.8.8'
  gem 'libnotify', '0.5.9'
end 
# Test gems on Windows
group :test do
  gem 'capybara', '1.1.2'
  gem 'rb-fchange', '0.0.5'
  gem 'rb-notifu', '0.0.4'
  gem 'win32console', '1.3.0'
end 

次にbundle installを実行してgemをインストールします。

$ bundle install

Guardを初期化し、RSpecと一緒に動作するようにします。

$ bundle exec guard init rspec
Writing new Guardfile to /Users/mhartl/rails_projects/sample_app/Guardfile
rspec guard added to Guardfile, feel free to edit it

結合テストとビューが更新されたら自動的に適切なテストが実行されるように、生成されたGuardfileを編集します (リスト3.34)。

リスト3.34 デフォルトのGuardfileに追記する。
require 'active_support/core_ext'

guard 'rspec', :version => 2, :all_after_pass => false do
  .
  .
  .
  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
  .
  .
  .
end

上のコードにある以下の行に注目してください。

guard 'rspec', :version => 2, :all_after_pass => false do

これは、テストのパスに失敗した後に、他の余分なテストが実行されないようにするためのものです (Red-Green-Refactorのサイクルを早めるため)。

以上の準備が終われば、以下のコマンドでguardを起動できます。

$ bundle exec guard

bundle execを省略するには、3.6.1の手順を再度実行してください。

spec/routingディレクトリが見つからないというエラーが表示された場合は、以下のように空のディレクトリを作ることで回避できます (訳注: 同様にして、spec/controllersのエラーが表示された場合も$ mkdir spec/controllersと実行することで解決できます)。

$ mkdir spec/routing

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

bundle exec rspecを実行すると、テストが開始されるまでしばらく時間がかかることにお気付きかもしれません。テストがいったん開始されればすぐに終了します。これは、RSpecを実行するたびにRailsの環境全体を読み込み直す必要があるためです。Sporkテストサーバー17はこの問題を解決するためのものです。Sporkは環境を1回だけ読み込み、今後実行するテストのためのプロセスを管理します。Sporkは3.6.2のGuardと組み合わせるとさらに便利です。

最初に、sporkに依存するgemをGemfileに追加します (リスト3.35)。

リスト3.35 サンプルアプリケーションのGemfile
source 'https://rubygems.org'

gem 'rails', '3.2.14'

.
.
.
group :development, :test do
  .
  .
  .
  gem 'guard-spork', '1.2.0'
   gem 'childprocess', '0.3.6'
  gem 'spork', '0.9.2'
end
.
.
.

次に、bundle installでSporkをインストールします。

$ bundle install

次に、Sporkの設定にbootstrapを指定します。

$ bundle exec spork --bootstrap

そして、環境の読み込みを一回で済ますため、spec/spec_helper.rbの中で環境をpreforkのブロックで読み込むように、RSpecの設定を変更する必要があります (リスト 3.36)。

リスト3.36 Spork.preforkブロックへの環境読み込みを追加する。
spec/spec_helper.rb
require 'rubygems'
require 'spork'

Spork.prefork do
  # Loading more in this block will cause your tests to run faster. However, 
  # if you change any configuration or code from libraries loaded here, you'll
  # need to restart spork for it take effect.
  # This file is copied to spec/ when you run 'rails generate rspec:install'
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  # Requires supporting ruby files with custom matchers and macros, etc,
  # in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  RSpec.configure do |config|
    # == Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr
    config.mock_with :rspec

    # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
    config.fixture_path = "#{::Rails.root}/spec/fixtures"

    # If you're not using ActiveRecord, or you'd prefer not to run each of your
    # examples within a transaction, remove the following line or assign false
    # instead of true.
    config.use_transactional_fixtures = true

    # If true, the base class of anonymous controllers will be inferred
    # automatically. This will be the default behavior in future versions of
    # rspec-rails.
    config.infer_base_class_for_anonymous_controllers = false
  end
end

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

end

Sporkを起動する前に、以下のようにテストスイートを実行して、基準となる実行時間を測定します。

$ time bundle exec rspec spec/requests/static_pages_spec.rb
......

6 examples, 0 failures

real	0m8.633s
user	0m7.240s
sys	0m1.068s

上の実行結果では、実際のテストは1/10秒以下で実行されますが、テストスイートは7秒以上かかっています。実行時間のスピードアップのため、別のターミナルウィンドウを開いてアプリケーションのルートディレクトリに移動し、以下のようにSporkサーバーを起動します。

$ bundle exec spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!

bundle execを省略するには、3.6.1の手順を再度実行してください。別のターミナルウィンドウでは、--drb (“distributed Ruby” の略) オプションを追加してテストスイートを実行すると、環境読み込みのオーバーヘッドが大幅に減少していることが確認できます。

$ time bundle exec rspec spec/requests/static_pages_spec.rb --drb
......

6 examples, 0 failures

real	0m2.649s
user	0m1.259s
sys	0m0.258s

rspecを実行するたびに--drbオプションを追加するのは不便なので、アプリケーションのルートディレクトリにある.rspecファイルにリスト3.37の内容を記載することをお勧めします。

リスト3.37 自動的にSporkを使うためのRSpecの設定。
.rspec
--colour
--drb

Sporkを使う上でひとつ注意があります。preforkで読み込むファイル (たとえばroutes.rb) が変更された場合、Sporkサーバーを再起動して新しいRailsの環境を再度読み込む必要があります。パスするはずのテストが失敗した場合は、Ctrl-CでSporkサーバーを停止して再起動してください。

$ bundle exec spork
Using RSpec
Loading Spork.prefork block...
Spork is ready and listening on 8989!
^C
$ bundle exec spork

GuardにSporkを導入する

SporkはGuardと併用すると非常に便利です。設定を行うと、以下のようにコマンド上で併用することができます。

$ bundle exec guard init spork

そのためには、リスト3.38に示したようにGuardfileを変更する必要があります。

リスト3.38 Spork向けに更新したGuardfile
require 'active_support/core_ext'

guard 'spork', :rspec_env => { 'RAILS_ENV' => 'test' } do
  watch('config/application.rb')
  watch('config/environment.rb')
  watch(%r{^config/environments/.+\.rb$})
  watch(%r{^config/initializers/.+\.rb$})
  watch('Gemfile')
  watch('Gemfile.lock')
  watch('spec/spec_helper.rb')
  watch('test/test_helper.rb')
  watch('spec/support/')
end

guard 'rspec', :version => 2, :all_after_pass => false, :cli => '--drb' do
  .
  .
  .
end

guardの変数に:cli => --drbが追加されました。この設定により、GuardがコマンドラインインタフェースからSporkサーバーを使うようになります。上の設定には、第5章から使用するspec/support/ディレクトリを監視するコマンドも追加してあります。

設定が完了したら、以下のguardコマンドでGuardとSporkを同時に起動します。

$ bundle exec guard

Guardは自動的にSporkサーバーを起動するため、テスト実行時のオーバヘッドを劇的に削減できます。

Guard、Spork、テスト通知機能 (オプション) を使用して便利なテスト環境を構築することで、テスト駆動開発がやみつきになることでしょう。詳細については、Railsチュートリアルのスクリーンキャスト18を参照してください。

3.6.4Sublime Text上でテストする

Sublime Textを使用していれば、エディタの中から直接テストを実行できる強力なヘルパーコマンドを利用できます。このコマンドを利用するには、Sublime Text 2 Ruby Tests19に記載されている、自分の環境向けの説明に従って設定を行なってください。著者の環境 (Macintosh OS X) の場合、以下のようにコマンドをインストールします。

$ cd ~/Library/Application\ Support/Sublime\ Text\ 2/Packages
$ git clone https://github.com/maltize/sublime-text-2-ruby-tests.git RubyTest

参考: Rails Tutorial Sublime Text20にある説明に従って設定することもできます。

Sublime Textを再起動すると、以下のようなコマンドがRubyTestパッケージによって提供されます。

  • Command-Shift-R: itブロック内のテストについては、テストを1回だけ実行します。describeブロック内のテストについては、一連のテストを実行します。
  • Command-Shift-E: 最後に実行したテストを再度実行します。
  • Command-Shift-T: 現在のファイルにあるテストをすべて実行します。

比較的小さいプロジェクトであってもテストスイートの実行には時間がかかるため、テストを一度に1つだけ実行したり、小規模なテストグループだけを実行したりできるのは大きな長所です。従来のテストでは、たった1つのテストを実行するだけでも、Railsの環境に匹敵するオーバーヘッドが発生してしまいました。このため、上述のテスト用コマンドとSporkの組み合わせは非常に相性が良いのです。Sporkは、テストを実行するたびに発生していたテスト環境起動によるオーバーヘッドを取り除いてくれるため、1つのテストを実行するたびにすべてをテストするのと同程度のオーバーヘッドが発生するようなことがなくなります。個人的には、以下のテスト手順がお勧めです。

  1. ターミナルウィンドウでSporkを起動する。
  2. テストを1つ (または小規模なテストグループ) を作成する。
  3. Command-Shift-Rコマンドでテストが失敗することを確認する。
  4. 対応するアプリケーションコードを作成する。
  5. Command-Shift-Eコマンドで上のテストと同じテストを実行し、今度は成功することを確認する。
  6. 2-5の手順を繰り返す。
  7. 中継点 (コミットの直前など) に到達したら、 コマンドラインでrspec spec/を実行してテストスイートをすべて実行し、成功することを確認する。

Sublime Textの中からテストが実行できることはもちろん便利ですが、場合によってはGuardの方が便利なこともあると思います。上の手順は、著者が個人的に常用しているテスト駆動開発のテクニックとしてご紹介しました。

  1. CapybaraはWebratの後続プロジェクトであり、世界最大の齧歯類が名前の由来です。
  2. なお、installは省略可能です。実は、bundleコマンドはbundle installのエイリアスでもあります。
  3. システムによっては、リスト1.7の.gitignoreを参考にして、さらに便利な設定にすることもできます。
  4. https://github.com/railstutorial/sample_app_2nd_ed 
  5. 実は、これらのファイルに対するリクエストはRails内のスタックに決して到達しないようになっています。そのようなファイルへのリクエストは、ファイルシステムに直接渡されます (詳細については「The Rails 3 Way」を参照してください)。
  6. いつもと同じように、subl コマンドを、使用しているテキストエディタを呼び出すコマンド (vim, emacsなど) に置き換えてください。
  7. HTMLは常に変化しています。doctypeを明示的に宣言することで、今後もブラウザが正しくページを描画してくれる可能性が高まります。極めてシンプルなdoctype宣言である<!DOCTYPE html>は、最新の標準HTML (HTML5) であることを示しています。
  8. ここで静的なページを作るために採用した方法は、おそらく最もシンプルな方法です。ただし他にも方法はあります。最適な方法は状況によって異なり、たとえば極めて多数の静的なページを1つのStaticPagesコントローラだけまかなおうとすると重荷になる可能性があります。今回はいくつかの静的なページを作るだけなので、重荷にはなりません。Railsで静的なページを作成するテクニックの詳細については、ブログhas_many :throughの「simple pages」という記事を参照してください。警告: この記事の内容はかなり高度なので、もっと後になって読む方がよいかもしれません。
  9. 毎回bundle execを追加するのは面倒ですが、3.6のオプションを使うと省略できます。
  10. 筆者は普段、ターミナルやテキストエディタの背景は黒色にしていますが、明るい色の背景の方がスクリーンショットの見栄えが良いので、(一時的に) 明るい背景を使用しています。
  11. 改行は、行の末尾と次の行の始まりを示します。コードの中では、\nの文字列で表します。
  12. もちろん、人間がいちいち文字数を数えていたら頭がどうにかなってしまいます。だからこそ、多くのテキストエディタにはこれらを支援する機能が備わっています。たとえば、図1.1をもう一度見てみると、コードを80文字以下に抑えるための小さな縦線が右側に見えます (実際には少し余裕を持たせて78列にしてあります) 。TextMateを使用していれば、View > Wrap Column > 78で設定できます。Sublime Textを使用していれば、View > Ruler > 78、またはView > Ruler > 80で設定できます。
  13. 2番目によく使われているHamlというテンプレートシステムもあります。Hamlは個人的に非常に気に入っているのですが、初心者向けのチュートリアルで使うにはやや標準から外れています。
  14. Railsでの開発経験者であれば、この時点でcontent_forの使用を検討すると思いますが、残念ながらAsset Pipelineと併用すると正常に動作しないことがあります。provide関数はcontent_forの代替です。
  15. Rubyを勉強したことのある方であれば、Railsはブロックの内容をyieldしていると推測することでしょう。そして、その推測はおそらく正しいでしょう。しかし、Rails開発のためにこれらの詳細を知る必要はありません。
  16. https://rvm.io/integration/bundler 
  17. sporkはspoon-forkを組み合わせた造語です。このプロジェクト名は、POSIXforkにおけるSporkの用法をもじったものです。
  18. http://railstutorial.jp/screencasts 
  19. https://github.com/maltize/sublime-text-2-ruby-tests 
  20. https://github.com/mhartl/rails_tutorial_sublime_text