Ruby on Rails チュートリアル

実例を使ってRailsを学ぼう

Michael Hartl (マイケル・ハートル)

第3版 目次

前書き

私が前にいた会社 (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日間かけて一気に読破しました1。最初から最後まで、途中を飛ばさずにやるのが一番効果的で有益な読み方です。ぜひやってみてください。

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

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 Bat、Geoffrey Grosenbach、Peter Cooper、Matt Aimonetti、Mark Bates、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 チュートリアル」という Web 開発を始めるときに最もよく参考にされる本の著者です。また、Softcover という自費出版プラットフォームの共同創業者でもあります。以前は、(今ではすっかり古くなってしまいましたが)「RailsSpace」という本の執筆および開発に携わったり、また、 一時人気を博した Ruby on Rails ベースのソーシャルネットワーキングプラットフォーム「Insoshi」の開発にも携わっていました。なお、2011年には、Rails コミュニティへの高い貢献が認められて、Ruby Hero Award を受賞しました。ハーバード大学卒業後、カリフォルニア工科大学物理学博士号を取得し、起業プログラム Y Combinator の卒業生でもあります。

  1. 3日間で読破するのは異常です! 実際にはもっと時間をかけて読むのが一般的です。

第7章 ユーザー登録

Userモデルができあがったので、いよいよユーザー登録機能を追加しましょう。7.2ではHTMLフォームを使用して登録情報をWebアプリケーションに送信します。続いて7.4ではユーザーを新規作成して情報をデータベースに保存します。ユーザー登録手続きの最後には、作成されたユーザーの新しいプロファイルを表示できるようにするために、ユーザーを表示するためのページを作成し、ユーザー用のRESTアーキテクチャを実装する第一歩を踏み出します (2.2.2)。それに伴い、5.3.4で実装した簡明かつ表現豊かな統合テストに対して、 いくつかのテストを追加していきます。

本章では、第6章で作成したUserモデルのバリデーションを信頼し、有効なメールアドレスを持っている (可能性のある) 新規ユーザーを増やしていきます。第10章では、 メールアドレスが本当に有効であることを確かめるために、アカウントを有効化する機能をサインアップの手順に追加します。

7.1 ユーザーを表示する

この節では、まずユーザーの名前とプロファイル写真を表示するためのページを作成します。モックアップを7.1に示しました1。ユーザープロファイルページの最終的な目標は、7.2のように2ユーザーのプロファイル写真と基本ユーザーデータ、そしてマイクロポストの一覧を表示することです。(7.2では、有名なlorem ipsumダミーテキストを使用しています。このテキストの成り立ちには面白いエピソードがあるので機会がありましたらどうぞ。)このページを作成したら、第12章のサンプル・アプリケーションで使用する予定です。

バージョン管理を使用している場合は、いつもと同じようにトピックブランチを作成します。

$ git checkout master
$ git checkout -b sign-up
images/figures/profile_mockup_profile_name_bootstrap
図7.1 この節で作成するユーザープロファイルのモックアップ
images/figures/profile_mockup_bootstrap
図7.2 理想とする最終的なプロファイルページのモックアップ

7.1.1 デバッグとRails環境

この節で作成するプロファイルは、このアプリケーションにおける初めての真に動的なページになります。ビューそのものは1ページのコードですが、アプリケーションのデータベースから取り出した情報を使用して各プロファイルの表示をカスタマイズします。サンプルアプリケーションに動的なページを追加する準備として、ここでWebサイトのレイアウトにデバッグ情報を追加しましょう (リスト7.1)。これにより、ビルトインのdebugメソッドとparams変数を使用して、各プロファイルページにデバッグ用の情報が表示されるようになります (詳細については7.1.2で解説します)。

リスト7.1: サイトのレイアウトにデバッグ情報を追加する app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
  </body>
</html>

本番環境に展開したアプリケーションではデバッグ情報を表示したくないので、リスト7.1には以下を記述してあります。

if Rails.env.development?

こうしておくと、デバッグ情報はRailsの3つのデフォルト環境のうち、開発環境でしか表示されなくなります。(コラム7.1)3。特に、Rails.env.development?trueになるのは開発環境に限られるため、以下の埋め込みRubyは

<%= debug(params) if Rails.env.development? %>

本番アプリケーションやテストで挿入されることはありません。(テスト環境でデバッグ情報が表示されても直接問題になることはありませんが、よいことではありません。デバッグ情報は開発環境以外では使用すべきではありません。)

コラム7.1 Railsの3つの環境

Railsにはテスト環境 (test)、開発環境 (development)、そして本番環境 (production) の3つの環境がデフォルトで装備されています。Rails consoleのデフォルトの環境はdevelopmentです。

  $ rails console
  Loading development environment
  >> Rails.env
  => "development"
  >> Rails.env.development?
  => true
  >> Rails.env.test?=> false

上のように、RailsにはRailsというオブジェクトがあり、それには環境の論理値 (boolean) を取るenvという属性があります。たとえば、Rails.env.test?はテスト環境ではtrueを返し、それ以外の環境ではfalseを返します。

テスト環境のデバッグなど、他の環境でconsoleを実行する必要が生じた場合は、環境をパラメータとしてconsoleスクリプトに渡すことができます。

  $ rails console test
  Loading test environment
  >> Rails.env
  => "test"
  >> Rails.env.test?
  => true

rails consoleコマンドと同様に、rails serverもデフォルトではdevelopment環境が使用されますが、以下のように明示的に他の環境を指定してサーバーを実行することもできます。

$ rails server --environment production

アプリケーションを本番環境で実行する場合、本番のデータベースが利用できないとアプリケーションを実行できません。そのため、rake db:migrateを本番環境で実行して本番データベースを作成します。

$ bundle exec rake db:migrate RAILS_ENV=production

(注: console、server、migrateの3つのコマンドでは、デフォルト以外の環境を指定する方法がそれぞれ異なっており、混乱を招く可能性があります。このため、3つの場合のすべてを本コラムで説明しました。)

ところで、サンプルアプリケーションを既にHeroku上にデプロイしている場合は、heroku run consoleというコマンドを打つことで、本番環境を確認することができます。

  $ heroku run console
  >> Rails.env
  => "production"
  >> Rails.env.production?
  => true

当然ながら、Herokuは本番サイト用のプラットフォームなので、実行されるアプリケーションはすべて本番環境となります。

デバッグ出力をきれいに整形するために、第5章で作成したカスタムスタイルシートをリスト7.2のように追加します。

リスト7.2: デバッグ表示を整形するための追加と、Sassのミックスイン app/assets/stylesheets/custom.css.scss
@import "bootstrap-sprockets";
@import "bootstrap";

/* mixins, variables, etc. */

$gray-medium-light: #eaeaea;

@mixin box_sizing {
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}
.
.
.
/* miscellaneous */

.debug_dump {
  clear: both;
  float: left;
  width: 100%;
  margin-top: 45px;
  @include box_sizing;
}

ここでSassのミックスイン機能 (ここではbox_sizing) を使用しています。ミックスイン機能を使用することで、CSSルールのグループをパッケージ化して複数の要素に適用することができます。たとえば以下のような変換を行います。

.debug_dump {
  .
  .
  .
  @include box_sizing;
}

上を以下のように置き換えます。

.debug_dump {
  .
  .
  .
  -moz-box-sizing:    border-box;
  -webkit-box-sizing: border-box;
  box-sizing:         border-box;
}

ミックスインは7.2.1でも使用します。今回の場合、デバッグ出力は7.3のようになります。

images/figures/home_page_with_debug_3rd_edition
図7.3 サンプルアプリケーションのHomeページにデバッグ情報を表示する

7.3のデバッグ出力には、描画されるページの状態を把握するのに役立つ情報が含まれます。

---
controller: static_pages
action: home

これはparamsの内容で、YAML4というものです。YAMLは基本的にハッシュであり、コントローラとページのアクションを一意に指定します。7.1.2には別の例もあります。

7.1.2 Usersリソース

ユーザープロファイルページを作成するには、その前にデータベースにユーザーが登録されている必要があります。これはいわゆる「卵が先か鶏が先か」問題です。このWebサイトでは、登録ページがない状態でどうやってユーザーを登録しておけばよいでしょうか。幸い、この問題は既に解決されています。6.3.4でRailsコンソールを使用してユーザーレコードを登録してありました。したがって、データベースの中に一人のユーザーがいるはずです。

$ rails console
>> User.count
=> 1
>> User.first
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com",
created_at: "2014-08-29 02:58:28", updated_at: "2014-08-29 02:58:28",
password_digest: "$2a$10$YmQTuuDNOszvu5yi7auOC.F4G//FGhyQSWCpghqRWQW..."=> 6

(もしまだデータベース上に一人もユーザーがいない場合は、6.3.4に戻ってユーザーを追加してください。) 先ほど、コンソールの出力結果からユーザーのIDが 1 であることを確認しました。次の目標は、このようなユーザー情報をWebアプリケーション上に表示することです。ここでは、Railsアプリケーションで好まれているRESTアーキテクチャ (コラム 2.2) の習慣に従うことにしましょう。つまり、データを作成、表示、更新、削除可能なリソースとして扱うということです。HTTP標準には、これらに対応する4つの基本操作 (POSTGETPATCHDELETE) が定義されています (コラム3.2)。

RESTの原則に従う場合、リソースへの参照はリソース名とユニークIDを使用するのが普通です。ユーザーをリソースとみなす場合、id=1のユーザーを参照するということは、/users/1というURLに対してGETリクエストを発行するということを意味します。ここでshowというアクションの種類は、暗黙のリクエストになります。RailsのREST機能が有効になっていると、GETリクエストは自動的にshowアクションとして扱われます。

2.2.1で説明したとおり、id=1のユーザーにアクセスするためのページのURLは/users/1となります。ただし、現時点でこのURLを使用してもエラーになります (7.4)。

images/figures/profile_routing_error_3rd_edition
図7.4: /users/1にアクセスした時のエラーログ

/users/1 のURLを有効にするために、routesファイル (config/routes.rb)に以下の1行を追加します。

resources :users

変更の結果を7.3に示します。

リスト7.3: Usersリソースをroutesファイルに追加する config/routes.rb
Rails.application.routes.draw do
  root             'static_pages#home'
  get 'help'    => 'static_pages#help'
  get 'about'   => 'static_pages#about'
  get 'contact' => 'static_pages#contact'
  get 'signup'  => 'users#new'
  resources :users
end

今の短期的な目的はWebページ上にユーザーを表示することではありますが、resources :usersという行は、動作する /users/1 URLを追加するためだけのものではありません。サンプルアプリケーションにこの行を追加すると、ユーザーのURLを生成するための多数の名前付きルート (5.3.3) と共に、RESTfulなUsersリソースで必要となるすべてのアクションが利用できるようになります5。この行に対応するURL、アクション、名前付きルートは表7.1のようになります (2.2との違いを比較してみてください)。次の3つの章に渡って、7.1の他の項目も利用して、Usersリソースを完全にRESTfulなリソースにするために必要なアクションをすべて作成する予定です。

HTTPリクエスト URL アクション 名前付きルート 用途
GET /users index users_path すべてのユーザーを表示するページ
GET /users/1 show user_path(user) 特定のユーザーを表示するページ
GET /users/new new new_user_path ユーザーを新規作成するページ (ユーザー登録)
POST /users create users_path ユーザーを作成するアクション
GET /users/1/edit edit edit_user_path(user) id=1のユーザーを編集するページ
PATCH /users/1 update user_path(user) ユーザーを更新するアクション
DELETE /users/1 destroy user_path(user) ユーザーを削除するアクション
表7.1 リスト7.3のUsersリソースが提供するRESTfulなルート

リスト7.3のコードを使用することで、ルーティングが有効になります。ただし、ルーティング先のページはまだありません (7.5)。この問題を解決するために、7.1.4で最小限のプロファイルページを作成する予定です。

images/figures/user_show_unknown_action_3rd_edition
図7.5 URL /users/1 のルーティングは有効だがページがない状態

ユーザーを表示するために、標準的なRailsの場所を使用することにします。app/views/users/show.html.erbです。リスト5.28でジェネレータを使用して作成したnew.html.erbビューと異なり、このshow.html.erbファイルは自動的には作成されないので、手動で作成します。このファイルを作成後、リスト7.4の内容を貼り付けてください。

リスト7.4: ユーザー情報を表示するための仮のビュー app/views/users/show.html.erb
<%= @user.name %>, <%= @user.email %>

このビューでは埋め込みRubyを使用してユーザー名とメールアドレスを表示しています。インスタンス変数@userがあることを前提としています。もちろん、ユーザー表示ページの最終的な状態はこれとは大きく異なりますし、このメールアドレスがこのまま一般に公開されるようなこともありません。

ユーザー表示ビューが正常に動作するためには、Usersコントローラ内のshowアクションに対応する@user変数を定義する必要があります。ご想像のとおり、ここではUserモデルのfindメソッド (6.1.4) を使用してデータベースからユーザーを取り出します。リスト7.5のように書き換えてください。

リスト7.5: Usersコントローラのshowアクション app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
  end
end

ユーザーのid読み出しにはparamsを使用しました。Usersコントローラにリクエストが正常に送信されると、params[:id]の部分はユーザーidの1に置き換わります。つまり、この箇所は6.1.4で学んだfindメソッドの User.find(1)と同じになります。(技術的な補足: params[:id]は文字列型の "1" ですが、findメソッドでは自動的に整数型に変換されます)。

ユーザーのビューとアクションが定義されたので、URL /users/1 は完全に動作するようになりました (7.6)。(もしbcrypt gemを追加してからまだ一度もRailsサーバを再起動させていない場合は、ここで再起動してください。) 7.6のデバッグ情報でparams[:id]の値を確認できることにも注目してください。

---
action: show
controller: users
id: '1'

以下のコードを使用して

User.find(params[:id])

id=1のユーザーを検索できたのは以上の仕組みによるものです (リスト7.5)。

images/figures/user_show_3rd_edition
図7.6 Usersリソース追加後ののユーザー表示ページ

7.1.3 デバッガー

7.1.2で、アプリケーションの振る舞いを理解するためにデバッグ情報が役に立つことを学びました。Rails 4.2からは、byebug gemを使ってもっと直接的にデバッグできるようになりました (リスト3.2)。どういう風にデバッグできるようになったのか、デバッガーをアプリケーションに差し込んで実際に確かめてみましょう (リスト7.6)。

リスト7.6: デバッガーをUsersコントローラに差し込む app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
    debugger
  end

  def new
  end
end

上のようにdebuggerを差し込んだ後に /users/1 にアクセスしてみると、Railsサーバがbyebugのプロンプトを表示するようになります。

(byebug)

ここではRailsコンソールのようにコマンドを呼び出すことができて、アプリケーションの今の状態を確認することができます。

(byebug) @user.name
"Example User"
(byebug) @user.email
"example@railstutorial.org"
(byebug) params[:id]
"1"

Ctrl-Dを押すとプロンプトから抜け出すことができます。また、デバッグが終わったらshowアクション内のdebuggerの行を削除してしまいましょう (リスト7.7)。

リスト7.7: デバッガーをUsersコントローラーから取り外す app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
  end
end

今後Railsアプリケーションの中でよく分からない挙動があったら、上のようにdebuggerを差し込んで調べてみましょう。トラブルが起こっていそうなコードの近くに差し込むのがコツです。byebugを使ってシステムの状態を調査することは、アプリケーション内のエラーを追跡したりデバッグするときに非常に強力なツールになります。

7.1.4 Gravatar画像とサイドバー

前節で基本的なユーザーページの定義は終わりましたので、今度は各ユーザーのプロファイル写真のあたりをもう少し肉付けし、サイドバーも作り始めましょう。ここでは世界共通のアバターとして認識されている「Gravatar」をユーザープロファイルに導入してみましょう6 (訳注: Gravatarアカウントを作成する必要はありません)。Gravatarは無料のサービスで、プロファイル写真をアップロードして、指定したメールアドレスと関連付けることができます。Gravatarは、プロファイル写真をアップロードするときの面倒な作業や、写真が欠けたりなどのトラブル、置き場所の悩みを解決します。ユーザーのメールアドレスを組み込んだGravatar専用の画像パスを構成するだけで、対応するGravatarの画像が自動的に表示されます (カスタム画像を扱う方法については11.4で扱います)。

ここでは、リスト7.8のようにgravatar_forヘルパーメソッドを使用してGravatarの画像を利用できるようにします。

リスト7.8: ユーザー表示ビューに名前とGravatarを表示する app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<h1>
  <%= gravatar_for @user %>
  <%= @user.name %>
</h1>

デフォルトでは、ヘルパーファイルで定義されているメソッドは自動的にすべてのビューで利用できます。ここでは、利便性を考えてgravatar_forをUsersコントローラに関連付けられているヘルパーファイルに置くことにしましょう。Gravatarのホームページにも書かれているように、GravatarのURLはMD5ハッシュを用いてユーザーのメールアドレスをハッシュ化しています。Rubyでは、Digestライブラリのhexdigestメソッドを使用したMD5ハッシュアルゴリズムが実装されています。

>> email = "MHARTL@example.COM"
>> Digest::MD5::hexdigest(email.downcase)
=> "1fda4469bcbec3badf5418269ffc5968"

メールアドレスは大文字と小文字を区別しませんが (6.2.4)、MD5ハッシュでは大文字と小文字が区別されるので、Rubyのdowncaseメソッドを使用してhexdigestの引数を小文字に変換しています。(本チュートリアルでは、リスト6.31のコールバック処理で小文字変換されたメールアドレスを利用しているため、ここで小文字変換を入れなくても結果は同じです。ただし、将来gravatar_forメソッドが別の場所から呼びだされる可能性を考えると、ここで小文字変換を入れることには意義があります。) gravatar_forヘルパーを組み込んだ結果をリスト7.9に示しました。

リスト7.9: gravatar_forヘルパーメソッドを定義する app/helpers/users_helper.rb
module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end

リスト7.9のコードは、Gravatarの画像タグにgravatarクラスとユーザー名のaltテキストを追加したものを返します (altテキストを追加しておくと、視覚障害のあるユーザーがスクリーンリーダーを使用するときにも役に立ちます)。

プロフィールページは7.7のようになります。ここにはデフォルトのGravatar画像が表示されていますが、これはデフォルトのメールアドレスuser@example.comが本当のメールアドレスではないためです。(ちなみにexample.comというドメイン名は、例として使用するために特別に予約されたドメインとなっています)

images/figures/profile_with_gravatar_3rd_edition
図7.7 ユーザー表示ページにGravatarのデフォルト画像が表示されている

アプリケーションでカスタムGravatarを利用できるようにするために、update_attributes (6.1.5) を使用してデータベース上のユーザー情報を更新します。

$ rails console
>> user = User.first
>> user.update_attributes(name: "Example User",
?>                        email: "example@railstutorial.org",
?>                        password: "foobar",
?>                        password_confirmation: "foobar")
=> true

ここではユーザーのメールアドレスに example@railstutorial.org を使用しました (7.8)。このメールアドレスはRailsチュートリアルのロゴでも使用されています。

images/figures/profile_custom_gravatar_3rd_edition
図7.8: ユーザー表示ページにGravatarのカスタム画像が表示されている

7.1のモックアップに近づけるために、ユーザーのサイドバーの最初のバージョンを作りましょう。ここではasideタグを使用して実装します。このタグはサイドバーなどの補完コンテンツの表示に使用されますが、単独で表示することもできます。rowクラスとcol-md-4クラスも追加しておきます。これらのクラスはBootstrapの一部です。ユーザー表示ページを変更した結果をリスト7.10に示します。

リスト7.10: ユーザーのshowビューにサイドバーを追加する app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
  </aside>
</div>

HTML要素とCSSクラスを配置したことにより、プロフィールページ (とサイドバーとGravatar) にSCSSでリスト7.11のようにスタイルを与えることができるようになりました7。(テーブルCSSのルールがネスティング (入れ子) されていますが、これが有効になるのはAsset PipelineでSassエンジンが使用されている場合に限られます) 。ページの変更の結果を7.9に示します。

リスト7.11: SCSSを使用してサイドバーなどのユーザー表示ページにスタイルを与える app/assets/stylesheets/custom.css.scss
.
.
.
/* sidebar */

aside {
  section.user_info {
    margin-top: 20px;
  }
  section {
    padding: 10px 0;
    margin-top: 20px;
    &:first-child {
      border: 0;
      padding-top: 0;
    }
    span {
      display: block;
      margin-bottom: 3px;
      line-height: 1;
    }
    h1 {
      font-size: 1.4em;
      text-align: left;
      letter-spacing: -1px;
      margin-bottom: 3px;
      margin-top: 0px;
    }
  }
}

.gravatar {
  float: left;
  margin-right: 10px;
}

.gravatar_edit {
  margin-top: 15px;
}
images/figures/user_show_sidebar_css_3rd_edition
図7.9 ユーザー表示ページにサイドバーとCSSを追加する

7.2 ユーザー登録フォーム

ここまででユーザープロファイルページがひとまず動作するようになりましたので、今度はユーザー登録フォームを作成しましょう。5.9 (7.10にも再録) に示したとおり、ユーザー登録ページはまだ空白のままなので、このままではユーザー登録できません。この節の目標は、このみっともないページを改造して7.11のモックアップのようなページに変えることです。

images/figures/new_signup_page_3rd_edition
図7.10 現状のユーザー登録ページ /signup
images/figures/signup_mockup_bootstrap
図7.11 ユーザー登録ページのモックアップ

Web経由でユーザーを作成する機能をこれから追加しますので、6.3.4で作成したユーザーをここで削除しておきましょう。最も簡単な方法は、Rakeのdb:migrate:resetタスクを実行してデータベースをリセットすることです。

$ bundle exec rake db:migrate:reset

最後に、システムによっては変更を反映するためにターミナル上でCtrl-Cを押してWebサーバーを再起動する必要が生じることもあります。

7.2.1 form_forを使用する

ユーザー登録ページで重要な点は、ユーザー登録に欠かせない情報を入力するためのformです。これを行うには、Railsでform_forヘルパーメソッドを使用します。このメソッドはActive Recordオブジェクトを取り込み、オブジェクトの属性を使用してフォームを構成します。

ユーザー登録ページ /signup のルーティングは、Usersコントローラーのnewアクションに既に紐付けられていることを思い出してください (リスト5.33)。したがって、次のステップは、 form_forの引数で必要となるUserオブジェクトを作成することになります。必要となる@user変数の定義は、以下のリスト7.12のようになります。

リスト7.12: newアクションに@user変数を追加する app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end
end

フォームそのものはリスト7.13で示します。7.2.2で詳細について触れますが、まずはリスト7.14のSCSSで見栄えを整えてみましょう。box_sizingミックスインをリスト7.2から再利用していることに注目してください。これらのCSSルールが一度適用されると、ユーザー登録ページは7.12のようになります.

リスト7.13: 新規ユーザーのためのユーザー登録フォーム app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :email %>
      <%= f.email_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>
リスト7.14: ユーザー登録フォームのCSS app/assets/stylesheets/custom.css.scss
.
.
.
/* forms */

input, textarea, select, .uneditable-input {
  border: 1px solid #bbb;
  width: 100%;
  margin-bottom: 15px;
  @include box_sizing;
}

input {
  height: auto !important;
}
images/figures/signup_form_3rd_edition
図7.12: ユーザー登録フォーム

7.2.2 フォームHTML

リスト7.13で定義したフォームを理解するために、小さなコードに分けて考えてみましょう。まずは、埋め込みRubyが使われているform_forからendまでの外側の構造を読み解いていきます。

<%= form_for(@user) do |f| %>
  .
  .
  .
<% end %>

doキーワードは、 form_forが1つの変数を持つブロックを取ることを表します。この変数fは “form” のfです。

通常、Railsヘルパーを使用している場合、実装の詳細について知っておく必要はありません。ただしfというオブジェクトが何をするのかは知っておく必要があります。このfオブジェクトは、HTMLフォーム要素 (テキストフィールド、ラジオボタン、パスワードフィールドなど) に対応するメソッドが呼び出されると、@userの属性を設定するために特別に設計されたHTMLを返します。つまり、以下のコードを実行すると、

<%= f.label :name %>
<%= f.text_field :name %>

Userモデルのname属性を設定する、ラベル付きテキストフィールド要素を作成するのに必要なHTMLを作成します

生成されたフォームのHTMLを見たい場合は、ブラウザ上で表示画面を右クリックし、出てきたポップアップ項目の中から [ソースを表示] といった項目をクリックしてください。WebページのHTMLソースはリスト7.15のようになります。HTMLソースの中の、フォームを形成するHTML構造に注目してみましょう。

リスト7.15: 7.12のフォームのHTMLソース
<form accept-charset="UTF-8" action="/users" class="new_user"
      id="new_user" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="user_name">Name</label>
  <input id="user_name" name="user[name]" type="text" />

  <label for="user_email">Email</label>
  <input id="user_email" name="user[email]" type="email" />

  <label for="user_password">Password</label>
  <input id="user_password" name="user[password]"
         type="password" />

  <label for="user_password_confirmation">Confirmation</label>
  <input id="user_password_confirmation"
         name="user[password_confirmation]" type="password" />

  <input class="btn btn-primary" name="commit" type="submit"
         value="Create my account" />
</form>

まずはこのHTMLソースの内部構造について説明します。リスト7.13リスト7.15をじっくり見比べてみると、以下の埋め込みRubyは

<%= f.label :name %>
<%= f.text_field :name %>

以下のHTMLを生成していることがわかります。

<label for="user_name">Name</label>
<input id="user_name" name="user[name]" type="text" />

また、以下の埋め込みRubyは

<%= f.label :email %>
<%= f.email_field :email %>

以下のHTMLを生成していることがわかります。

<label for="user_email">Email</label>
<input id="user_email" name="user[email]" type="email" />

さらに、以下のコードは

<%= f.label :password %>
<%= f.password_field :password %>

以下のHTMLを生成していることがわかります。

<label for="user_password">Password</label>
<input id="user_password" name="user[password]" type="password" />

次の7.13に示すように、テキストフィールド (type="text"type="email") では内容をそのまま表示していますが、パスワードフィールド (type="password") ではセキュリティ上の目的のために文字が隠蔽されています (7.13)。(emailフィールドとtextフィールドは同じように見えますが、細かな点が違います。たとえば、type="email"となっている場合、モバイル端末から入力フォームをタップすると、メールアドレスに最適化された特別なキーボードが表示されるようになります。)

images/figures/filled_in_form_bootstrap_3rd_edition
図7.13 textフィールドとpasswordフィールドに文字を入力した状態

7.4でも説明しますが、ユーザーの作成で重要なのはinputごとにある特殊なname属性です。

<input id="user_name" name="user[name]" - - - />
.
.
.
<input id="user_password" name="user[password]" - - - />

Railsはこれらのnameの値を使用して、初期化ハッシュを (params変数経由で) 構成します。このハッシュは、ユーザーが入力した値に基づいてユーザーを作成するとき (7.3) に使用されます。

次に重要な要素は、formタグ自身です。Railsはformタグを作成するときに@userオブジェクトを使用します。すべてのRubyオブジェクトは自分のクラスを知っている (4.4.1) ので、Railsは@userのクラスがUserであることを知ることができます。また、@user新しいユーザーなので、 Railsはpostメソッドを使用してフォームを構成する方法を知っています。これは新しいオブジェクトを作成するための正式な動詞 (verb) です (コラム3.2)。

<form action="/users" class="new_user" id="new_user" method="post">

ここでclassid属性はほぼ無関係です。ここで重要な属性はaction="/users"method="post"です。これらの2つの属性は、HTTP POSTリクエストに対する指示を構成しています。これらの属性の効用については次の2つの節で説明します。

ところで、form タグの内側で次のようなHTMLが生成されていたことにもお気付きでしょうか。

  <div style="display:none">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="authenticity_token" type="hidden"
           value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  </div>

このコードはブラウザ上では何も表示しませんが、Railsの内部で使用される特別なコードです。 したがって、どういった意図で生成されたのかは、現時点ではまだ理解しなくても大丈夫です。簡潔にまとめると、Unicode文字の「&#x2713; (チェックマーク ✓)」を使ってブラウザが正しい文字コードで送信できるようにしたり、 Cross-Site Request Forgery (CSRF)と呼ばれる攻撃を阻止するために信頼できるトークンを含めたりしています8

7.3 ユーザー登録失敗

7.12ではフォームのHTMLがどうなっているかを簡単に説明しました (リスト7.15参照) が、フォームを理解するにはユーザー登録の失敗のときが最も参考になります。この節では、無効なデータ送信を受け付けるユーザー登録フォームを作成し、ユーザー登録フォームを更新してエラーの一覧を表示します。このモックアップを7.14に示します。

images/figures/signup_failure_mockup_bootstrap
図7.14 ユーザー登録が失敗したときのモックアップ

7.3.1 正しいフォーム

7.1.2で、resources :usersroutes.rbファイルに追加すると (リスト7.3) 自動的にRailsアプリケーションが7.1のRESTful URLに応答するようになったことを思い出してください。特に、/usersへのPOSTリクエストはcreateアクションに送られます。私たちはここで、createアクションでフォーム送信を受け取り、User.newを使用して新しいユーザーオブジェクトを作成し、ユーザーを保存 (または保存に失敗) し、再度の送信用のユーザー登録ページを表示するという方法で機能を実装しようと思います。まずはユーザー登録フォームのコードを見直してみましょう。

<form action="/users" class="new_user" id="new_user" method="post">

7.2.2で説明したように、このHTMLはPOSTリクエストを/usersというURLに送信します。

ユーザー登録フォームを動かすために、まずリスト7.16にようにコードを追加するところから始めます。このリストでは、5.1.3の「パーシャル」のところでも使ったrenderメソッドを再度使いまわしています。renderはコントローラのアクションの中でも正常に動作します。ここで、以前に説明したif-else分岐構造を思い出してください。この文を使用して、保存が成功したかどうかに応じて@user.saveの値がtrueまたはfalse (6.1.3) になるときに、それぞれ成功時の処理と失敗時の処理を場合分けすることができます。

リスト7.16: ユーザー登録の失敗に対応できるcreateアクション app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])    # 実装は終わっていないことに注意!
    if @user.save
      # Handle a successful save.
    else
      render 'new'
    end
  end
end

コメントにもあるように、上のコードはまだ実装が完了していませんので注意してください。しかし実装の出発点としてはこれで十分です。なお、最終的な実装は7.3.2で完了します。

リスト7.16のコードの動作を理解するもっともよい方法は、実際に無効なユーザー登録データを送信 (submit)してみることです。結果を7.15に、また、すべてのデバッグ情報を7.16に示しました (読みやすいようにフォントサイズを拡大しています)。(7.15の下部に見えているのがRailsのweb consoleという機能です。これはrails consoleをブラウザ上で開けるようにし、デバッグをしやすくするための機能です。たとえばUserモデルを調べたいときなどには便利ですが、今のところは paramsの中身を精査するなどの込み入ったことはできません。)

images/figures/signup_failure_3rd_edition
図7.15 ユーザー登録失敗
images/figures/signup_failure_debug_3rd_edition
図7.16 ユーザー登録失敗時のデバッグ情報

Railsが送信を扱う方法をより深く理解するために、デバッグ情報のうちパラメーターハッシュのuserの部分を詳しく見てみましょう (7.16)。

"user" => { "name" => "Foo Bar",
            "email" => "foo@invalid",
            "password" => "[FILTERED]",
            "password_confirmation" => "[FILTERED]"
          }

このハッシュはUsersコントローラにparamsとして渡されます。7.1.2で説明したとおり、このparamsハッシュには各リクエストの情報が含まれています。/users/1のようなURLの場合、params[:id]の値は該当するユーザーのid (この例では1) になります。ユーザー登録情報の送信の場合、paramsには複数のハッシュに対するハッシュ (hash-of-hashes: 入れ子になったハッシュ) が含まれます (なお、4.3.3ではhash-of-hashesの説明とともに、コンソールセッションで使用するためにあえてparamsという名前の変数を導入しました)。上のデバッグ情報では、フォーム送信の結果が、送信された値に対応する属性とともにuserハッシュに保存されています。ハッシュのキーは、inputタグのname属性です (リスト7.17)。

<input id="user_email" name="user[email]" type="email" />

"user[email]"という名前は、userハッシュのemail属性を正確に指します。

ハッシュのキーはデバッグ情報では文字列として表現されていますが、Railsはこれらを文字列ではなく、params[:user]がuser属性のハッシュになるような「シンボル」(Rubyの機能の1つで、一意性が保証された特別な文字列) としてUsersコントローラに渡します。実際、4.4.5で初めて使われ、リスト7.21でも使用されていたように、これらのハッシュはUser.newの引数として必要な属性と正確に一致します。これはつまり、以下の行は

@user = User.new(params[:user])

以下とほぼ等価であるということです。

@user = User.new(name: "Foo Bar", email: "foo@invalid",
                 password: "foo", password_confirmation: "bar")

以前のバージョンのRailsでは、以下のコードは

@user = User.new(params[:user])

実際に動作しましたが、動作はデフォルトで不安定であり、悪意のあるユーザーによってアプリケーションのデータベースが書き換えられることのないように慎重な手続きによって使用しなければならず、しかもその手続はエラーを起こしやすいものでした。Rails 4.0以降では、上のコードはエラーになります (上の7.15および7.16を参照)。これにより、デフォルトでのセキュリティが高められました。

7.3.2 Strong parameters

4.4.5で、マスアサインメントの概念について簡単に説明しました。これは、以下のように値のハッシュを使用してRubyの変数を初期化するものです。

@user = User.new(params[:user])    # 実装は終わっていないことに注意!

リスト7.16のコメントと、上の再録コメントでも重ねて指摘しているように、この実装は最終形ではありません。その理由は、paramsハッシュ全体を初期化するという行為はセキュリティ上、極めて危険だからです。これは、ユーザーが送信したデータをまるごとUser.newに渡していることになります。ここで、Userモデルにadmin属性というものがあるとしましょう。この属性は、Webサイトの管理者であるかどうかを示します(この属性を実装するのは9.4.1になってからです)。admin=’1’という値をparams[:user]の一部に紛れ込ませて渡してしまえば、この属性をtrueにすることができます。これはcurlなどのコマンドベースのHTTPクライアントを使用すれば簡単に行うことができます。paramsハッシュがまるごとUser.newに渡されてしまうと、どのユーザーでもadmin=’1’をWebリクエストに紛れ込ませるだけでWebサイトの管理者権限を奪い取ることができてしまいます。

以前のバージョンのRailsでは、モデル層でattr_accessibleメソッドを使用することで上のような危険を防止していましたが、 Rails 4.0ではコントローラ層でStrong Parametersというテクニックを使用することが推奨されています。Strong Parametersを使用することで、必須のパラメータと許可されたパラメータを指定することができます。さらに、上のようにparamsハッシュをまるごと渡すとエラーが発生するので、Railsはデフォルトでマスアサインメントの脆弱性から守られるようになりました。

この場合、paramsハッシュでは:user属性を必須とし、名前、メールアドレス、パスワード、パスワードの確認の属性をそれぞれ許可し、それ以外を許可しないようにしたいと考えています。これは、以下のように記述することで行うことができます。

params.require(:user).permit(:name, :email, :password, :password_confirmation)

このコードの戻り値は、paramsハッシュのバージョンと、許可された属性です (:user属性がない場合はエラーになります)。

これらのパラメータを使いやすくするために、user_paramsという外部メソッドを使用するのが慣習になっています。このメソッドは適切に初期化したハッシュを返し、params[:user]の代わりとして使用されます。

@user = User.new(user_params)

このuser_paramsメソッドはUsersコントローラの内部でのみ実行され、Web経由で外部ユーザーにさらされる必要はないため、リスト7.22に示すようにRubyのprivateキーワードを使って外部から使用できないようにします (privateキーワードの詳細については 8.4で説明します)。

リスト7.17: createアクションでStrong Parametersを使用する app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      # Handle a successful save.
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

ちなみに、privateキーワード以降のコードを強調するために、user_paramsのインデントを1段深くしてあります。(経験的にはこれは賢い慣習だと思います。というのも、クラス内に多数のメソッドがある場合、privateメソッドの場所が簡単に見つかるからです。これにより、インデントが無い場合と比べて、どこからprivateになるのか困惑することがなくなります。)

この時点で、(送信ボタンを押してもエラーが出ないという意味で) ユーザー登録フォームは動くようになります。ただし7.17が示すように、(開発者用のデバッグ領域を除いて) 間違った送信をしても何もフィードバックが返ってきていません。これはユーザーが困惑する原因となります。また、有効なユーザー情報を送信しても新しいユーザーが実際に作成されることもありません。前者の問題を7.3.3で、後者の問題を7.4でそれぞれ解決していきます。

images/figures/invalid_submission_no_feedback
図7.17: 無効な情報をユーザー登録フォームで送信した結果

7.3.3 ユーザー登録のエラーメッセージ

ユーザー登録に失敗した場合の最後の手順として、問題が生じたためにユーザー登録が行われなかったということをユーザーにわかりやすく伝えるエラーメッセージを追加しましょう。Railsは、このようなメッセージをUserモデルの検証時に自動的に生成してくれます。たとえば、ユーザー情報のメールアドレスが無効で、パスワードが短すぎる状態で保存しようとしたとします。

$ rails console
>> user = User.new(name: "Foo Bar", email: "foo@invalid",
?>                 password: "dude", password_confirmation: "dude")
>> user.save
=> false
>> user.errors.full_messages
=> ["Email is invalid", "Password is too short (minimum is 6 characters)"]

6.2.2で少し触れたerrors.full_messagesオブジェクトは、 エラーメッセージの配列を持っています。

上のコンソールセッションに示されているように、リスト7.16で保存に失敗すると、@userオブジェクトに関連付けられたエラーメッセージの一覧が生成されます。このメッセージをブラウザで表示するには、ユーザーのnewページでエラーメッセージのパーシャル (partial) を出力します。このとき、form-controlというCSSクラスも一緒に追加することで、Bootstrapがうまく取り扱ってくれるようになります。変更の結果をリスト7.18に示します。ここで使用しているエラーメッセージのパーシャルはあくまで試作品である点に注意してください。最終版は11.3.2を参照してください。

リスト7.18: ユーザー登録失敗時にエラーメッセージが表示されるようにする app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(@user) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Create my account", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

ここでは、’shared/error_messages’というパーシャルをrender (レンダリング) している点に注目してください。これはRails全般の慣習で、パーシャルは複数のコントローラにわたるビューに対し、専用のshared/ディレクトリを使用するようにしています(これは9.1.1で実現します)。ただし、今はまだapp/views/sharedといったディレクトリは作っていないので、1.1で紹介したmkdirコマンドを使い、新しくディレクトリを作成する必要があります。

$ mkdir app/views/shared

また、いつものようにテキストエディタを使ってパーシャル (_error_messages.html.erb) も作成します。パーシャルの内容はリスト7.19のようになります。

リスト7.19: フォーム送信時にエラーメッセージを表示するためのパーシャル app/views/shared/_error_messages.html.erb
<% if @user.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(@user.errors.count, "error") %>.
    </div>
    <ul>
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

パーシャルによって、RailsとRubyには、Railsエラーオブジェクト用の2つのメソッドを含む多くの成果物が導入されました。最初はcountメソッドを紹介します。これはエラーの数を返します。

>> user.errors.count
=> 2

もう1つはany?メソッドです。これはempty?メソッドと互いに補完します。

>> user.errors.empty?
=> false
>> user.errors.any?
=> true

4.2.3では文字列に対してempty?メソッドを使用しましたが、Railsのエラーオブジェクトに対しても使用できます。オブジェクトが空の場合はtrue、 それ以外の場合はfalseを返します。any?メソッドはちょうどempty?と逆の動作で、要素が1つでもある場合はtrue、ない場合はfalseを返します。(なお、これらのcountempty?any?メソッドは、Rubyの配列に対してもそのまま使用できます。これは11.2で応用する予定です。)

さらに、pluralizeという英語専用のテキストヘルパーが新たに登場しています。このヘルパーはデフォルトではコンソールでは使用できませんが、ActionView::Helpers::TextHelperを経由して明示的にインクルードできます9

>> include ActionView::Helpers::TextHelper
>> pluralize(1, "error")
=> "1 error"
>> pluralize(5, "error")
=> "5 errors"

pluralizeの最初の引数に整数が与えられると、それに基づいて2番目の引数の英単語を複数形に変更したものを返します。このメソッドの背後には強力なインフレクター (活用形生成) があり、不規則活用を含むさまざまな単語を複数形にすることができます。

>> pluralize(2, "woman")
=> "2 women"
>> pluralize(3, "erratum")
=> "3 errata"

pluralizeを使用することで、コードは以下のようになります。

<%= pluralize(@user.errors.count, "error") %>

このコードはたとえば "0 errors""1 error""2 errors" などのように、エラーの数に応じて活用された単語を返します。これにより、"1 errors" のような英語の文法に合わない文字列を避けることができます (これはWeb上でどうしようもないほどよく見かけるエラーです)。

リスト7.24には、エラーメッセージにスタイルを与えるためのCSS id error_explanationも含まれていることに注目してください (5.1.2のCSSでスタイルidに「#」記号を使用していることも思い出してください)。さらにRailsは、無効な内容で送信がされて元のページに戻されると、divで囲まれたエラー用のCSSクラスfield_with_errorsを返します。これらのラベルによって、リスト7.20のようにエラーメッセージをSCSSで整形することができます。ここでは、Sassの@extend関数を使ってBootstrapのhas-errorというCSSクラスを適用してみます。

リスト7.20: エラーメッセージにスタイルを与えるためのCSS app/assets/stylesheets/custom.css.scss
.
.
.
/* forms */
.
.
.
#error_explanation {
  color: red;
  ul {
    color: red;
    margin: 0 0 30px 0;
  }
}

.field_with_errors {
  @extend .has-error;
  .form-control {
    color: $state-danger-text;
  }
}

リスト7.18リスト7.19のコードと、SCSSのリスト7.20を組み合わせることで、無効なユーザー登録情報を送信したときのエラーメッセージが分かりやすくなります (7.18)。これらのメッセージはモデルの検証時に生成されるので、メールアドレスのスタイルやパスワードの最小文字列などを変更すると、メッセージも自動的に変更されます。

(このとき、存在性のバリデーションもhas_secure_passwordによるバリデーションも空のパスワードを検知してしまうため、ユーザー登録フォームで空のパスワードを入力すると2つの同じエラーメッセージが表示されてしまいます。もちろんこういった冗長なエラーメッセージを直接修正することも可能ですが、幸運にも今回の場合は、後ほど追加する allow_nil: true というオプションでこの問題は解決できます。)

images/figures/signup_error_messages_3rd_edition
図7.17 ユーザー登録失敗時のエラーメッセージ

7.3.4 失敗時のテスト

完全なテスト機能を備えた強力なWebフレームワークがなかった頃は、開発者はフォームのテストを毎回手動で行う必要がありました。たとえば、もし仮にユーザー登録ページを手動でテストしなければならないとしたら、ブラウザでそのページを表示し、有効なデータと無効なデータを交互に流しこみ、どちらの場合にもアプリケーションが正常に動作することを確認しなければならないでしょう。さらに、アプリケーションに変更が生じるたびに、まったく同じテストを繰り返さなければなりません。このプロセスは苦痛で、バグも発生しがちです。

しかし幸運なことに、Railsではフォーム用のテストを書くことができ、こういったプロセスを自動化することができます。本項では、無効な送信をしたときの正しい振る舞いについてテストを書いていきます。7.4.4では同様の方法で、有効な送信をしたときの正しい振る舞いについてテストを書いていきます。

まずは、新規ユーザー登録用の統合テストを生成するところから始めていきます。コントローラーの慣習である「リソース名は複数形」に因んで、統合テストのファイル名はusers_signupとします。

$ rails generate integration_test users_signup
      invoke  test_unit
      create    test/integration/users_signup_test.rb

(7.4.4で書くテストでも、ここで生成したファイルを使います)

このテストでは、ユーザー登録ボタンを押したときに (ユーザー情報が無効であるために) ユーザーが作成されないことを確認します。(なお、エラーメッセージに対するテストは7.7の演習に残しておきます。)これを確認するには、ユーザーのcountを使用します。背後で動作するこのcountメソッドは、Userを含むあらゆるActive Recordクラスで使用できます。

$ rails console
>> User.count
=> 0

7.2の冒頭でデータベースをリセットしてあるので、現時点ではUser.count0になっています。5.3.4のように、 assert_selectを使って関連ページのHTML要素をテストしていきます。これにより、今後うっかり要素を変更してしまっても気付けるようになります。

まずはgetメソッドを使ってユーザー登録ページにアクセスします。

get signup_path

フォーム送信をテストするためには、 users_pathに対してPOSTリクエストを送信する必要があります (7.1)。これは、次のようにpost関数を使って実現できます

assert_no_difference 'User.count' do
  post users_path, user: { name:  "",
                           email: "user@invalid",
                           password:              "foo",
                           password_confirmation: "bar" }
end

createアクションのUser.new (リスト7.16)で期待されているデータを、params[:user]というハッシュにまとめています。また、 assert_no_differenceメソッドのブロック内でpost関数を使い、メソッドの引数には’User.count’を与えています。これは、 assert_no_differenceのブロックを実行する前後で引数の値 (User.count) が変わらないことをテストしています。すなわちこのテストは、ユーザ数を覚えた後に、データを投稿してみて、ユーザ数が変わらないかどうかを検証するテストになります。したがって、以下のコードと等価になります。

before_count = User.count
post users_path, ...
after_count  = User.count
assert_equal before_count, after_count

これらのコードは等価ではありますが、assert_no_differenceを使う方が明瞭で、Rubyの慣習的にも正しいです。

また、上のコードではget関数を使っていないことにも注目してください。これは各関数に技術的な関連性がなく、ユーザー登録ページにアクセスしなくても、直接post関数を呼び出してユーザー登録ができることを意味しています。個人的には、コンセプトを明確にする意味とユーザー登録ページをダブルチェックする意味も兼ねて、 (実際の手順に倣って) 両方の関数を呼び出す方が好きです。

上記のアイデアをコードに落とし込むと、リスト7.21のようになります。なお、送信に失敗したときにnewアクションが再描画されるはずなので、assert_templateを使ったテストも含めていることに注意してください。エラーメッセージが正しく表示されているかどうかについては、演習として残しておきます (7.7)。

リスト7.21: 無効なユーザー登録に対するテスト GREEN test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, user: { name:  "",
                               email: "user@invalid",
                               password:              "foo",
                               password_confirmation: "bar" }
    end
    assert_template 'users/new'
  end
end

アプリケーションコードは既に実装済みなので、今回の統合テストも含め、全てのテストがGREENになるはずです。

リスト7.22: GREEN
$ bundle exec rake test

7.4 ユーザー登録成功

無効なフォームの送信を扱えるようになったので、いよいよ新規ユーザーを実際にデータベースに保存できるようにし (もちろんフォームが有効な場合に)、ユーザー登録フォームを完成させましょう。まずは、ユーザーを保存できるようにします。保存に成功すると、ユーザー情報は自動的にデータベースに登録されます。次にブラウザの表示をリダイレクトして、登録されたユーザーのプロファイルを表示します。ついでにウェルカムメッセージも表示しましょう。モックアップを7.19に示します。保存に失敗した場合は、単に7.3で開発したとおりの動作が実行されます。

images/figures/signup_success_mockup_bootstrap
図7.19 ユーザー登録に成功した画面のモックアップ

7.4.1 登録フォームの完成

ユーザー登録フォームを完成させるために、リスト7.17のコメントアウトされた部分にコードを書き、適切に動作するようにしましょう。現状では、有効な情報で送信するとエラーが発生してしまいます。7.20が示すように、Railsのデフォルトのアクションは対応するビューを表示するようになっています。しかしcreateアクションに対応するビューのテンプレートがないため (あるはずがありません)、このようなエラーが発生しています。

images/figures/valid_submission_error
図7.20: 有効な情報でユーザー登録をしてもエラーメッセージが表示される

ユーザー登録に成功した場合は、ページを描画するのではなく別のページにリダイレクトするようにしてみましょう。ルートURLにリダイレクトしてもよいですが、一般的な慣習にしたがって、新しく作成されたユーザーのプロフィールページにリダイレクトしてみます。実際のアプリケーションコードをリスト7.23に示します (redirect_toメソッドに注目してください)。

リスト7.23: 保存とリダイレクトを行う、userのcreateアクション app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

ここで

redirect_to @user

といった行がありますが、これは次のコードと等価になります。

redirect_to user_url(@user)

これはRailsが、redirect_to @userというコードからuser_url(@user)といったコードを実行したいことを(自動的に)推察してくれた結果になります。

7.4.2 flash

リスト7.23のコードによって、ユーザー登録フォームが実際に動くようになりました。これでブラウザから正しいユーザー情報を登録できるようになりましたが、その前にWebアプリケーションに常識的に備わっている機能を追加してみましょう。登録完了後に表示されるページにメッセージを表示し (この場合は新規ユーザーへのウェルカムメッセージ)、2度目以降にはそのページにメッセージを表示しないようにするというものです。

Railsでこういった情報を表示するためには、flashという特殊な変数を使います。この変数はハッシュのように扱います。Railsの一般的な慣習に倣って、:successというキーには成功時のメッセージを代入するようにします (リスト7.24)。

リスト7.24: ユーザー登録ページにフラッシュメッセージを追加する app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
    if @user.save
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
  end

  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end
end

flash変数に代入したメッセージは、リダイレクトした直後のページで表示できるようになります。今回はflash内に存在するキーがあるかを調べ、もしあればその値 (メッセージ) を全て表示するように、レイアウトを修正します。4.3.3でコンソール上で実行した例を思い出してみてください。そこではあえてflashと名付けたハッシュを使用してハッシュの値を列挙しました。

$ rails console
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", danger: "It failed."}
>> flash.each do |key, value|
?>   puts "#{key}"
?>   puts "#{value}"
>> end
success
It worked!
danger
It failed.

上で示したパターンに則って、flash変数の内容をWebサイト全体にわたって表示できるようにすると、次のようなコードになります。

<% flash.each do |message_type, message| %>
  <div class="alert alert-<%= message_type %>"><%= message %></div>
<% end %>

なお、このコードではHTMLとERbが雑に混ざっています。これをキレイに整形する課題は演習として残しておきます (7.7)。さて、次の埋め込みRubyでは

alert-<%= message_type %>

適用するCSSクラスをメッセージの種類によって変更するようにしています。これにより、たとえば:successキーのメッセージが表示される場合、適用されるCSSクラスは次のようになります。

alert-success

(:successキーはシンボルですが、テンプレート内に反映させる直前で、埋め込みRubyが自動的に"success"という文字列に変換しています。)これにより、キーの内容によって異なったCSSクラスを適用させることができ、メッセージの種類によってスタイルを動的に変更させることができます。たとえば、8.1.4ではflash[:error]を使用してログインに失敗したことを表すメッセージを表示します10。(実際、既にalert-dangerというCSSクラスを使って、リスト7.19のエラーメッセージのスタイルをdivタグで指定しています。)Bootstrap CSSは、このようなflashのクラス用に4つのスタイルを持っています (successinfowarningdanger)。また、本書のサンプルアプリケーションでは、これらの全てのスタイルを場合に応じて使っていきます。

テンプレート内にflashのメッセージが差し込まれるので、次のようなコードは、

flash[:success] = "Welcome to the Sample App!"

最終的には次のようなHTMLになります。

<div class="alert alert-success">Welcome to the Sample App!</div>

先ほど説明した埋め込みRubyをレイアウトに埋め込んだ結果を、リスト7.25に示します。

リスト7.25: flash変数の内容をWebサイトのレイアウトに追加する app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  .
  .
  .
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <% flash.each do |message_type, message| %>
        <div class="alert alert-<%= message_type %>"><%= message %></div>
      <% end %>
      <%= yield %>
      <%= render 'layouts/footer' %>
      <%= debug(params) if Rails.env.development? %>
    </div>
    .
    .
    .
  </body>
</html>

7.4.3 実際のユーザー登録

ついにユーザー登録が完成しました。名前を “Rails Tutorial”、メールアドレスを “example@railstutorial.org”として登録してみましょう (7.21)。登録結果 (7.20)にはユーザー登録成功を示すウェルカムメッセージが、successクラスのさわやかな緑色の背景で表示されています。このクラスは5.1.2のBootstrap CSSフレームワークのものです。もしメールアドレスが既に使用されているというメッセージが表示されたら、7.2でやったようにRakeのdb:migrate:resetを実行してデータベースをリセットしてください。ユーザー表示ページを再度読み込むと、今度はフラッシュメッセージは表示されなくなりました (7.23)。

images/figures/first_signup
図7.21: ユーザー登録に必要な情報を入力する
images/figures/signup_flash_3rd_edition
図7.22 ユーザー登録が成功し、フラッシュメッセージが表示される
images/figures/signup_flash_reloaded_3rd_edition
図7.23 ブラウザでページを再読み込みすると、フラッシュメッセージが表示されなくなる

今度はデータベースを覗いて、新規ユーザーが確かに登録されていることをダブルチェックしましょう。

$ rails console
>> User.find_by(email: "example@railstutorial.org")
=> #<User id: 1, name: "Rails Tutorial", email: "example@railstutorial.org",
created_at: "2014-08-29 19:53:17", updated_at: "2014-08-29 19:53:17",
password_digest: "$2a$10$zthScEx9x6EkuLa4NolGye6O0Zgrkp1B6LQ12pTHlNB..."=> 6

7.4.4 成功時のテスト

次に進む前に、ここで一旦、有効な送信に対するテストを書いてみます。これによって、アプリケーションの振る舞いを検証し、もし今後バグが埋め込またらそれを検知できるようになります。7.3.4で書いた無効な送信に対するテストと同様に、今回の目的はデータベースの中身が正しいかどうか検証することです。すなわち、有効な情報を送信して、ユーザーが作成されたことを確認します。リスト7.21のときと同じは、次のようにテストを書きましたが

assert_no_difference 'User.count' do
  post users_path, ...
end

今回はassert_differenceというメソッドを使ってテストを書きます。

assert_difference 'User.count', 1 do
  post_via_redirect users_path, ...
end

assert_no_differenceと同様に、このメソッドは第一引数に文字列 (’User.count’) を取り、assert_differenceブロック内の処理を実行する直前と、実行した直後のUser.countの値を比較します。第二引数はオプションですが、ここには比較した結果の差異 (今回の場合は1) を渡します。

リスト7.21と同じファイルにassert_differenceを使ったテストを追加すると、リスト7.26のようになります。ここで、users_pathにPOSTリクエストを送信するために、post_via_redirectというメソッドを使っていることに注目してください。このメソッドは、POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッドです。したがって、この行の直後では’users/show’テンプレートが表示されているはずです。ちなみに、ここにflashのテストも追加しておくとよいでしょう。これは演習として残しておきます (7.7)。

リスト7.26: 有効なユーザー登録に対するテスト GREEN test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
  .
  .
  .
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post_via_redirect users_path, user: { name:  "Example User",
                                            email: "user@example.com",
                                            password:              "password",
                                            password_confirmation: "password" }
    end
    assert_template 'users/show'
  end
end

リスト7.26では、ユーザー登録に成功させた後に、どのテンプレートが表示されているのか検証していることにも注目してください。このテストがパスするためには、Userのルート(リスト7.3)とUserの showアクション(リスト7.5)、そしてshow.html.erbビュー(リスト7.8)がそれぞれ正しく動いている必要があります。最後に、

assert_template 'users/show'

上のコードでは、ユーザープロフィールに関するほぼ全て (たとえばページにアクセスしたらなんらかの理由でエラーが発生しないかどうかなど) をテストできていることに注目してください。この類のエンドツーエンドテストは、アプリケーションの重要な機能をカバーしてくれています。こういった理由が統合テストが便利だと呼ばれる所以です。

7.5 プロのデプロイ

ユーザー登録ページを動かすことができたので、このアプリケーションをデプロイして、本番環境でも動かせるようにしてみましょう。第3章からデプロイをして来ましたが、実際にデータを操作できるようにするデプロイは初めてです。そこで、この機会にプロレベルのデプロイ方法について説明していきます。具体的には、ユーザー登録をセキュアにするために、本番用のアプリケーションに重要な機能を追加していきます。その後、デフォルトのWebサーバを実際の世界で使われているWebサーバに置き換えていきます。

デプロイの下準備として、まずはこの時点までの変更をmasterブランチにマージしておいてください。

$ git add -A
$ git commit -m "Finish user signup"
$ git checkout master
$ git merge sign-up

7.5.1 本番環境でのSSL

本章で開発したユーザー登録フォームで送信すると、名前やメールアドレス、パスワードといったデータがネットワーク越しに流されていきます。実は、このようなネットワークに流れるデータは途中で捕捉できるため、扱いには注意が必要です。これはサンプルアプリケーションの本質的なセキュリティ上の欠陥です。そしてこれを修正するためにSecure Sockets Layer (SSL)11を使います。これはローカルのサーバからネットワークに流れる前に、大事な情報を暗号化する技術です。今回はユーザー登録ページのためだけにSSLを導入しますが、これはWebサイト全体で適用できるため、第8章で実装するログイン機構をセキュアにしたり、8.4で説明するセッションハイジャックの脆弱性に対しても多くの利点を生み出します。

そしてSSLを有効化するのも簡単です。 production.rbという本番環境の設定ファイルの1行を修正するだけで済みます。 具体的には、 config変数で「本番環境ではSSLを強制する」という設定をするだけです (リスト7.27)。

リスト7.27: 本番環境ではSSLを使うように修正する config/environments/production.rb
Rails.application.configure do
  .
  .
  .
  # Force all access to the app over SSL, use Strict-Transport-Security,
  # and use secure cookies.
  config.force_ssl = true
  .
  .
  .
end

次に、遠隔にあるサーバーのSSLをセットアップします。本番用のWebサイトでSSLを使えるようにするためには、ドメイン毎にSSL証明書を購入し、セットアップする必要があります。これには多くの作業が必要となりますが、幸運にもそういった作業をしなくても済む方法があります。それは、Heroku上でサンプルアプリケーションを動かし、HerokuのSSL証明書に便乗する方法です (訳注: ただし、この方法はHerokuのサブドメインでのみ有効です。独自ドメインを使う場合はSSL証明書を購入する必要があります)。結果として、7.5.2でアプリケーションのデプロイが終わると、自動的にSSLが有効化されているはずです。(もしwww.example.comなどの独自ドメインでSSLを使いたい場合は、Heroku’s page on SSL (英語) の記事を参照してください。)

7.5.2 本番環境用Webサーバー

SSLを導入したので、次はアプリケーションの設定をいじって、本番環境に適したWebサーバを使ってみましょう。Herokuのデフォルトでは、Rubyだけで実装されたWEBrickというWebサーバを使っています。WEBrickは簡単にセットアップできたり動せることが特長ですが、著しいトラフィックを扱うことには適していません。つまり、WEBrickは本番環境として適切なWebサーバではありません。よって、今回はWEBrickをPumaというWebサーバに置き換えてみます。Pumaは多数のリクエストを捌くことに適したWebサーバです。

新しいWebサーバを追加するために、Heroku内のPumaドキュメント (英語) にしたがってセットアップしていきます。まずはpuma gemをGemfileに追加します (リスト7.28)。このとき、ローカル環境 (開発用の環境) でPumaを使う必要はないので、リスト7.28のように:productionグループの中に追加しておきます。

リスト7.28: GemfileにPumaを追加する
source 'https://rubygems.org'
.
.
.
group :production do
  gem 'pg',             '0.17.1'
  gem 'rails_12factor', '0.0.2'
  gem 'puma',           '2.11.1'
end

Bundlerでは本番環境用のgemはインストールしない設定にしておいたので (3.1)、リスト7.28は開発環境に影響はありません。しかし、BundlerにGemfile.lockを更新してもらう必要があるので、いつものように次のコマンドを実行しておきます。

$ bundle install

次のステップは、config/puma.rbというファイルを作成し、そこにリスト7.29のような設定情報を追加します。リスト7.29Herokuのドキュメント12をそのまま引用したコードです。これらのコードは理解しなくても大丈夫です。

リスト7.29: 本番環境のWebサーバー設定ファイル config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['MAX_THREADS'] || 5)
threads threads_count, threads_count

preload_app!

rackup      DefaultRackup
port        ENV['PORT']     || 3000
environment ENV['RACK_ENV'] || 'development'

on_worker_boot do
  # Worker specific setup for Rails 4.1+
  # See: https://devcenter.heroku.com/articles/
  # deploying-rails-applications-with-the-puma-web-server#on-worker-boot
  ActiveRecord::Base.establish_connection
end

最後に、Procfileと呼ばれる、Heroku上でPumaのプロセスを走らせる設定ファイルを作成します (リスト7.30)。なお、このProcfileはルートディレクトリ (Gemfileと同じディレクトリ) に置いておく必要があるので、ファイルの置き場所には注意してください。

リスト7.30: Pumaが使うようにProcfileで定義する ./Procfile
web: bundle exec puma -C config/puma.rb

これで、本番環境用のWebサーバの設定は完了しました。これらの変更をコミットし、デプロイしてみましょう13

$ bundle exec rake test
$ git add -A
$ git commit -m "Use SSL and the Puma webserver in production"
$ git push
$ git push heroku
$ heroku run rake db:migrate

ユーザー登録フォームが無事に動いたら成功です。成功すると7.24のようになります。このとき、URLがhttps://に変わっていて、アドレスバーに鍵アイコンが表示されていることにも注目してください (7.24)。これは先ほど設定したSSLがうまく動いていることを示しています。

images/figures/signup_in_production_3rd_edition
図7.24: 本番環境 (Web上) で実際にユーザー登録をしてみる

7.5.3 Rubyのバージョン番号

ところで、Herokuのデプロイするとき、もしかしたら次のような警告メッセージを目にしたことがあるかもしれません。

###### WARNING:
       You have not declared a Ruby version in your Gemfile.
       To set your Ruby version add this line to your Gemfile:
       ruby '2.1.5'

(これは「Rubyのバージョンを明示的に指定してください」というメッセージですが) 経験的には、本書のようなチュートリアルの段階では明示的に指定しない方がスムーズに進むことが多いので、この警告は現時点では無視してしまった方がよいでしょう。というのも、サンプルアプリケーションでRubyのバージョンを常に最新に保っておくと、多大な不都合に繋がりかねないからです14。また、本書のサンプルアプリケーションにおいては、ローカルで使っているバージョンと本番環境のバージョンが異なっていても、違い生じることはほぼ無いでしょう。とは言うものの、次の点は頭の片隅に置いておいてください。それは、仕事でHerokuを使ったアプリケーションを動かす場合はGemfileでRubyのバージョンを明示しておいた方が賢明である、という点です。これによって開発環境と本番環境の互換性を最大限に高めることができるので、(バージョンの差異による誤作動やエラーなどが無くなり) お勧めです。

7.6 最後に

ユーザー登録機能の実装は、私たちのサンプルアプリケーションにとって大きなマイルストーンでした。この時点でサンプルアプリケーションはかなり実用的になってきましたが、まだ重要な機能がいくつも残っています。第8章では、認証 (authentication) システムを導入し、ユーザーがログインとログアウトをできるようにします。第9章では、どのユーザーも自分のアカウント情報を更新できるようにし、Webサイトの管理者がユーザーを削除できるようにします。それにより、Usersリソースに7.1のRESTアクションがすべて実装されるようにします。

7.6.1 本章のまとめ

  • debugメソッドを使うことで、役立つデバッグ情報を表示できる
  • Sassのmixin機能を使うと、CSSのルールをまとめたり他の場所で再利用できるようなる
  • Railsには標準で3つ環境が備わっており、それぞれ開発環境 (development)テスト環境 (test)本番環境 (production)と呼ぶ
  • 標準的なRESTfulなURLを通して、ユーザー情報をリソースとして扱えるようになった
  • Gravatarを使うと、ユーザーのプロフィール画像を簡単に表示できるようになる
  • form_forヘルパーは、Active Recordのオブジェクトに対応したフォームを生成する
  • ユーザー登録に失敗した場合はnewビューを再描画するようにした。その際、Active Recordが自動的に検知したエラーメッセージを表示できるようにした
  • flash変数を使うと、一時的なメッセージを表示できるようになる
  • ユーザー登録に成功すると、データベース上にユーザーが追加、プロフィールページにリダイレクト、ウェルカムメッセージの表示といった順で処理が進む
  • 統合テストを使うことで送信フォームの振る舞いを検証したり、バグの発生を検知したりできる
  • セキュアな通信と高いパフォーマンスを確保するために、本番環境ではSSLとPumaを導入した

7.7 演習

: 『演習の解答マニュアル (英語)』にはRuby on Railsチュートリアルブックのすべての演習の解答が掲載されており、www.railstutorial.orgで本書を購入いただいた方には無料で配布しています (訳注: 解答は英語です)。

演習とチュートリアル本編との食い違いを避ける方法については、演習用のトピックブランチに追加したメモ (3.6) を参照してください。

  1. リスト7.31のコードを使用して、7.1.4で定義されたgravatar_forヘルパーにオプションのsizeパラメーターを取ることができる (gravatar_for user, size: 40のようなコードをビューで使用できる) ことを確認してください。(9.3.1でこれを改善したヘルパーを使います)
  2. リスト7.18で実装したエラーメッセージに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。リスト7.32にテンプレートを用意しておいたので、参考にしてください。
  3. 7.4.2で実装したflashに対するテストを書いてみてください。どのくらい細かくテストするかはお任せします。 リスト7.33に最小限のテンプレートを用意しておいたので、参考にしてください (ヒント: FILL_INメソッドを適切なコードに置き換えると完成します)。(テキストに対するテストは壊れやすいです。文量の少ないflashのキーであっても、それは同じです。個人的には、flashが空でないかをテストするだけの場合が多いです)
  4. 7.4.2で触れたように、flash用のHTML (リスト7.25) は読みにくいです。より読みやすくしたリスト7.34のコードに対してテストスイートを実行し、こちらも正常に動作することを確認してください。このコードでは、Railsのcontent_tagヘルパーを使用しています。
リスト7.31: gravatar_forヘルパーにキーワード引数を追加する app/helpers/users_helper.rb
module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user, options = { size: 80 })
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    size = options[:size]
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end
リスト7.32: エラーメッセージをテストするためのテンプレート test/integration/users_signup_test.rb
require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, user: { name:  "",
                               email: "user@invalid",
                               password:              "foo",
                               password_confirmation: "bar" }
    end
    assert_template 'users/new'
    assert_select 'div#<CSS id for error explanation>'
    assert_select 'div.<CSS class for field with error>'
  end
  .
  .
  .
end
リスト7.33: flashをテストするためのテンプレート test/integration/users_signup_test.rb
require 'test_helper'
  .
  .
  .
  test "valid signup information" do
    get signup_path
    assert_difference 'User.count', 1 do
      post_via_redirect users_path, user: { name:  "Example User",
                                            email: "user@example.com",
                                            password:              "password",
                                            password_confirmation: "password" }
    end
    assert_template 'users/show'
    assert_not flash.FILL_IN
  end
end
リスト7.34: content_tagを使ってレイアウトの中にflashを埋め込む app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
      .
      .
      .
      <% flash.each do |message_type, message| %>
        <%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
      <% end %>
      .
      .
      .
</html>
  1. Mockingbirdでは7.1のプロファイル写真のようなカスタム画像はサポートされていません。ここではGIMPを使用して手動で画像を置きました。
  2. このカバの画像はhttp://www.flickr.com/photos/43803060@N00/24308857/から引用しました。
  3. 実は、この3つ以外にもカスタムの環境を作成することができます。詳細については「環境を追加した場合のRailsCast (英語)」を参照してください。
  4. Railsのdebug情報は YAML (一種の再帰的略語であり、“YAML Ain’t Markup Language” の略とされています) 形式で表示されます。YAMLは人間だけでなくコンピュータにとっても読みやすい形式です。
  5. この時点では、ルーティングは動作していますが、対応するページが動作しているとは限りません。たとえば、/users/1/edit がUsersコントローラのeditアクションに正常にルーティングされているとしても、editアクションが存在しなければ、このURLにアクセスしたときにエラーになります。
  6. ヒンズー教では、アバターは人間や動物の形をとって神が顕現したものと考えられています。これを拡大解釈して、アバターという用語は、特にネット界隈で、その人物を表現するもの (かつその人そのものの一部でもある) という意味で使われます。
  7. リスト7.11では.gravatar_editというCSSクラスを追加しています。これは第9章でも使われます。
  8. 動作の詳細を知りたい場合は、Stack OverflowのRails信頼性トークン関連の書き込み (英語) を参照してください。
  9. 私はRails APIpluralizeを探し、これを見つけました。
  10. 実際には、これに非常に近いflash.nowを使いますが、本当に必要になるまでは使わないようにしようかと思います。
  11. 技術上は、SSLはTLS (Transport Layer Security) と名称が変わりましたが、未だに “SSL” と呼ばれ続けています。
  12. リスト7.29では少しだけコードの見栄えを修正しています。これは標準的な1行80文字の制限に合わせるための変更です。
  13. 本章ではデータモデルに対して変更を加えていなかったので、6.4のステップが済んでいれば、本当はHeroku上でマイグレーションを実行しなくても問題ないはずです。ただし、読者からトラブル報告がいくつか来ていたので、念のためheroku run rake db:migrateを実行するようにしてあります。
  14. たとえば、 ローカルマシンでRuby 2.1.4がインストールできなくて何時間も過ごしてしまい、なんとか無事にインストールできたと思ったら、先日Ruby 2.1.5がリリースされたことに気付いたときなどです。ちなみにRuby 2.1.5のインストールにも苦戦しました。
Railsチュートリアルは,Ruby/Rails のアジャイル開発を得意とする YassLab によって運営・保守されております.
継続的に良いコンテンツを提供する為に,電子書籍解説動画のご購入を検討して頂けると幸いです m(_ _)m
スポンサーシップや商用利用などに関するご相談がありましたら,お問い合わせページよりお気軽にご連絡ください :)