Ruby on Rails チュートリアル
-
第2版 目次
- 第1章ゼロからデプロイまで
- 第2章デモアプリケーション
- 第3章ほぼ静的なページの作成
- 第4章Rails風味のRuby
- 第5章レイアウトを作成する
- 第6章ユーザーのモデルを作成する
- 第7章ユーザー登録
- 第8章サインイン、サインアウト
- 第9章ユーザーの更新・表示・削除
- 第10章ユーザーのマイクロポスト
- 第11章ユーザーをフォローする
|
||
第2版 目次
|
||
最新版を読む |
Ruby on Rails チュートリアル
プロダクト開発の0→1を学ぼう
下記フォームからメールアドレスを入力していただくと、招待リンクが記載されたメールが届きます。リンクをクリックし、アカウントを有効化した時点から『30分間』解説動画のお試し視聴ができます。
メール内のリンクから視聴を開始できます。
第2版 目次
- 第1章ゼロからデプロイまで
- 第2章デモアプリケーション
- 第3章ほぼ静的なページの作成
- 第4章Rails風味のRuby
- 第5章レイアウトを作成する
- 第6章ユーザーのモデルを作成する
- 第7章ユーザー登録
- 第8章サインイン、サインアウト
- 第9章ユーザーの更新・表示・削除
- 第10章ユーザーのマイクロポスト
- 第11章ユーザーをフォローする
第3章ほぼ静的なページの作成
本章では、今後のチュートリアルを楽に理解できるように、簡単なサンプルアプリケーションを開発してみます。本書を通して開発するアプリケーションは、最終的にはユーザーやマイクロポスト、ログイン/ログアウトなどの認証機能を持ちますが、まずは簡単なトピックである「静的なページの作成」から始めます。非常にシンプルなページではありますが、静的なページを作成することは良い経験になり、また、多くの示唆も得られます。私達がこれから開発するアプリケーションにとって、最高のスタート地点といえるでしょう。
Rails はデータベースと連携して動的なWebサイトを開発するように設計されていますが、HTMLファイルだけで構成されている静的なページを作ることもできます。実際、静的なページをRailsで作ることのメリットもあります。たとえば、あとでほんの少し動的なコンテンツを追加することができます。本章では、このような静的なページの作成について学んでいきます。まずは、自動化テストの雰囲気を掴んでいきます。自動化テストは、私達のコードが正しく動いているという自信を与えてくれます。さらに、良いテストを書くことで、自信をもってリファクタリングを行うことができます。たとえば、フォームの振る舞いを変更せずに、フォーム内で使われている関数を書き換えたいときに便利です。
本章には多くのサンプルコードがあります。特に 3.2 や 3.3 で多くのコードを紹介していますが、Rubyが初めての方は、コードを隅々まで理解しなければならないのだろうかと心配する必要はありません。1.1.1 で紹介したように、まずはテストをコピー&ペーストしてみて、アプリケーションがうまく動くかどうか検証してみると良いでしょう。この時点では、テストがどのようにして動くかは気にしなくても大丈夫です。また、第4章ではRubyについて解説します。そこではRubyの書き方やコンセプトについて学びます。最後に、本書ではRSpecを使ったテストを繰り返し実施していきます。ですから、もし途中でよく分からないテストがあったとしても、読み飛ばして先に進むことをお勧めします。1、2章先を読み進めた後に読み返してみると、当初はよく分からなかったテストが、実はとてもシンプルであることを理解できるはずです (Code SchoolのRSpecコースの履修を検討してみるのもよいでしょう。このコースはRSpecに関する多くの疑問に答えてくれるという読者からの報告もあります)。
では、第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.47を参照してください。これが最終的なGemfileになります)。
Gemfile
。 source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0
gem 'rails', '4.0.5'
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.5'
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
group :production do
gem 'pg', '0.15.1'
gem 'rails_12factor', '0.0.2'
end
このGemfileでは、開発環境とテスト環境でrspec-railsを使うようにしています。このため、開発環境ではRSpec固有のジェネレーターにアクセスすることができます。同様に、テスト環境でもRSpecを使用してテストを実行できるようになります。Gemfileに記述したrspec-railsが依存関係を解決してくれるため、 個々の環境にRSpec自身を手動でインストールする必要がなくなり、自動的にインストールされるようになります。同様の理由で、Capybara gemもGemfileに記述しています。これは、英語に近い文法を使って、ユーザーとサンプルアプリケーションの対話的操作をシミュレーションできるGemです1。CapybaraはSeleniumなどのgemに依存しています。
group :production do
gem 'pg', '0.15.1'
gem 'rails_12factor', '0.0.2'
end
Herokuは、開発環境と本番環境とで同じデータベースを使うことを推奨していますが、今回開発するサンプルアプリケーションでは、データベースが異なっていても特に問題はありません。また、SQLiteはPostgreSQLに比べて極めて簡単に セットアップできます。なお、ローカルの開発マシンへのPostgreSQLのインストールと構築については、3.5の演習課題に含めました。
Gemfileに新しく追加したgemを実際にインストールするには、bundle update
とbundle install
を実行します。
$ bundle install --without production
$ bundle update
$ bundle install
1.4.1や第2章でも説明したように、--without productionオプションを追加することで、本番環境のgemのみをインストールしないようにすることができます。注: このオプションは “remembered option” と呼ばれるもので、このオプションを一度実行するとコマンドに保存され、今後Bundlerを実行するときにオプションを追加する必要がなくなります。このため、今後は単にbundle install
を実行するだけで、自動的に本番環境用gemをスキップできるようになります2。
このサンプルアプリケーションはパブリックリポジトリ (public repository) として公開されるので、Railsでセッション変数の暗号化に使用するための、いわゆる秘密トークン (secret token) を必ず更新することが重要です。リスト3.2のコードを使用して、トークンをハードコードすることなく動的に生成するようにしてください。(リスト3.2のコードはやや高度なもので、本書のこの段階では時期尚早ではありますが、重大なセキュリティ上の問題が発生する可能性を回避するために、あえてこの段階に含めておくのがよいと考えます)。この場合、必ずリスト1.7の改訂版.gitignore
を使用するようにし、.secret
キーをリポジトリでうっかり公開してしまうことのないようにしてください。
config/initializers/secret_token.rb
# Be sure to restart your server when you modify this file.
# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rake secret` to generate a secure secret key.
# Make sure your secret_key_base is kept private
# if you're sharing your code publicly.
require 'securerandom'
def secure_token
token_file = Rails.root.join('.secret')
if File.exist?(token_file)
# Use the existing token.
File.read(token_file).chomp
else
# Generate a new token and store it in token_file.
token = SecureRandom.hex(64)
File.write(token_file, token)
token
end
end
SampleApp::Application.config.secret_key_base = secure_token
次に、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.3のように、アプリケーションのルートディレクトリに置かれているREADME
ファイルをわかりやすく書き換えてみましょう。
README
ファイル。 # Ruby on Rails チュートリアル:サンプルアプリケーション
これは、以下のためのサンプルアプリケーションです。
[*Ruby on Rails Tutorial*](http://railstutorial.jp/)
by [Michael Hartl](http://www.michaelhartl.com/).
次に、拡張子を .md
に変更し、Markdownファイルとして認識できるようにします。その後、これらの変更をコミットします。
$ git mv README.rdoc README.md
$ git commit -am "Improve the README"
1.3.5でgit commit -a -m "Message"
というGitコマンドを実行したことを思い出してください。あのときは “すべてを変更” (-a
) オプションとコミットメッセージを追加するオプション (-m
) を使用しました。上で実行したコマンドで示したように、実はこれらの2つのオプションを1つにまとめてgit commit -am "Message"
と実行することができます。
本書では今後、このサンプルアプリケーションを使っていくことになるので、GitHub上にリポジトリを作成し、プッシュしておくと良いでしょう。
$ git remote add origin https://github.com/<ユーザー名>/sample_app.git
$ git push -u origin master
ここまで作業を進めると、著者がGitHubにアップロードしたRailsチュートリアルのサンプルアプリケーションのようになります (ユーザー名はrailstutorialで、アプリケーション名はsample_app_rails_4と若干異なります)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のアクションとビューに静的なHTMLだけを含めたものを作成します5。Railsのアクションは、コントローラ (1.2.6で紹介した MVCの Cに該当) の中に置かれる機能で、共通の目的を持つ一連のアクションをコントローラ内にまとめることができます。コントローラについては第2章でも簡単に触れましたが、第6章で説明するREST アーキテクチャを読むと、より深く理解することができます。一言で言うと、コントローラとは (基本的に動的な) Webページの集合を束ねるコンテナのことです。
現在どのディレクトリで作業しているかがわからなくなった場合は、1.2.3 (図 1.2)を再度参照して、Rails のディレクトリ構造を確認してください。この節では、主にapp/controllers
ディレクトリやapp/views
ディレクトリ内で作業を進めます (なお 3.2 では、新しいディレクトリをさらに1つ追加します)。好みのテキストエディタまたはIDEを使用してサンプルアプリケーションを開いてください (コラム 3.1)。
Railsのすべてのディレクトリを一覧できるテキストエディタまたはIDEが使用できると非常に便利です。テキストエディタやIDEの細かな使用法は残念ながらそれぞれ異なりますが、どのツールを使ってもRailsアプリケーションのディレクトリを開くことができます。Unix系のシステムでは、ドット.
でカレントディレクトリを表現できるので、コマンドライン上で以下を実行してRailsアプリケーションの現在のディレクトリを開き、使用するエディタを呼び出してみてください。
$ cd ~/rails_projects/sample_app $ <エディタ名> .
たとえば、Sublime Text でサンプルアプリケーションを開く場合は、以下を実行します。
$ subl .
Vimの場合は以下を実行します。
$ vim .
(vim
というコマンド名は、使用している「フレーバー」によってはgvim
やmvim
などになっていることがあります。)
さっそく静的なページを作ってみましょう。まずは、1.3.5で紹介したように、Gitを使ってトピックブランチを作ります。今回のように新しい機能やページを作成するときは、masterブランチではなくトピックブランチで作業するのがよいでしょう。Git でバージョン管理をしている場合は、次のコマンドでトピックブランチを作成してください。
$ git checkout -b static-pages
Railsにはgenerate
というスクリプトがあり、このスクリプトにコントローラ名を入力するだけで、この魔法のようなスクリプトがコントローラを作成してくれます。これより、複数の静的なページを取り扱うStaticPagesコントローラを作成します。具体的には、HomeページとHelpページ、Aboutページで使用するアクションを作ってみます。generate
スクリプトは、任意の数のアクションを引数に取ることができます。まずは、上記の3つのアクションのうち、最初の2つのアクションを作ってみます (リスト 3.4)。
$ 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.2で元に戻す方法を紹介しています。
どれほど十分に気を付けていたとしても、Railsアプリケーションの開発中に何か失敗してしまうことはありえます。ありがたいことに、Railsにはそのような失敗をカバーする機能がいくつもあります。
一般的なシナリオの1つは、生成したコードを元に戻したい場合です。たとえば、コントローラを生成した後で、もっといいコントローラ名を思い付いた場合などです。リスト 3.4に示したように、コントローラを生成すると、コントローラファイル以外にも多数のファイルが作成されます。自動生成されたコードを元に戻すためには、新規作成されたファイルを削除するだけではなく、既存のファイルに挿入されたコードも削除する必要があります (実際、routes.rb ファイルに自動的に追加されたコードも元に戻す必要があります) 。このようなときは、rails destroyコマンドを実行するだけで元に戻すことができます。たとえば次の2つのコマンドは、自動生成と、それに対応する取り消し処理の例です。
$ rails generate controller FooBars baz quux $ rails destroy controller FooBars baz quux
なお 第6章でも、以下のようにモデルを自動生成する方法を紹介します。
$ rails generate model Foo bar:string baz:integer
モデルの自動生成についても、同様の方法で元に戻すことができます。
$ rails destroy model Foo
(上のコマンドからわかるように、モデル名以外の引数は不要です。その理由については第6章で説明しています)。
また、第2章でも簡単に紹介しましたが、マイグレーションの変更を元に戻す方法もあります。詳細は第6章で説明します。簡単に言うと、まず以下のコマンドでデータベースのマイグレーションを変更できます。
$ rake db:migrate
以下のコマンドで1つ前の状態に戻すことができます。
$ rake db:rollback
最初の状態に戻したい場合は、以下のコマンドを使います。
$ rake db:migrate VERSION=0
既にお気付きの方もいると思いますが、マイグレーションは逐次的に実行され、それぞれのマイグレーションに対してバージョン番号が付与されます。したがって、上記の 0を別の数字に置き換えることによって、指定したバージョンの状態に戻すことができます。
これらの方法に慣れることで、開発中に袋小路に迷い込んでしまった場合でも、これらの機能を使って元の状態に復帰することができます。
リスト 3.4のようにStaticPagesコントローラを生成すると、config/routes.rbファイルが自動的に更新されます。Railsは、この routes
ファイルに記述されている内容 (ルーティング) に従って、 複数のURLとWebページを対応付けます。ここで、初めてconfig
ディレクトリについて触れます。このディレクトリは図 3.1のような構成になっています。config
ディレクトリという名前のとおり、このディレクトリ内にあるファイルは、Railsがアプリケーションの設定を読み込む時に必要になります。
先ほどhome
アクションとhelp
アクションを生成したので、次のリスト 3.5 に示されるように、routesファイルにはそれぞれのアクションで使用されるルールが定義されています。
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というURLに対するリクエストを、StaticPagesコントローラのhome
アクションと結びつけています。具体的には、get
と書くことで、GETリクエストに対して該当するアクションを結びつけています。なお、ここでいう GETリクエストとは、HTTP (HyperText Transfer Protocol) (コラム 3.3) が対応しているメソッドの1つです。今回の場合は、StaticPagesコントローラ内にhome
アクションを追加したので、/static_pages/homeにアクセスすることでページを取得 (GET) できるようになりました。/static_pages/homeにアクセスして結果を表示します (図3.2)。
HTTP (HyperText Transfer Protocol) には4つの基本的な操作があり、それぞれGET、POST、PATCH、DELETEという4つの動詞に対応づけられています。(ローカル環境でRailsアプリケーションを開発しているときは、クライアントとサーバーが同じコンピュータ上で動いていますが、一般的には、それぞれ別のコンピュータで動作しているという点を理解しておいてください)。Railsを含む多くのWebフレームワークは、HTTPの各操作を発展させたREST アーキテクチャの影響を受けています。第2章でも簡単に触れましたが、第7章では、より深い内容について学びます。
GET は、最も頻繁に使用されるHTTP操作で、主にWeb上のデータを読み取る際に使われます。“ページを取得する” という意味のとおり、Webブラウザはgoogle.comやwikipedia.orgのようなWebサイトを開くたびにGETリクエストを送信しています。Railsアプリケーションでは、POSTリクエストは何かを作成するときによく使われます (なお本来のHTTPでは、POSTを更新に使ってもよいとしています)。たとえば、ユーザー登録フォームで新しいユーザーを作成するときは、POSTリクエストを送信します。他にも、PATCHと DELETEという2つの操作があり、それぞれサーバー上の何かを更新したり削除したりするときに使われます。これら2つの操作は、GETやPOSTほどは使用されていません。これは、ブラウザがPATCHとDELETEをネイティブでは送信しないからです。しかし、Ruby on Railsなどの多くのWebフレームワークは、ブラウザがこれらの操作のリクエストを送信しているかのように見せかける技術 (偽装) を駆使して、PATCHとDELETEという操作を実現しています。
なお、以前のバージョンのRailsではPATCHではなくPUTが使用されていました。PUTはRails 4.0でも依然サポートされてはいますが、PATCHの方が意図したHTTPの使用法により適しているので、新しいアプリケーションではPATCHが推奨されています。
このページがどのようにして表示されるのかを理解するために、まずはテキストエディタでStaticPagesコントローラを開いてみましょう。リスト3.6のような内容になっているはずです。ここで、第2章のUsersコントローラやMicropostsコントローラとは異なり、StaticPagesコントローラは一般的なRESTアクションに対応していないことに注意してください。これは、静的なページの集合に対しては、適切なアクションと言えます。言い換えると、RESTアーキテクチャは、あらゆる問題に対して最適な解決方法であるとは限らないということです。
class StaticPagesController < ApplicationController
def home
end
def help
end
end
リスト 3.6のclass
というキーワードから、static_pages_controller.rb
はStaticPagesController
というクラスを定義していることがわかります。クラスは、関数 (メソッドとも呼ばれます) をまとめるときに便利な手法です。今回の例では、def
というキーワードを使って、home
アクションやhelp
アクションを定義しています。また、山括弧<
は、StaticPagesController
が ApplicationController
というRailsのクラスを継承していることを示しています。この後も説明しますが、今回作成したページには、Rails特有の機能が多数使用されています (具体的なクラスや継承については、4.4で詳しく説明します)。
今回のStaticPagesコントローラにあるメソッドは、以下のようにどちらも最初は空になっています。
def home
end
def help
end
純粋なRuby言語であれば、これらのメソッドは何も実行しません。しかし、Railsでは動作が異なります。StaticPagesController
はRuby のクラスですが、ApplicationController
クラスを継承しているため、StaticPagesControllerのメソッドはRails特有の振る舞いをします。具体的には、/static_pages/homeというURLにアクセスすると、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)。
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)。
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で構成されている」という点です。
本章では以後、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のようにコードを書いてみましょう。
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'
expect(page).to have_content('Sample App')
end
end
end
リスト 3.9のコードはすべてRubyで書かれています。しかし、Rubyを勉強したことがある人にとっては見慣れない書き方に見えるかもしれません。これは、Rubyの柔軟性の高さを応用して、RSpecがテスト用の独自言語 (Domain-Specific Language: DSL) を定義しているからです。ここで重要なのは、RSpecを使うためにRSpec独自の文法を理解する必要はないということです。最初のうちは魔法のように見えるかもしれませんが、RSpecやCapybaraは英語に近い形で読めるように設計されています。したがって、本チュートリアルで取り上げるテスト例を読み進めるだけで、英語圏の方ならRSpecの文法を楽に扱えるようになります。
リスト3.9にはdescribe
というブロックがあり、そのブロック内には it "…" 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
最初の行では、Homeページに対するテストであることを記述しています。これは単なる文字列であり、好きな文字列を使用できます。RSpecはこの文字列を解釈しないので、人間にとってわかりやすい説明をここに書くようにします。次の行では、「/static_pages/home
のHomeページにアクセスしたとき、“Sample App”という語が含まれていなければならない」と記述しています。最初の行と同様で、RSpec はダブルクォート (") で囲まれた文字列を無視しますので、ここにも人間にとってわかりやすい説明文を書きましょう (訳注: 英語で should have...と書くことで、メソッドのitと整合性が取れます)。その次の行について説明します。
visit '/static_pages/home'
上の行は、Capybaraのvisit
機能を使って、ブラウザでの/static_pages/home
URLへのアクセスをシミュレーションします。
expect(page).to have_content('Sample App')
その次の行 (上のコード) では、これもCapybaraが提供するpage
変数を使って、アクセスした結果のページに正しいコンテンツが表示されているかどうかをテストしています。
テストが正常に実行されるようにするために、リスト3.10に示した1行を spec_helper.rb
に追加する必要があります (Railsチュートリアルの第3版を出すときには、新しいfeature specsの技法を使用してこの行の追加を不要にすることを計画しています)。
spec/spec_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
.
.
.
RSpec.configure do |config|
.
.
.
config.include Capybara::DSL
end
テストではさまざまなオプションが使用できます。さらに高度で便利なツールもあり、詳細については3.6で説明します。今回は、コマンドラインでrspec
コマンドを実行してみてましょう (なお、bundle exec
をこのコマンドの前に置くことで、Gemfile
内で定義された環境でRSpecが実行されるように、明示的に指示することができます6)。
$ bundle exec rspec spec/requests/static_pages_spec.rb
上のコマンドを実行すると、「テストが失敗した」という結果が返ってきます。 結果の表示はシステムによって異なりますが、著者のシステムでは、図3.3のように失敗したテストが赤く表示されます7。
テストにパスするために、自動生成されたHomeページのHTMLをリスト3.11のように書き換えてみましょう。
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
を使って、指定したURLにジャンプする以下のリンクを追加しました (ちなみにアンカータグ内の “href” は “hypertext reference” と読みます)。
<a href="http://railstutorial.jp/">Ruby on Rails Tutorial</a>
結果を見るために、もう一度テストを実行してみましょう。
$ bundle exec rspec spec/requests/static_pages_spec.rb
著者のシステムでは、図3.4のように表示されました。
Helpページについても、Homeページの例を参考にして、同じようなテストとアプリケーションコードを使用できることが推測できます。最初に、文字列を ’Help’
に書き換えたテストを追加してみましょう (リスト 3.12)。
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'
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
上のコードでテストを実行してみます。
$ bundle exec rspec spec/requests/static_pages_spec.rb
テストのうち、1つは失敗するはずです。(注: 執筆作業軽減のため、今後はRSpecの出力結果を掲載しません。画面表示の内容はシステムによって大きく異なるうえ、各段階での出力画面数を把握してメンテナンスし続けるのが極めて困難なためです。ご了承ください。)
リスト3.13に示したように、このアプリケーションコードはリスト3.11と同じようなコードになります。
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であえて先送りにしたAboutページを新しく追加します。段階ごとにテストを作成してRSpecを実行することで、テスト駆動開発によってアプリケーション開発を進める方法を理解できるようになるでしょう。
赤 (Red)
赤色から緑色にするために、最初にAboutページ用の失敗するテストを書き、赤色にしましょう。 リスト3.12を参考にすることで、正しいテストを推測できるでしょう(リスト3.14)。
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'
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
緑 (Green)
3.1でも触れたように、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.15のように変更することでこの問題は解決します。
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.6のhome
アクションや help
アクションのパターンに従って、StaticPagesコントローラの中にabout
アクションを追加します (リスト 3.16)。
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
今度は、ビューなどの "テンプレート" が見当たらないというエラーメッセージが表示されます。
Missing template static_pages/about
これは、about
ビューを追加することで解決します。具体的には、app/views/static_pages
ディレクトリの中にabout.html.erb
というファイルを作成し、リスト 3.17の内容を書き込みます。
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>
今度は、RSpecを実行すると緑色になるはずです。
$ bundle exec rspec spec/requests/static_pages_spec.rb
もちろん、実際にブラウザを起動して、テストが正しく動いているかどうかを確かめることもできます (図3.5)。
リファクタリング
テストが緑色になったので、安心してコードをリファクタリングできるようになりました。多くの場合、コードを書き進めるうちに肥大化したり繰り返しが増えたりして、いつしか「悪臭を放つ」醜悪なコードになりはてるものです。コンピュータはコードが醜くても気にしませんが、開発者にとってはそうはいきません。だからこそ、頻繁にリファクタリングを実施し、コードを清潔な状態に保ち続けることが重要です。この点において、良いテストコードがあるということは非常に貴重です。リファクタリングする際にバグが混入する可能性を劇的に小さくしてくれるからです。
サンプルアプリケーションはかなり小規模なので、今のところリファクタリングの余地はありません。しかしコードの「悪臭」はやがてあらゆる箇所からただよいはじめ、そのうちにリファクタリングが必要になることでしょう。実際、3.3.4で早くもリファクタリングに取りかかることになります。
3.3少しだけ動的なページ
以上で、静的ページのアクションとビューを作成しました。次は、ページの内容を反映したタイトルを持ち、ページごとに内容が変化する、少しだけ動的なページを作成してみましょう。 タイトルを変えるぐらいのことが本当に動的コンテンツと呼べるかどうかは議論の余地があると思いますが、いずれにしろこのページは、第7章で紹介する真に動的なコンテンツの基礎となります。
3.2のテスト駆動開発の説明を読んでいない場合は、リスト3.15、リスト3.16、リスト3.17のコードを使って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
コマンドを使ってください)。実際のアプリケーション開発時には、上のような操作を行うことはおそらくないでしょう。ここでは、レイアウトファイルの役割をよりわかりやすく説明するために、最初にレイアウトファイルを無効にしました。
ページ | URL | 基本タイトル | 追加タイトル |
---|---|---|---|
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 Us" |
この節が完了するまでに、3つの静的ページのすべてのタイトルが、“Ruby on Rails Tutorial Sample App | Home”のように、最後の単語のみ表3.1に従ってページごとに変更されるようになります。最初に、リスト3.14のテストを元に、モデルに応じたタイトル表示のテスト (リスト3.18) を追加します。
it "should have the right title" do
visit '/static_pages/home'
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home")
end
このテストではhave_title
メソッドを使っています。これは与えられたコンテンツにHTML要素 (タイトル) があるかどうかをチェックします。つまり、以下のコードは
expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home")
title
タグの内容が以下のとおりになっていることを確認します。
"Ruby on Rails Tutorial Sample App | Home"
ここで注意していただきたいのは、与えられた内容に対して完全一致する文字列を使用しなくてはならないということではなく、以下のように部分文字列を指定するだけでもよいということです。
expect(page).to have_title("Home")
上のコードでもタイトル全体とマッチします。
リスト3.18と同じ要領で、StaticPagesのテストに3つの静的ページのテストを追加します (リスト3.19)。テストを追加するにつれて、似たようなコードが繰り返し使用されていることに注目してください。このような重複は5.3.4で取り除きます。
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'
expect(page).to have_content('Sample App')
end
it "should have the title 'Home'" do
visit '/static_pages/home'
expect(page).to have_title("Ruby on Rails Tutorial Sample App | 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("Ruby on Rails Tutorial Sample App | 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 Us'" do
visit '/static_pages/about'
expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us")
end
end
end
リスト 3.19の内容を反映後、以下を実行します。
$ bundle exec rspec spec/requests/static_pages_spec.rb
今度はテストが赤色 (テストが失敗する) になるはずです。
3.3.2タイトルのテストをパスさせる
今度は、タイトルのテストがパスするようにし、それと同時にWebページを正しく表示させるためのHTMLをすべて追加しましょう。現代的なWebページのマークアップは、基本的に以下のようになっています。
<!DOCTYPE html>
<html>
<head>
<title>Greeting</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
上の構造には次のものが含まれています。1) document type (doctype) は使用するHTMLのバージョン (ここではHTML5)8 をブラウザに対して宣言します。2) head
セクション。ここではtitle
タグに囲まれた "Greeting" という文字があります。3) body
セクション。ここではp
(paragraph) タグを使って、 “Hello, world!” と表示しています。(なお、HTMLではスペースやタブは無視されるので、インデントはあってもなくても大丈夫です。ただし、インデントがある方がHTMLのデータ構造を理解しやすくなります)。
上のHTML基本構造をHomeページに適用するとリスト3.20のようになります。
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.20ではリスト 3.19にある以下のテスト済みのタイトルを使います。
<title>Ruby on Rails Tutorial Sample App | Home</title>
従って、Homeページのテストはパスするはずです。HelpとAboutのテストはまだ赤色 (失敗) の状態ですので、リスト 3.21とリスト 3.22のようにコードを追加してテストを緑色 (成功) にします。
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.org/book">Rails Tutorial book</a>.
</p>
</body>
</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>
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.23のように、タイトルに含まれる"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.23は、ERbと呼ばれている、Rubyの埋め込みの最初の例です (これで、HTMLビューのファイルの拡張子が.html.erb
となっている理由をおわかりいただけたと思います)。 ERbはWebページに動的な要素を加えるときに使うテンプレートシステムです9。
<% provide(:title, 'Home') %>
上のコードに<% ... %>と書かれていることに注目してください。Railsはこの中でprovide
関数を呼び出し、:title
というラベルに ’Home’
という文字列を関連付けます10。次に、以下のコードでRubyのyield
関数を使用し、それを<%= ... %>表記 (上と微妙に異なることに注目してください) で囲むことにより、テンプレートにタイトルを挿入します11。
<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.24、リスト 3.25)。
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>
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.26のようになります。
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>
上のコードにある、以下の特殊なコードに注目してください。
<%= yield %>
このコードは、各ページの内容をレイアウトに挿入するためのものです。ここでは、このコードの詳細な動作を正確に理解することは重要ではありません。レイアウトを使用するうえでは、/static_pages/homeにアクセスすると、home.html.erb
の内容がHTMLに変換され、<%= yield %>の位置に挿入されるということだけ理解しておけば問題ありません。
Railsのデフォルトのレイアウトには、以下の行が追加されていることにも注目してください。
<%= stylesheet_link_tag ... %>
<%= 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.23、リスト3.24、 リスト3.25のビューには、レイアウトと重複するHTMLがまだ残っているので、それらを削除して、内部のコンテンツだけ残します。これにより、 リスト3.27、リスト3.28、リスト 3.29にそれぞれ示したように、ビューのコードが美しく簡潔なものになります。
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>
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>
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では、静的ページの開発のための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演習
- サンプルアプリケーションにContact (問い合わせ先) ページを作成してください。リスト3.19に従い、最初に、タイトルが “Ruby on Rails Tutorial Sample App | Contact” となっているかどうかを確認するテストを作成し、それによってURLが/static_pages/contactのページが存在することを確認するテストを作成してください。次に、リスト 3.30の内容をContactページに反映し、これらのテストにパスするようにしてください。(この演習の解答は5.3の説明に含まれています)。
- お気付きの方もいると思いますが、リスト3.19に示されている、StaticPagesコントローラのspecテストには若干の重複があります。特に、“Ruby on Rails Tutorial Sample App”というタイトルはそれぞれのタイトルのテストで繰り返し記述されています。引数として渡されたシンボルと同名の変数にブロックの評価値を格納する、RSpecの
let
関数を使い、リスト 3.31のようにテストを変更し、テストにパスするようにしましょう。リスト3.31では文字列の式展開 (string interpolation) が行われていますが、これについては4.2.2で紹介します。 - (上級者向け)「The Twelve-Factor App」のページでも説明されているように、互換性の問題によるエラーを最小限にとどめるために、開発/テスト/本番環境で共通のデータベースを使うことは良いことです。「Heroku instructions for local PostgreSQL installation」には、PostgreSQLをローカル環境にインストールする手順が紹介されてます。PostgreSQLを使う場合は、 リスト3.32に示したように、
Gemfile
からsqlite3 gemを削除し、pg gemのみを使うようにしてください。さらに、config/database.yml
ファイルと、PostgreSQLをローカル環境で動作させる方法を学ぶ必要があります。以上の情報を元に、PostgreSQLを使用して開発データベースとテストデータベースを作成し、それぞれ設定を行うことが、この課題のゴールです。PostgreSQLデータベースへの接続と内容表示には、Inductionというツールが便利です。警告: この演習はかなり難易度が高いので、上級者にのみお勧めします。もし行き詰まってしまったら、すぐにこの演習を飛ばして次の作業に進んでください。なお、既に説明したとおり、このチュートリアルで開発しているサンプルアプリケーションは、SQLiteとPostgreSQLのどちらについても完全に互換性があります。
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/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 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
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("#{base_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 Us'" do
visit '/static_pages/about'
expect(page).to have_title("#{base_title} | About Us")
end
end
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("#{base_title} | Contact")
end
end
end
Gemfile
。 source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0
gem 'rails', '4.0.5'
gem 'pg', '0.15.1'
group :development, :test do
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.5'
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
group :production do
gem 'rails_12factor', '0.0.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でも簡単に紹介しましたが、rake
やrspec
コマンドを実行するときには、現在の環境に依存するgemをGemfile
から読み込んでプログラムを実行するために、bundle exec
をコマンドの前に追加する必要があります (技術的な理由により、rails
コマンドだけは例外です)。この節の作業はかなり厄介です。また、bundle execの入力を省略する方法を2とおりの方法で説明します。
RVM Bundler の統合
最初に、お勧めの方法としてRVMを使う方法を紹介します。RVMはバージョン1.11以降からBundlerとの統合が含まれています。最初に、以下に従ってRVMのバージョンを最新にします。
$ rvm get stable
$ rvm -v
rvm 1.19.5 (stable)
バージョン1.11.x以降のRVMを使用していれば、インストールされたgemは自動的に適切なBundlerの環境で実行されますので、それ以上何もしなくても以下のようにbundle execを省略して実行できます。
$ rspec spec/
bundle exec
を省略することができました。 このとおりにできた場合は、この節の残りはスキップしてください。
新しいバージョンのRVMを使うことができない場合は、Ruby Version Managerがローカル環境で自動的に設定する適切な実行ファイルがあれば、RVM Bundler integration12でbundle 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を統合できます。そして、rake
やrspec
などのコマンドを適切な環境で自動的に実行してくれます。bundler_stubs
ディレクトリを.gitignore
ファイルに追加します (リスト3.33)。これらのファイルはローカル環境でしか使用しないためです。
.gitignore
ファイルにbundler_stubs
を追加する。 # Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
# Ignore other unneeded files.
doc/
*.swp
*~
.project
.DS_Store
.idea
bundler_stubs/
3.6.2のguard
など、他の実行ファイルを追加する場合は、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.2のguard
など、他の実行ファイルを追加した場合は、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.34のように、guard-rspec
をGemfile
に追加します。
Gemfile
にGuardを追加する。 source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0
gem 'rails', '4.0.5'
group :development, :test do
gem 'sqlite3', '1.3.8'
gem 'rspec-rails', '2.13.1'
gem 'guard', '2.6.1'
gem 'guard-rspec', '2.5.0'
end
group :test do
gem 'selenium-webdriver', '2.35.1'
gem 'capybara', '2.1.0'
# Uncomment this line on OS X.
# gem 'growl', '1.0.3'
# Uncomment these lines on Linux.
# gem 'libnotify', '0.8.0'
# Uncomment these lines on Windows.
# gem 'rb-notifu', '0.0.4'
# gem 'win32console', '1.3.2'
end
gem 'sass-rails', '4.0.5'
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
group :production do
gem 'pg', '0.15.1'
gem 'rails_12factor', '0.0.2'
end
上のコードを使用する際は、testグループ内で自分のシステムに該当する行を必ずコメント解除してください (Mac用のGrowlの通知機能を使用するのであれば、GrowlをAppleのApp Storeで購入する必要があります。値段は大したことはありません)。
次に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.35)。
Guardfile
に追記する。require
が追加されていることに注意。 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
上のコードにある以下の行に注目してください。
guard 'rspec', 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テストサーバー13はこの問題を解決するためのものです。Sporkは環境を1回だけ読み込み、今後実行するテストのためのプロセスを管理します。Sporkは3.6.2のGuardと組み合わせるとさらに便利です。
最初に、spork
に依存するgemをGemfile
に追加します (リスト3.36)。
Gemfile
にSporkを追加する。 source 'https://rubygems.org'
ruby '2.0.0'
#ruby-gemset=railstutorial_rails_4_0
gem 'rails', '4.0.5'
group :development, :test do
.
.
.
gem 'spork-rails', '4.0.0'
gem 'guard-spork', '1.5.0'
gem 'childprocess', '0.3.6'
end
.
.
.
次に、bundle install
でSporkをインストールします。
$ bundle install
次に、Sporkの設定にbootstrapを指定します。
$ bundle exec spork --bootstrap
そして、環境の読み込みを一回で済ますため、spec/spec_helper.rb
の中で環境をpreforkのブロックで読み込むように、RSpecの設定を変更する必要があります (リスト 3.37)。
Spork.prefork
ブロックへの環境読み込みを追加する。spec/spec_helper.rb
require 'rubygems'
require 'spork'
Spork.prefork do
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}
# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
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
# 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
# Run specs in random order to surface order dependencies. If you
# find an order dependency and want to debug it, you can fix the
# order by providing the seed, which is printed after each run.
# --seed 1234
config.order = "random"
config.include Capybara::DSL
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.38の内容を記載することをお勧めします。
.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.39に示したようにGuardfile
を変更する必要があります。
Guardfile
。 require 'active_support/inflector'
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
guard 'rspec', after_all_pass: false, cli: '--drb' do
.
.
.
end
guard
の変数に:cli => --drbが追加されました。この設定により、Guardがコマンドラインインタフェース (CLI) からSporkサーバーを使うようになります。上の設定には、第5章から使用するspec/support/
ディレクトリを監視するコマンドも追加してあります。
設定が完了したら、以下のguard
コマンドでGuardとSporkを同時に起動します。
$ bundle exec guard
Guardは自動的にSporkサーバーを起動するため、テスト実行時のオーバヘッドを劇的に削減できます。
Guard、Spork、テスト通知機能 (オプション) を使用して便利なテスト環境を構築することで、テスト駆動開発がやみつきになることでしょう。詳細については、Railsチュートリアルのスクリーンキャスト14を参照してください。
3.6.4Sublime Text上でテストする
Sublime Textを使用していれば、エディタの中から直接テストを実行できる強力なヘルパーコマンドを利用できます。このテスト方法は著者のお気に入りです。最終的に大規模にスケールアップする可能性のある少数のテストを、長期間に渡って実施したいような場合に非常に有用であるためです。このコマンドを利用するには、Sublime Text 2 Ruby Tests15に記載されている、自分の環境向けの説明に従って設定を行なってください。著者の環境 (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 Text16にある説明に従って設定することもできます。
Sublime Textを再起動すると、以下のようなコマンドがRubyTestパッケージによって提供されます。
- Command-Shift-R:
it
ブロック内のテストについては、テストを1回だけ実行します。describe
ブロック内のテストについては、一連のテストを実行します。 - Command-Shift-E: 最後に実行したテストを再度実行します。
- Command-Shift-T: 現在のファイルにあるテストをすべて実行します。
比較的小さいプロジェクトであってもテストスイートの実行には時間がかかるため、テストを一度に1つだけ実行したり、小規模なテストグループだけを実行したりできるのは大きな長所です。従来のテストでは、たった1つのテストを実行するだけでも、Railsの環境に匹敵するオーバーヘッドが発生してしまいました。このため、上述のテスト用コマンドとSporkの組み合わせは非常に相性が良いのです。Sporkは、テストを実行するたびに発生していたテスト環境起動によるオーバーヘッドを取り除いてくれるため、1つのテストを実行するたびにすべてをテストするのと同程度のオーバーヘッドが発生するようなことがなくなります。個人的には、以下のテスト手順がお勧めです。
- ターミナルウィンドウでSporkを起動する。
- テストを1つ (または小規模なテストグループ) を作成する。
- Command-Shift-Rコマンドでテストが失敗することを確認する。
- 対応するアプリケーションコードを作成する。
- Command-Shift-Eコマンドで上のテストと同じテストを実行し、今度は成功することを確認する。
- 2-5の手順を繰り返す。
- 中継点 (コミットの直前など) に到達したら、 コマンドラインで
rspec spec/
を実行してテストスイートをすべて実行し、成功することを確認する。
Sublime Textの中からテストが実行できることはもちろん便利ですが、場合によってはGuardの方が便利なこともあると思います。上の手順は、著者が個人的に常用しているテスト駆動開発のテクニックとしてご紹介しました。
- CapybaraはWebratの後続プロジェクトであり、世界最大の齧歯類が名前の由来です。↑
- なお、
install
は省略可能です。実は、bundle
コマンドはbundle install
のエイリアスでもあります。↑ - システムによっては、リスト1.7の.gitignoreを参考にして、さらに便利な設定にすることもできます。↑
- https://github.com/railstutorial/sample_app_rails_4 ↑
- ここで静的なページを作るために採用した方法は、おそらく最もシンプルな方法です。ただし他にも方法はあります。最適な方法は状況によって異なり、たとえば極めて多数の静的なページを1つのStaticPagesコントローラだけまかなおうとすると重荷になる可能性があります。今回はいくつかの静的なページを作るだけなので、重荷にはなりません。↑
- 毎回
bundle exec
を追加するのは面倒ですが、3.6のオプションを使うと省略できます。↑ - 筆者は普段、ターミナルやテキストエディタの背景は黒色にしていますが、明るい色の背景の方がスクリーンショットの見栄えが良いので、(一時的に) 明るい背景を使用しています。↑
- HTMLは常に変化しています。doctypeを明示的に宣言することで、今後もブラウザが正しくページを描画してくれる可能性が高まります。極めてシンプルなdoctype宣言である
<!DOCTYPE html>
は、最新の標準HTML (HTML5) であることを示しています。↑ - 2番目によく使われているHamlというテンプレートシステムもあります。Hamlは個人的に非常に気に入っているのですが、初心者向けのチュートリアルで使うにはやや標準から外れています。↑
- Railsでの開発経験者であれば、この時点で
content_for
の使用を検討すると思いますが、残念ながらAsset Pipelineと併用すると正常に動作しないことがあります。provide
関数はcontent_forの代替です。↑ - Rubyを勉強したことのある方であれば、Railsはブロックの内容をyieldしていると推測することでしょう。そして、その推測はおそらく正しいでしょう。しかし、Rails開発のためにこれらの詳細を知る必要はありません。↑
- https://rvm.io/integration/bundler ↑
- sporkはspoon-forkを組み合わせた造語です。このプロジェクト名は、POSIXのforkにおけるSporkの用法をもじったものです。↑
- http://railstutorial.jp/screencasts ↑
- https://github.com/maltize/sublime-text-2-ruby-tests ↑
- https://github.com/mhartl/rails_tutorial_sublime_text ↑
Railsチュートリアルは YassLab 社によって運営されています。
コンテンツを継続的に提供するため、書籍・動画・質問対応サービスなどもご検討していただけると嬉しいです。
研修支援や教材連携にも対応しています。note マガジンや YouTube チャンネルも始めたので、よければぜひ遊びに来てください!