Ruby on Rails チュートリアル

実例を使って Rails を学ぼう

第4版 目次

第2章Toyアプリケーション

この章では、Railsの強力な機能をいくつか紹介するためのおもちゃアプリケーションを作成します。大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使ってアプリケーションをすばやく生成し、それを元に高度なRailsプログラミングとWebプログラミングの概要を学びます。コラム 1.2で述べたように、本書の以後の章では基本的にこの逆のアプローチを取り、少しずつアプリケーションを作りながら各段階と概念を説明する予定ですが、scaffoldはRailsアプリケーションの概要を素早くつかむには最適なので、この章でのみあえて使うことにします。生成されたToyアプリケーションはブラウザのアドレスバーにURLを入力すれば動かせます。これを使って、Railsアプリの構造とRailsで推奨されているRESTアーキテクチャについて考察することにします。

Toyアプリケーションは、後に作成するサンプルアプリケーション(sampleapp)と同様、ユーザーと、それに関連しているマイクロポストから成り立っています。このToyアプリケーションはもちろん動きますが完成品ではなく、しかも多くの手順が「魔法」のように思えるかもしれません。3以降で作成するサンプルアプリケーション(sampleapp)では同等の機能を1つ1つ手動で作成しますので、ご安心ください。その分時間がかかることになりますが、どうか最後まで本書にお付き合いいただければと思います。本書の目的は、scaffoldを使った即席のアプローチによる表面的な理解ではなく、そこを突破してRailsを深いレベルまで理解することにあります。

2.1 アプリケーションの計画

はじめに、Toyアプリケーションをどのようなものにするのか、計画を立てましょう。1.3で説明したように、rails newコマンドでRailsのバージョン番号を指定して、アプリケーションの骨組みを生成するところから始めましょう。

最新バージョンで始めたい方は『第6版』をご覧ください。
$ cd ~/environment
$ rails _5.1.6_ new toy_app
$ cd toy_app/

1.2.1で推奨されているクラウドIDEをご利用の場合は、この2つ目のアプリは1つ目のアプリと同じワークスペースに作成されるという点に注意する必要があります。2つ目のアプリのために別のワークスペースを作成する必要はありません。ファイルが表示されるようにするには、ファイルナビゲーターの歯車アイコンをクリックして [Refresh File Tree] をクリックします。

次に、Bundlerで扱うGemfileをテキストエディタで編集します。リスト 2.1の内容に書き換えてください。

リスト 2.1: Toyアプリケーション用のGemfile
source 'https://rubygems.org'

gem 'rails',        '5.1.6'
gem 'puma',         '3.9.1'
gem 'sass-rails',   '5.0.6'
gem 'uglifier',     '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks',   '5.0.1'
gem 'jbuilder',     '2.7.0'

group :development, :test do
  gem 'sqlite3', '1.3.13'
  gem 'byebug',  '9.0.6', platform: :mri
end

group :development do
  gem 'web-console',           '3.5.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.0.2'
  gem 'spring-watcher-listen', '2.0.1'
end

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

# Windows環境ではtzinfo-dataというgemを含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

リスト 2.1の内容はリスト 1.13と同じです。

1.5.1でも説明したとおり、--without productionオプションを追加することで、本番用のgemを除いたローカルgemをインストールします。

$ bundle install --without production

1.3.1でも説明したように、もしうまく動かなかったらbundle updateを実行してみてください (コラム 1.1)。

最後に、GitでこのToyアプリケーションをバージョン管理下に置きます。

$ git init
$ git add -A
$ git commit -m "Initialize repository"

次に、Bitbucketで [Create] ボタンをクリックして新しいリポジトリを作成します(図 2.1)。続いて、生成したファイルをこの新しいリモートリポジトリにプッシュします。

$ git remote add origin git@bitbucket.org:<username>/toy_app.git
$ git push -u origin --all
images/figures/create_demo_repo_bitbucket
図 2.1: Bitbucketにtoyアプリのリポジトリを作成する

最後に、1.3.4で紹介した「Hello, world!」と同じ手順でデプロイの準備をしましょう (リスト 2.2リスト 2.3)。

リスト 2.2: Applicationコントローラにhelloアクションを追加する app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "hello, world!"
  end
end
リスト 2.3: ルートルーティングを設定する config/routes.rb
Rails.application.routes.draw do
  root 'application#hello'
end

続いてこの変更をコミットし、Herokuにプッシュします。

$ git commit -am "Add hello"
$ heroku create
$ git push heroku master

1.5のときと同じように警告メッセージが表示されることがありますが、無視して構いません。詳しくは7.5で解説します。Herokuアプリのアドレス以外は、図 1.23と同じになるはずです。

これで、アプリケーション自体を作成するための下準備が整いました。Webアプリケーションを作る際、アプリケーションの構造を表すためのデータモデルを最初に作成しておくのが普通です。

今回のToyアプリケーションでは、ユーザーと短いマイクロポスト (Twitterにおけるツイート) のみをサポートするマイクロブログを作成します。そこで、まずはアプリケーションのユーザーで使うモデルを作成 (2.1.1) し、次にマイクロポストで使うモデルを作成します (2.1.2)。

2.1.1 ユーザーのモデル設計

Webでのユーザー登録の方法が多岐にわたることからもわかるように、ユーザーという概念をデータモデルで表す方法はたくさんありますが、ここではあえて最小限の表現方法を使います。

各ユーザーには、重複のない一意のキーとなるinteger型のID番号 (idと呼びます) を割り当て、このIDに加えて一般公開されるstring型の名前 (name)、そして同じくstring型のメールアドレス (email) を持たせます。メールアドレスはユーザー名としても使われます。ユーザーのデータモデルの概要を図 2.2に示します。

demo_user_model
図 2.2: ユーザーのデータモデル

詳しくは6.1.1から解説しますが、図 2.2のユーザー (users) はデータベースのテーブル (table) に相当します。また、 idnameemail の属性はそれぞれテーブルのカラム (column: 列) に相当します。

2.1.2 マイクロポストのモデル設計

マイクロポストのデータモデルはユーザーよりもさらにシンプルです。idとマイクロポストのテキスト内容を格納するtext型のcontentだけで構成されています1。しかし実際には、マイクロポストをユーザーと関連付ける (associate) 必要があります。そのため、マイクロポストの投稿者を記録するためのuser_idも追加します。これにより、データモデルは図 2.3のようになります。

demo_micropost_model
図 2.3: マイクロポストのデータモデル

2.3.3では、user_idという属性を使って、1人のユーザーに複数のマイクロポストが関連付けられるという構造を簡潔に説明します。詳細は13で完全に説明します。

2.2 Usersリソース

ここでは、2.1.1で説明したユーザー用のデータモデルを、そのモデルを表示するためのWebインターフェイスに従って実装します。このデータモデルとWebインターフェイスは、組み合わさってUsersリソースとなり、ユーザーというものを、HTTPプロトコル経由で自由に作成/取得/更新/削除できるオブジェクトとみなすことができるようになります。「はじめに」で約束したとおり、このUsersリソースはすべてのRailsプロジェクトに標準装備されているscaffoldジェネレータで生成します。scaffoldで生成された膨大なコードを今詳細に読む必要はありません。今の段階ではおそらく混乱するだけでしょう。

Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。scaffoldコマンドの引数には、リソース名を単数形にしたもの (この場合はUser) を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します2

$ rails generate scaffold User name:string email:string
      invoke  active_record
      create    db/migrate/20160515001017_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml
      invoke  resource_route
       route  resources :users
      invoke  scaffold_controller
      create    app/controllers/users_controller.rb
      invoke    erb
      create      app/views/users
      create      app/views/users/index.html.erb
      create      app/views/users/edit.html.erb
      create      app/views/users/show.html.erb
      create      app/views/users/new.html.erb
      create      app/views/users/_form.html.erb
      invoke    test_unit
      create      test/controllers/users_controller_test.rb
      invoke    helper

      create      app/helpers/users_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/users/index.json.jbuilder
      create      app/views/users/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/users.coffee
      invoke    scss
      create      app/assets/stylesheets/users.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss

name:stringemail:stringオプションを追加することで、Userモデルの内容が図 2.2の表のとおりになるようにします (なお、idパラメータはRailsによって自動的に主キーとしてデータベースに追加されるため、追加不要です)。

続いてToyアプリケーションの開発を進めるには、次のようにrails db:migrateを実行してデータベースをマイグレート (migrate) する必要があります (リスト 2.4)。

リスト 2.4: データベースをマイグレートする
$ rails db:migrate
==  CreateUsers: migrating =================================
-- create_table(:users)
   -> 0.0017s
==  CreateUsers: migrated (0.0018s) ========================

リスト 2.4のコマンドは、単にデータベースを更新し、usersデータモデルを作成するためのものです (データベースのマイグレーションの詳細については6.1.1以降で説明します)。

なお、Rails 5以前のバージョンでは、db:migrateコマンドはrailsコマンドではなくrakeコマンドが使われていました。このため、Rails 4以前の古いRailsアプリケーションではRakeの使い方を覚える必要があります。詳しく知りたい場合はコラム 2.1を読んでみてください。

コラム 2.1. Rake

Unixでは、ソースコードから実行用プログラムをビルドするために主にMakeというツールが使われてきました。Rakeはいわば、Rubyで記述することのできるRuby版のMakeといった言語です。

Rails 4以前ではRakeを使っているため、古いRailsアプリケーションを扱うためにはRakeについて学ぶ必要があります。おそらくもっとも頻繁に使われていたRakeコマンドは、データベースのデータモデルを更新するためのrake db:migrateコマンドと、自動化されたテストスイートを実行するためのrake testコマンドの2つでしょう。

また、Rails 4以前のアプリケーションでは、rakeコマンドのバージョンをGemfileで定義しているため、Bundlerのbundler execコマンドを通して実行する必要があります。したがって、例えばRails 5におけるマイグレーションコマンドは次のようになりますが、

  $ rails db:migrate

Rails 4以前では、次のように実行する必要があります。

  $ bundle exec rake db:migrate

リスト 2.4までの手順が完了すると、次のコマンドでローカルWebサーバーを別タブで実行できるようになります (図 1.10)。

$ rails server

これで、1.3.2で説明したとおりにローカルサーバーが動作するはずです (クラウドIDEで作業している方は、IDEに搭載された簡易ブラウザではなく、必ずChromeやFirefoxなどの汎用ブラウザの別タブを使ってdevelopmentサーバーの結果を確認してください)。

2.2.1 ユーザーページを探検する

ブラウザでルートURL「/」(「スラッシュ」と読みます: 1.3.4)参照) を開くと、図 1.15と同じ「hello, world!」のページが表示されますが、Usersリソースをscaffoldで生成したことで、ユーザー管理用のページが多数追加されている点が異なります。例えば、/usersを表示すればすべてのユーザーの一覧が表示されますし、/users/newを表示すれば新規ユーザー作成ページが表示されます。このセクションでは以後、ユーザーに関連するページについて手短に説明します。その際、表 2.1に記載されている、ページとURLの関係を参照するとわかりやすいと思います。

URL アクション 用途
/users index すべてのユーザーを一覧するページ
/users/1 show id=1のユーザーを表示するページ
/users/new new 新規ユーザーを作成するページ
/users/1/edit edit id=1のユーザーを編集するページ
表 2.1: Usersリソースにおける、ページとURLの関係。

まずはユーザーの一覧を表示するindexアクションのURL (/users) を見てみましょう。もちろん、この時点ではまだユーザーは登録されていません (図 2.4)。

images/figures/demo_blank_user_index_3rd_edition
図 2.4: Usersリソース (/users) ページの最初の状態

ユーザーを新規作成するには、図 2.5newページを表示します。なお、7ではこのページがユーザー登録ページに変わります。

images/figures/demo_new_user_3rd_edition
図 2.5: 新規ユーザー作成ページ (/users/new)

テキストフィールドに名前とメールアドレスを入力して [Create User] ボタンを押してください。図 2.6のようにshowページが表示されます (緑色のウェルカムメッセージは、7.4.2で解説する flashという機能を使って表示しています)。ここで、URLが /users/1 と表示されていることに注目してください。ご想像のとおり、この1という数字は図 2.2のid属性そのものです。7.1では、このページをユーザーのプロフィールページに作り変える予定です。

images/figures/demo_show_user_3rd_edition
図 2.6: ユーザー表示用のページ (/users/1)

今度はユーザーの情報を変更するため、/users/1/edit にあるeditページを表示してみましょう (図 2.7)。この編集ページ上でユーザーに関する情報を変更し、[Update User] ボタンを押せば、Toyアプリケーション内のユーザー情報が変更されます (図 2.8)。(詳細は6で説明しますが、このユーザー情報は、Webアプリケーションの背後にあるデータベースに保存されています)。サンプルアプリケーション(sampleapp)でも、ユーザーを編集または更新する機能を10.1で実装します。

images/figures/demo_edit_user_3rd_edition
図 2.7: ユーザー編集用のページ (/users/1/edit)
images/figures/demo_update_user_3rd_edition
図 2.8: 情報が更新されたユーザー

ここで /users/new にあるnewページに戻り、ユーザーをもう1人作成してみましょう。indexページを表示してみると、図 2.9のようにユーザーが追加されています。7.1ではもっと本格的なユーザー一覧ページを作成する予定です。

images/figures/demo_user_index_two_3rd_edition
図 2.9: 2人目のユーザーが追加された一覧ページ (/users)

ユーザーの作成、表示、編集方法について説明しましたので、今度はユーザーを削除してみましょう (図 2.10)。図 2.10の [Destroy] リンクをクリックするとユーザーが削除され、indexページのユーザーは1人だけになります (もしこのとおりにならない場合は、ブラウザのJavaScriptが有効になっているかどうかを確認してください。Railsでは、ユーザーを削除するリクエストを発行するときにJavaScriptを使っています)。なお、10.4ではサンプルアプリケーション(sampleapp)にユーザーを削除する機能を実装し、管理権限 (admin) を持つユーザー以外は削除を実行できないように制限をかけます。

images/figures/demo_destroy_user_3rd_edition
図 2.10: ユーザーを削除する

演習

  1. CSSを知っている読者へ: 新しいユーザーを作成し、ブラウザのHTMLインスペクター機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
  2. emailを入力せず、名前だけを入力しようとした場合、どうなるでしょうか?
  3. 「@example.com」のような間違ったメールアドレスを入力して更新しようとした場合、どうなるでしょうか?
  4. 上記の演習で作成したユーザーを削除してみてください。ユーザーを削除したとき、Railsはどんなメッセージを表示するでしょうか?

2.2.2 MVCの挙動

これでUsersリソースの概略についての説明が終わりましたが、ここで1.3.3で紹介した MVC (Model-View-Controller = モデル-ビュー-コントローラ) パターンの観点からこのリソースを考察してみましょう。具体的には、「/users にあるindexページをブラウザで開く」という操作をしたとき、内部では何が起こっているかについてMVC (図 2.11) で説明します。

解説動画「第2章 前編: 12. Rails 処理の流れ」で詳しく解説
images/figures/mvc_detailed
図 2.11: RailsにおけるMVC

図 2.11で行われている手順の概要を以下に示します。

  1. ブラウザから「/users」というURLのリクエストをRailsサーバーに送信する。
  2. 「/users」リクエストは、Railsのルーティング機構 (ルーター) によってUsersコントローラ内のindexアクションに割り当てられる。
  3. indexアクションが実行され、そこからUserモデルに、「すべてのユーザーを取り出せ」(User.all)と問い合わせる。
  4. Userモデルは問い合わせを受け、すべてのユーザーをデータベースから取り出す。
  5. データベースから取り出したユーザーの一覧をUserモデルからコントローラに返す。
  6. Usersコントローラは、ユーザーの一覧を@users変数 (@はRubyのインスタンス変数を表す) に保存し、indexビューに渡す。
  7. indexビューが起動し、ERB (Embedded RuBy: ビューのHTMLに埋め込まれているRubyコード) を実行して HTMLを生成 (レンダリング) する。
  8. コントローラは、ビューで生成されたHTMLを受け取り、ブラウザに返す3

上の流れをもう少し詳しく見てみることにします。最初にブラウザからのリクエストを見てみましょう。このリクエストは、アドレスバーにURLを入力したりリンクをクリックした時に発生します (図 2.11の①)。 リクエストはRailsルーティングに到達し (②)、ここでURL (とリクエストの種類: コラム 3.2参照) に基づいて適切なコントローラのアクションに割り当てられます (ディスパッチ)。 ユーザーからリクエストされたURLを、Usersリソースで使うコントローラのアクションに割り当てるためのコードは、リスト 2.5のようになります。 このようなマッピングを行うコードはRailsのルーティング設定ファイル (config/routes.rb) に書きます。Railsではconfig/routes.rbで、URLとアクションの組み合わせ (表 2.1) を効率よく設定していきます (:usersという一見奇妙な記法は、Ruby言語特有の「シンボル」と呼ばれるものです。詳細については4.3.3で説明します)。

リスト 2.5: Railsルートで使うUsersリソース用のルール config/routes.rb
Rails.application.routes.draw do
  resources :users
  root 'application#hello'
end

それでは、このルーティングファイルを変更してみましょう。サーバーのルートURLにアクセスしたら、デフォルトのページの代わりにユーザー一覧を表示するようにします。つまり、「/」(スラッシュ) にアクセスしたら/usersを開くようにします。リスト 2.3で次のような変更をしたことについて思い出してください。

root 'application#hello'

これにより、ルートにアクセスするとApplicationコントローラ内のhelloアクションにルーティングされるようになったのでした。今回の場合は、Usersコントローラのindexアクションを使いたいので、リスト 2.6のコードを元に書き換えてみましょう

リスト 2.6: ルートからusersへのルーティングを追加する config/routes.rb
Rails.application.routes.draw do
  resources :users
  root 'users#index'
end

2.2.1以降で紹介した各ページは、Usersコントローラ内のアクションにそれぞれ対応しています。リスト 2.7は、scaffoldで生成したコントローラの骨格です。class UsersController < ApplicationControllerという記法では、Rubyのクラス継承の文法をそのまま使っていることにご注目ください (継承の概略については2.3.4、詳細については4.4で説明します)。

リスト 2.7: Usersコントローラの骨格 app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    .
    .
    .
  end

  def show
    .
    .
    .
  end

  def new
    .
    .
    .
  end

  def edit
    .
    .
    .
  end

  def create
    .
    .
    .
  end

  def update
    .
    .
    .
  end

  def destroy
    .
    .
    .
  end
end

ページの数よりもアクションの数の方が多いことにお気付きでしょうか。indexshowneweditアクションはいずれも2.2.1のページに対応していますが、それ以外にもcreateupdatedestroyアクションがあります。通常、これらのアクションは、ページを出力せずにデータベース上のユーザー情報を操作します (もちろんページを出力しようと思えばできますが)。

表 2.2は、RailsにおけるRESTアーキテクチャ (コラム 2.2) を構成するすべてのアクションの一覧です。RESTは、コンピュータ科学者Roy Fieldingによって提唱された「REpresentational State Transfer」という概念に基づいています4表 2.2のURLには重複しているものがあることにご注目ください。例えば、showアクションと updateアクションは、どちらも/users/1というURLに対応しています。これらのアクション同士の違いは、それらのアクションに対応するHTTP requestメソッドの違いでもあります。HTTP requestメソッドの詳細については3.3で説明します。

HTTPリクエスト URL アクション 用途
GET /users index すべてのユーザーを一覧するページ
GET /users/1 show id=1のユーザーを表示するページ
GET /users/new new 新規ユーザーを作成するページ
POST /users create ユーザーを作成するアクション
GET /users/1/edit edit id=1のユーザーを編集するページ
PATCH /users/1 update id=1のユーザーを更新するアクション
DELETE /users/1 destroy id=1のユーザーを削除するアクション
表 2.2: リスト 2.5のUsersリソースが提供するRESTfulなルート
コラム 2.2. REpresentational State Transfer (REST)

Rails関連の書籍を読んでいると “REST” という略語をよく見かけます。これはREpresentational State Transferの略です。RESTは、インターネットそのものやWebアプリケーションなどの、分散・ネットワーク化されたシステムやアプリケーションを構築するためのアーキテクチャのスタイルの1つです。REST理論そのものはかなり抽象的ですが、RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント (ユーザーやマイクロポストなど) を「リソース」としてモデル化することを指します。これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除 (Create/Read/Update/Delete: CRUD) 操作と、4つの基本的なHTTP requestメソッド (POST/GET/PATCH/DELETE) の両方に対応しています。(HTTP requestメソッドの詳細については3.3で、具体的にはコラム 3.2で説明します)。

Rails開発者にとっては、RESTfulなスタイルを採用することで、作成すべきコントローラやアクションの決定が楽になります。作成(C)・取得(R)・更新(U)・削除(D)を行うリソースだけでアプリケーション全体を構成してしまうことすら可能です。ユーザーやマイクロポストなどに関しては自然にリソース化できるので問題ありません。14では、「ユーザーをフォローする」というやや複雑な課題について、REST理論を用いた自然で効率的なモデリングを披露します。

UsersコントローラとUserモデルの関係をさらに考察するために、リスト 2.8indexアクションを整理してみました (コラム 1.1でも紹介しましたが、たとえ完全に理解できなくても、コードの読み方について学ぶことは非常に重要です)。

リスト 2.8: Toyアプリケーションの簡潔なユーザーindexアクション app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    @users = User.all
  end
  .
  .
  .
end

indexアクションに@users = User.allという行があります (図 2.11の③に相当)。これによって、Userモデルからすべてのユーザーの一覧を取り出し (④)、@usersという変数に保存します (⑤)。なお、@usersは「あっと ゆーざーず」と発音します。Userモデルの内容はリスト 2.9にあります。驚くほどシンプルな内容ですが、継承 (2.3.4および4.4) によって多くの機能が備わっています。特に、Active RecordというRubyライブラリのおかげで、リスト 2.9のUserモデルはUser.allというリクエストに対して、DB上のすべてのユーザーを返すことができます。

リスト 2.9: ToyアプリケーションのUserモデル app/models/user.rb
class User < ApplicationRecord
end

@users変数にユーザー一覧が保存されると、コントローラはリスト 2.10ビュー を呼び出します (⑥)。 @記号で始まる変数をRubyではインスタンス変数と呼び、Railsのコントローラ内で宣言したインスタンス変数はビューでも使えるようになります。この場合、リスト 2.10index.html.erbビューは@usersの一覧を並べ、1行ごとにHTMLの行として出力します (今はこのコードの意味がわからなくても問題ありません。これはあくまで説明のためのものです)。

リスト 2.10: indexアクションに対応しているビュー app/views/users/index.html.erb
<h1>Listing users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th colspan="3"></th>
    </tr>
  </thead>

<% @users.each do |user| %>
  <tr>
    <td><%= user.name %></td>
    <td><%= user.email %></td>
    <td><%= link_to 'Show', user %></td>
    <td><%= link_to 'Edit', edit_user_path(user) %></td>
    <td><%= link_to 'Destroy', user, method: :delete,
                                     data: { confirm: 'Are you sure?' } %></td>
  </tr>
<% end %>
</table>

<br>

<%= link_to 'New User', new_user_path %>

ビューはその内容をHTMLに変換し (⑦)、コントローラがブラウザにHTMLを送信して、ブラウザでHTMLが表示されます (⑧)。

演習

  1. 図 2.11を参考にしながら、/users/1/edit というURLにアクセスしたときの振る舞いについて図を書いてみてください。
  2. 図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。
  3. ユーザーの情報を編集するページのファイル名は何でしょうか?

2.2.3 Usersリソースの欠点

scaffoldで作成したUsersリソースは、Railsの概要を手っ取り早く説明するには良いのですが、次のようなさまざまな問題点を抱えています。

  • データの検証が行われていない。 このままでは、ユーザー名が空欄であったり、でたらめなメールアドレスを入力したりしても通ってしまいます。
  • ユーザー認証が行われていない。 ログイン、ログアウトが行われていないので、誰でも無制限に操作できてしまいます。
  • テストが書かれていない。 厳密にはこれは正しい表現ではありません。というのも、scaffoldで生成したコードにはごく簡単なテストが一応含まれているからです。ただ、scaffoldのテストコードはデータ検証やユーザー認証、その他の必要な要求を満たしていません。
  • レイアウトやスタイルが整っていない。 サイトデザインも操作法も一貫していません。
  • 理解が困難。 scaffoldのコードを理解できるぐらいなら、そもそも本書を読む必要はないでしょう。

2.3 Micropostsリソース

Usersリソースを生成して内容を理解しましたので、今度はMicropostsリソースで同じことをやってみましょう。なお、この節全体について、Micropostsリソースを理解する際には2.2のuser要素と比較しながら進めることをお勧めします。実際、これらの2つのリソースはさまざまな面で似通っています。RailsのRESTful構造を身体に叩きこむには、繰り返し学ぶのが一番です。UsersリソースとMicropostsリソースの構造の類似点を理解することが、この章の主要な目的です。

2.3.1 マイクロポストを探検する

Usersリソースの場合と同様に、Micropostsリソースもscaffoldでコードを生成してみましょう。rails generate scaffoldコマンドを使って、図 2.3のデータモデルを実装してみます5

$ rails generate scaffold Micropost content:text user_id:integer
      invoke  active_record
      create    db/migrate/20160515211229_create_microposts.rb
      create    app/models/micropost.rb
      invoke    test_unit
      create      test/models/micropost_test.rb
      create      test/fixtures/microposts.yml
      invoke  resource_route
       route  resources :microposts
      invoke  scaffold_controller
      create    app/controllers/microposts_controller.rb
      invoke    erb
      create      app/views/microposts
      create      app/views/microposts/index.html.erb
      create      app/views/microposts/edit.html.erb
      create      app/views/microposts/show.html.erb
      create      app/views/microposts/new.html.erb
      create      app/views/microposts/_form.html.erb
      invoke    test_unit
      create      test/controllers/microposts_controller_test.rb
      invoke    helper

      create      app/helpers/microposts_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/microposts/index.json.jbuilder
      create      app/views/microposts/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/microposts.coffee
      invoke    scss
      create      app/assets/stylesheets/microposts.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.scss

(Springに関連したエラーが発生したら、同じコマンドをもう一度実行してみてください)。新しいデータモデルでデータベースを更新するには、2.2のときと同様にマイグレーションを実行します。

$ rails db:migrate
==  CreateMicroposts: migrating =============================
-- create_table(:microposts)
   -> 0.0023s
==  CreateMicroposts: migrated (0.0026s) ======================

これでMicropostsを作成する準備ができました。作成方法は2.2.1と同じです。既に気づいたかもしれませんがscaffoldジェネレータによってRailsのroutesファイルが更新され、リスト 2.11のようにMicropostsリソース用のルールが追加されました6。ユーザーの場合と同様に、resources :micropostsというルーティングルールは、表 2.3で示すようにマイクロポスト用のURIをMicropostsコントローラ内のアクションに割り当てます。

リスト 2.11: Railsルートで使うMicropostsリソース用のルール config/routes.rb
Rails.application.routes.draw do
  resources :microposts
  resources :users
  root 'users#index'
end
HTTPリクエスト URL アクション 用途
GET /microposts index すべてのマイクロポストを表示するページ
GET /microposts/1 show id=1のマイクロポストを表示するページ
GET /microposts/new new マイクロポストを新規作成するページ
POST /microposts create マイクロポストを新規作成するアクション
GET /microposts/1/edit edit id=1のマイクロポストを編集するページ
PATCH /microposts/1 update id=1のマイクロポストを更新するアクション
DELETE /microposts/1 destroy id1のマイクロポストを削除する
表 2.3: Micropostsリソースが提供するリスト 2.11のRESTfulルート

Micropostsコントローラ自体の構造をリスト 2.12に示します。リスト 2.12の内容は、UsersControllerMicropostsControllerに置き換わっていること以外はリスト 2.7完全に同一である点に注目してください。これは、RESTアーキテクチャが2つのリソースに同じように反映されていることを示しています。

リスト 2.12: Micropostsコントローラの骨格 app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  .
  .
  .
  def index
    .
    .
    .
  end

  def show
    .
    .
    .
  end

  def new
    .
    .
    .
  end

  def edit
    .
    .
    .
  end

  def create
    .
    .
    .
  end

  def update
    .
    .
    .
  end

  def destroy
    .
    .
    .
  end
end

/microposts/newページをブラウザで開き、新しいマイクロポストの情報を入力してマイクロポストをいくつか作成してみましょう (図 2.12)。

images/figures/demo_new_micropost_3rd_edition
図 2.12: 新しいマイクロポストの作成ページ (/microposts/new)

ここではひとまずマイクロポストを1つか2つ作成し、少なくとも片方のuser_id1になるようにして、2.2.1で作成した最初のユーザーのidと同じにします。結果は図 2.13のようになるはずです。

images/figures/demo_micropost_index_3rd_edition
図 2.13: マイクロポストのindexページ (/microposts)

演習

  1. CSSを知っている読者へ: 新しいマイクロポストを作成し、ブラウザのHTMLインスペクター機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
  2. マイクロポストの作成画面で、ContentもUserも空にして作成しようとするどうなるでしょうか?
  3. 141文字以上の文字列をContentに入力した状態で、マイクロポストを作成しようとするとどうなるでしょうか? (ヒント: WikipediaのRubyの記事にある1段落目がちょうど150文字程度ですが、どうなりますか?)
  4. 上記の演習で作成したマイクロポストを削除してみましょう。

2.3.2 マイクロポストをマイクロにする

マイクロポストのマイクロという名前にふさわしくなるように、何らかの方法で文字数制限を与えてみましょう。Railsではこのようなとき、バリデーション (validation) を使って簡単に制限を加えることができます。例えばTwitterのように140文字制限を加えたいときは、lengthを使うだけです。テキストエディタかIDEを使ってapp/models/micropost.rbを開き、リスト 2.13の内容を書き加えてみてください。

リスト 2.13: マイクロポストの最大文字数を140文字に制限する。 app/models/micropost.rb
class Micropost < ApplicationRecord
  validates :content, length: { maximum: 140 }
end

リスト 2.13のコードは、これで本当に動作するのかと思えるかもしれませんが、ちゃんと動作します (検証機能については6.2でさらに詳しく説明します)。141文字以上の新規マイクロポストを投稿してみればわかります。図 2.14に示したとおり、マイクロポストの内容が長すぎるというエラーメッセージがRailsによって表示されます (エラーメッセージの詳細については7.3.3で説明します)。

images/figures/micropost_length_error_3rd_edition
図 2.14: マイクロポストの作成に失敗した場合のエラーメッセージ

演習

  1. 先ほど2.3.1.1の演習でやったように、もう一度Contentに141文字以上を入力してみましょう。どのように振る舞いが変わったでしょうか?
  2. CSSを知っている読者へ: ブラウザのHTMLインスペクター機能を使って、表示されたエラーメッセージを調べてみてください。

2.3.3 ユーザーはたくさんマイクロポストを持っている

異なるデータモデル同士の関連付けは、Railsの強力な機能です。ここでは、1人のユーザーに対し複数のマイクロポストがあるとしましょう。UserモデルとMicropostモデルをそれぞれリスト 2.14リスト 2.15のように更新することでこの関連付けを表現できます。

リスト 2.14: 1人のユーザーに複数のマイクロポストがある。 app/models/user.rb
class User < ApplicationRecord
  has_many :microposts
end
リスト 2.15: 1つのマイクロポストは1人のユーザーにのみ属する。 app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 140 }
end

この関連付けを図で表したものが図 2.15です。micropostsテーブルにはuser_idカラムを作成してあったので、それによってRailsとActive Recordがマイクロポストとユーザーを関連付けることができるようになっています。

images/figures/micropost_user_association
図 2.15: マイクロポストとユーザーの関連付け

1314では、関連付けられたユーザーとマイクロポストを同時に表示し、Twitterのようなマイクロポストのフィードを作成する予定です。ここでは、Railsのconsoleを使って、ユーザーとマイクロポストの関連付けを確認するにとどめます。Railsのconsoleは、Railsアプリケーションを対話的に操作することができる便利なツールです。

解説動画「第2章 後編: 4. rails console」から詳しく解説

まず、ターミナルでrails consoleコマンドを入力します。続いて、User.firstと打ち込んでデータベースから1人目のユーザー情報を取り出し、first_user変数に保存します7

$ rails console
>> first_user = User.first
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2016-05-15 02:01:31", updated_at: "2016-05-15 02:01:31">
>> first_user.microposts
=> [#<Micropost id: 1, content: "First micropost!", user_id: 1,
created_at: "2011-11-03 02:37:37", updated_at: "2011-11-03 02:37:37">,
"2016-05-15 02:37:37", updated_at: "2016-05-15 02:37:37">,
#<Micropost id: 2, content: "Second micropost", user_id: 1,
created_at: "2016-05-15 02:38:54", updated_at: "2016-05-15 02:38:54">]
>> micropost = first_user.microposts.first
=> #<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2016-05-15 02:37:37", updated_at: "2016-05-15 02:37:37">
>> micropost.user
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2016-05-15 02:01:31", updated_at: "2016-05-15 02:01:31">
>> exit

(最後の行のようにexitを実行するとrails consoleを終了できます。多くのシステムでは、Ctrl-dキーを押して終了することもできます。)8 first_user.micropostsというコードを実行すると、そのユーザーに関連付けられているマイクロポストにアクセスできます。このときActive Recordは、user_idfirst_userのid (ここでは1) と等しいマイクロポストを自動的に返します。Active Recordの関連付け機能については1314で詳しく解説します。

演習

  1. ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください (コラム 1.1で紹介した技術の出番です)。うまく表示できたかどうか、/users/1 にアクセスして確認してみましょう。
  2. リスト 2.16は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう (図 2.16のようになっていると成功です)。
  3. リスト 2.17FILL_INとなっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください (図 2.17)。
リスト 2.16: マイクロポストのコンテンツが存在しているかどうかの確認 app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  validates :content, length: { maximum: 140 },
                      presence: true
end
images/figures/micropost_content_cant_be_blank
図 2.16: Userモデルの存在確認バリデーションの結果
リスト 2.17: Userモデルに存在性のバリデーションを追加する app/models/user.rb
class User < ApplicationRecord
  has_many :microposts
  validates FILL_IN, presence: true    # 「FILL_IN」をコードに置き換えてください
  validates FILL_IN, presence: true    # 「FILL_IN」をコードに置き換えてください
end
images/figures/user_presence_validations
図 2.17: Userモデルの存在性がうまく検証できた場合の結果

2.3.4 継承の階層

最後に、Toyアプリケーションで使っているRailsのコントローラとモデルのクラス階層について簡単に解説します。この節を理解するには、多少なりともオブジェクト指向プログラミング (OOP) の経験が必要です (特にクラスを理解している必要があります)。「まだクラスを知らない」という方も心配しないでください。4.4でクラスの概念についてじっくり解説するので、ここでの説明が理解できなくても問題ありません。

最初に、モデルの継承構造について説明します。リスト 2.18リスト 2.19を比較してみると、UserモデルとMicropostモデルはいずれも、ApplicationRecordというクラスを継承しています (Rubyでは継承関係を<記号で表現します)。また、ApplicationRecordクラスは、Active Recordが提供する基本クラス ActiveRecord::Base を継承しています。図 2.18は、このクラス間の関係をまとめたものです。このActiveRecord::Baseという基本クラスを継承したことによって、作成したモデルオブジェクトはデータベースにアクセスできるようになり、データベースのカラムをあたかもRubyの属性のように扱えるようになります。

リスト 2.18: Userクラスにおける継承 app/models/user.rb
class User < ApplicationRecord
  .
  .
  .
end
リスト 2.19: Micropostクラスにおける継承 app/models/micropost.rb
class Micropost < ApplicationRecord
  .
  .
  .
end
images/figures/demo_model_inheritance_4th_ed
図 2.18: UserモデルとMicropostモデルの継承階層

コントローラの継承構造も、モデルの継承構造と本質的には同じです。リスト 2.20リスト 2.21を比較してみると、UsersコントローラとMicropostsコントローラはいずれもApplicationControllerを継承しています。リスト 2.22をみると気づくように、ApplicationControllerActionController::Baseというクラスを継承していることがわかります。このクラスは、RailsのAction Packというライブラリが提供しているコントローラの基本クラスです。これらのクラス同士の関係を図 2.19に示します。

リスト 2.20: UsersControllerクラスにおける継承 app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
end
リスト 2.21: MicropostsControllerクラスにおける継承 app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  .
  .
  .
end
リスト 2.22: ApplicationControllerクラスにおける継承 app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  .
  .
  .
end
images/figures/demo_controller_inheritance
図 2.19: UsersコントローラとMicropostsコントローラにおける継承関係

モデルの継承関係と同様に、UsersコントローラもMicropostsコントローラも最終的にはActionController::Baseという基本クラスを継承しています。このため、どちらのコントローラもモデルオブジェクトの操作や、送られてくるHTTP requestのフィルタリング、ビューをHTMLとして出力などの多彩な機能を実行できるようになっています。 Railsのコントローラは必ずApplicationControllerを継承しているので、Applicationコントローラで定義したルールは、アプリケーションのすべてのアクションに反映されます。例えば9.1では、ログインとログアウト用のヘルパーメソッドをサンプルアプリケーション(sampleapp)のすべてのコントローラで利用できるようにしています。

演習

  1. Applicationコントローラのファイルを開き、ApplicationControllerActionController::Baseを継承している部分のコードを探してみてください。
  2. ApplicationRecordActiveRecord::Baseを継承しているコードはどこにあるでしょうか? 先ほどの演習を参考に、探してみてください。ヒント: コントローラと本質的には同じ仕組みなので、app/modelsディレクトリ内にあるファイルを調べてみると...?)

2.3.5 アプリケーションをデプロイする

Micropostsリソースの説明が終わりましたので、ここでリポジトリをBitbucketに登録しましょう。

$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push

通常、Gitのコミットはなるべくこまめに行うようにし、更新をあまりためないことが望ましいのですが、この章の締めくくりとしてサイズの大きなコミットを1度だけ行うぐらいであれば問題ありません。

この時点で、Toyアプリケーションを1.5のようにHerokuに展開してもかまいません。

$ git push heroku

(上のコマンド操作では、2.1のHerokuアプリを作成済みであることが前提です。アプリを作成していないのであれば、先にheroku creategit push heroku masterを実行してから上のコマンド操作を実行してください。)

アプリケーションのデータベースを動作させるためには、次のheroku runコマンドを実行して本番データベースのマイグレーションを行う必要もあります (リスト 2.4)。

$ heroku run rails db:migrate

このコマンドを実行すると、先ほど定義したユーザーとマイクロポストのデータモデルを使って、Heroku上のデータベースが更新されます。マイグレーションが完了すれば、Toyアプリを実際のPostgreSQLデータベースをバックエンドに配置した本番環境で利用できるようになっているはずです(図 2.20)。

最後に、もし2.3.3.1の演習をやっていれば、「最初のユーザーのマイクロポストを表示する」というコードを削除する必要があることに注意してください。削除し忘れていた場合でも慌てないで。該当する行を削除して、もう一度コミットをして、そして、Herokuにpushするだけです。

images/figures/toy_app_production
図 2.20: Toyアプリを本番環境で実行する

演習

  1. 本番環境で2〜3人のユーザーを作成してみましょう。
  2. 本番環境で最初のユーザーのマイクロポストを作ってみましょう
  3. マイクロポストのContentに141文字以上を入力した状態で、マイクロポストを作成してみましょう。リスト 2.13で加えたバリデーションが本番環境でもうまく動くかどうか、確認してみてください。

2.4 最後に

非常に簡単ではありますが、ついにRailsアプリケーションを最後まで完成させました。この章で作成したToyアプリケーションには良いところもありますが、さまざまな弱点もあります。

良い

  • Rails全体を高度なレベルで概観できた
  • MVCモデルを紹介できた
  • RESTアーキテクチャに初めて触れた
  • データモデルの作成を初めて行った
  • データベースを背後に持つWebアプリケーションを本番環境で動かした

弱点

  • レイアウトもスタイルも設定されていない
  • “Home” や “About” のような定番の静的なページがない
  • ユーザーがパスワードを設定できない
  • ユーザーが画像を置けない
  • ログインの仕組みがない
  • セキュリティのための仕組みがまったくない
  • ユーザーとマイクロポストの自動関連付けが行われていない
  • Twitterのような「フォロワー (following)機能」や「フォロー中 (followed)機能」がない
  • マイクロポストをフィードできない
  • まともなテストがない
  • 理解が困難

本書では以後、このアプリの良い点を保ちつつ、弱点を1つ1つ克服していきます。

2.4.1 本章のまとめ

  • Scaffold機能でコードを自動生成すると、Webのあらゆる部分からモデルデータにアクセスしてやりとりできるようになる
  • Scaffoldは何よりも手っ取り早いのがとりえだが、これを元にRailsを理解するには向いていない
  • RailsではWebアプリケーションの構成にMVC (Model-View-Controller) というモデルを採用している
  • Railsが解釈するRESTには、標準的なURLセットと、データモデルとやりとりするためのコントローラアクションが含まれている
  • Railsではデータのバリデーション (validation) がサポートされており、データモデルの属性の値に制限をかけることができる
  • Railsには、さまざまなデータモデル同士を関連付けを定義するための組み込み関数が多数用意されている
  • Railsコンソールを使うと、コマンドラインからRailsアプリケーションとやりとりすることができる
1. マイクロポストはその名のとおり短いのですが、string型の上限がかなり大きいのでそこに保存するとかなり余ってしまいます。text型ならもっと手頃で、かつ最大長を必要に応じて大きくすることもできます。
2. scaffoldで指定する名前は、モデル名の命名の習慣に従って「単数形」にします。リソースやコントローラは「複数形」で表し、モデルは「単数形」で表します。したがって、Scaffoldでは (Usersではなく) Userと指定します。
3. いくつかの文献では「ビューは (ApacheやNginxなどのWebサーバーを経由して) ブラウザにHTMLを直接返す」と説明しています。著者は、Railsの実際の実装とは無関係に、コントローラを軸にして情報の流れを捉えると分かりやすいと考えています。つまりビュー単体を切り離して理解しようとするのではなく、「情報の流れの中心にコントローラというハブがある」と考えると良いでしょう。
4. 論文の正式なタイトル: Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000.
5. Userでscaffoldを実行した場合と同様に、scaffoldジェネレータではマイクロポストでもRailsモデルを単数形とする習慣に従います。実行したコマンドがgenerate Micropostと単数形になっていたのはこのためです。
6. scaffoldで生成した実際のコードにはリスト 2.11よりも多くの改行が追加されることがありますが、Rubyでは単なる改行は無視されるので問題ありません。
7. 実際のターミナル上では、Rubyのバージョンに応じてプロンプトが2.1.1 :001 >などと表示されることがありますが、例では >> に統一してあります。
8. 「Ctrl-C」と同様に「Ctrl-D」と表記する慣習になっていますが、 実際に押すのは小文字のdなので、シフトキーを押し続ける必要はありません。