Ruby on Rails チュートリアル
-
第6版 目次
- 第1章ゼロからデプロイまで
- 第2章Toyアプリケーション
- 第3章ほぼ静的なページの作成
- 第4章Rails風味のRuby
- 第5章レイアウトを作成する
- 第6章ユーザーのモデルを作成する
- 第7章ユーザー登録
- 第8章基本的なログイン機構
- 第9章発展的なログイン機構
- 第10章ユーザーの更新・表示・削除
- 第11章アカウントの有効化
- 第12章パスワードの再設定
- 第13章ユーザーのマイクロポスト
- 第14章ユーザーをフォローする
|
||
第6版 目次
|
||
最新版を読む |
Ruby on Rails チュートリアル
プロダクト開発の0→1を学ぼう
下記フォームからメールアドレスを入力していただくと、招待リンクが記載されたメールが届きます。リンクをクリックし、アカウントを有効化した時点から『30分間』解説動画のお試し視聴ができます。
メール内のリンクから視聴を開始できます。
第6版 目次
- 第1章ゼロからデプロイまで
- 第2章Toyアプリケーション
- 第3章ほぼ静的なページの作成
- 第4章Rails風味のRuby
- 第5章レイアウトを作成する
- 第6章ユーザーのモデルを作成する
- 第7章ユーザー登録
- 第8章基本的なログイン機構
- 第9章発展的なログイン機構
- 第10章ユーザーの更新・表示・削除
- 第11章アカウントの有効化
- 第12章パスワードの再設定
- 第13章ユーザーのマイクロポスト
- 第14章ユーザーをフォローする
第2章Toyアプリケーション
この章では、Railsの強力な機能をいくつか紹介するためのToyアプリケーションを作成します。大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使ってアプリケーションをすばやく生成し、それを元に高度なRailsプログラミングとWebプログラミングの概要を学びます。コラム 2.1でも説明しますが、本書の以後の章では基本的にこの逆のアプローチを取り、少しずつアプリケーションを作りながら各段階と概念を説明する予定です。とは言えscaffoldはRailsアプリケーションの概要を素早くつかむには最適なので、この章でのみあえて使うことにします。生成されたToyアプリケーションはブラウザのアドレスバーにURLを入力すれば動かせます。これを使って、Railsアプリの構造とRailsで推奨されているRESTアーキテクチャについて考察することにします。
Toyアプリケーションは、後に作成するサンプルアプリケーションと同様、ユーザーと、それに関連しているマイクロポストから成り立っています。このToyアプリケーションはもちろん動きますが完成品とは言えません。多くの手順が「魔法」のように思えるかもしれませんが、第3章以降で作成するサンプルアプリケーションでは同等の機能を1つ1つ手動で作成しますので、ご安心ください。その分時間がかかることになりますが、どうか最後まで本書にお付き合いいただければと思います。本書の目的は、scaffoldを使った手軽なアプローチではなく、そこを突破してRailsを深いレベルまで理解することにあります。
Railsの作者David Heinemer Hansson氏による有名な動画「15分で作るブログ(英語)」が強い印象を与えたおかげで、Railsは立ち上げ当初から一気に盛り上がりました。この後にも続々同じような動画が作られていますが、いずれもRailsの能力の一端を垣間見るにはぴったりなので、ぜひ一度ご覧ください。この動画では「15分でブログを作る」ためにScaffoldという手軽な生成機能を使っています。Railsの魔法のようなgenerate scaffold
コマンドで自動生成したコードがあるからこそ、このような早業が可能なのです。
実際、筆者はRuby on Rails のチュートリアルを書きながら、あまりにも手軽にコードを生成できるscaffoldの機能を使う誘惑にかられることが何度もありました。しかし、自動生成されたコードは量が多く複雑で、Rails初心者には向いていません。たとえ運よく動いたとしても、正常に動いている理由を解明するのはおそらく難しいでしょう。これではRailsに関する実践的な知識はほとんど身に付きません。
Railsチュートリアルでは、より実践的な知識を身につけるために、Scaffoldとほぼ逆のアプローチで開発を進めていきます。具体的には、第2章で作成する簡単なデモアプリではscaffoldを使いますが、このチュートリアルの中核である第3章以降のサンプルアプリケーションからは、scaffoldを一切使わずに開発を進めていきます。scaffold を使わない代わりに、開発の各ステップで、手頃なサイズのコードを書いてもらいます。この手頃なサイズのコードは、無理なく理解できる程度にシンプルで、かつ、ある程度の手ごたえとやりがいを得られるように配慮してあります。各ステップで理解する必要のあるコードの量はわずかですが、こうした理解を積み重ねていくことで、最終的にRailsの知識を高いレベルで身につけられるように構成されています。このようにして得た深い知識は柔軟性が高く、どのようなWebアプリを作成する時にも応用が効きます。
2.1 アプリケーションの計画
はじめに、Toyアプリケーションをどのようなものにするのか、計画を立てましょう。1.3で説明したように、rails new
コマンドでRailsのバージョン番号を指定して、アプリケーションの骨組みを生成するところから始めましょう。
rails new
する前に容量を上げておくことをオススメします。 $ cd ~/environment
$ rails _6.0.4_ new toy_app
$ cd toy_app/
1.2.1で推奨されているクラウドIDEをご利用の場合、このToyアプリは1つ目のhelloアプリと同じワークスペースに作成されるため、別のワークスペースを作成する必要はありません。ファイルが表示されるようにするには、ファイルナビゲーターの歯車アイコンをクリックして[Refresh File Tree]をクリックします。
次に、Bundlerで扱うGemfile
をテキストエディタで編集します。リスト 2.1の内容に書き換えてください。
Gemfile
Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.7.6'
gem 'rails', '6.0.4'
gem 'puma', '4.3.6'
gem 'sass-rails', '5.1.0'
gem 'webpacker', '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder', '2.9.1'
gem 'bootsnap', '1.10.3', require: false
group :development, :test do
gem 'sqlite3', '1.4.2'
gem 'byebug', '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
gem 'web-console', '4.0.1'
gem 'listen', '3.1.5'
gem 'spring', '2.1.0'
gem 'spring-watcher-listen', '2.0.1'
end
group :test do
gem 'capybara', '3.28.0'
gem 'selenium-webdriver', '3.142.4'
gem 'webdrivers', '4.1.2'
end
group :production do
gem 'pg', '1.1.4'
end
# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
1.5.1でも説明したとおり、--without production
オプションを追加することで、本番用のgemを除いたローカルgemをインストールします。
$ bundle _2.2.17_ config set --local without 'production'
$ bundle _2.2.17_ install
1.3.1でも説明したように、もしうまく動かなかったらbundle update
を実行してみてください (コラム 1.2)。
bundle install
(もしくはbundle update
)が完了したら、1.2.2でセットアップしたYarnを使ってWebpackerをインストールします。もしWebpackerを既にインストール済みの場合は設定を上書きするかどうかを聞かれるので、そのときは「no」と入力してください。
$ rails webpacker:install
最後に、GitでこのToyアプリケーションをバージョン管理下に置きます。
$ git init
$ git add -A
$ git commit -m "Initialize repository"
次に、1.4.3と同じ手順でGitHubで新しいリポジトリを作成します(このときリポジトリを図 2.1のようにprivateにしておくことをお忘れなく)。続いて、生成したファイルをこの新しいリモートリポジトリにプッシュします。
$ git remote add origin https://github.com/<あなたのGitHubアカウント名>/toy_app.git
$ git push -u origin master
最後に、1.3.4で紹介した「Hello, world!」と同じ手順でデプロイの準備をしましょう(リスト 2.2、リスト 2.3)。
hello
アクションを追加する app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def hello
render html: "hello, world!"
end
end
config/routes.rb
Rails.application.routes.draw do
root 'application#hello'
end
続いてこの変更をコミットし、Herokuにプッシュします。このとき、GitHubにもプッシュして両者を同期しておくのはよい方法です1。
$ git commit -am "Add hello"
$ heroku create
$ heroku stack:set heroku-20
$ git push && git push heroku master
ここで&&
(andと読みます)というアンパサンド演算子を2つ使って、GitHubへのプッシュとHerokuへのプッシュを連結しました。この場合、2番目のコマンドは、1番目のコマンド実行に成功した場合に限って実行されます2。
前章の1.5と同様に警告メッセージが表示されることがありますが、7.5で解説するので今は無視してください。HerokuアプリのURL以外は、前章の図1.35と同じ結果が表示されるはずです。
これでアプリケーション開発の下準備が整いました。Webアプリケーションを作る際、アプリケーションの構造を表すデータモデルを最初に設計しておくと手戻りが防げて便利なので、まずは設計から始めます。本章の題材となる“toy_app”は、ユーザーと短いマイクロポスト(Twitterにおけるツイート)のみを取り扱う小さな練習用アプリケーションです。本来はパスワード(password
)の取り扱い方なども設計する必要がありますが、今回は練習なので一旦考えずに進めていきます。本格的なパスワードの設計および実装については第6章で説明します。
それでは“toy_app”の開発に向けて、Userモデル(2.1.1)とMicropostモデル(2.1.2)のそれぞれについて考えていきましょう。
2.1.1 ユーザーのモデル設計
Webでのユーザー登録の方法が多岐にわたることからもわかるように、ユーザーという概念をデータモデルで表す方法はたくさんありますが、ここではあえて最小限の表現方法を使います。
各ユーザーには、重複のない一意のキーとなるinteger
型のID番号(id
と呼びます)を割り当て、このIDに加えて一般公開されるstring
型の名前(name
)、そして同じくstring
型のメールアドレス(email
)を持たせます。メールアドレスはユーザー名としても使われます。ユーザーのデータモデルの概要を図 2.2に示します。
詳しくは6.1.1から解説しますが、図 2.2のユーザー(users
)はデータベースのテーブル(table)に相当します。また、 id
、name
、 email
の属性はそれぞれテーブルのカラム(column: 列)に相当します。
2.1.2 マイクロポストのモデル設計
マイクロポストは比較的短い投稿であると最初に説明したことを思い出しましょう。マイクロポストは“ツイート”とTwitterの用語で呼ばれますが、本質的には一般用語です(“micro”というプレフィックスを使っているのは、Twitterが初期に用いていた“micro-blog”という説明に従った結果です)。マイクロポストのデータモデルの中核部分はユーザーよりもさらにシンプルです。id
とマイクロポストのテキスト内容を格納するtext
型のcontent
だけで構成されています3。しかし実際には、マイクロポストをユーザーと関連付ける(associate) 必要があります。そのため、マイクロポストの投稿者を記録するためのuser_id
も追加します。これにより、データモデルは図 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
)を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します4。
$ rails generate scaffold User name:string email:string
invoke active_record
create db/migrate/20190820002503_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
create test/system/users_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
create app/views/users/_user.json.jbuilder
invoke assets
invoke scss
create app/assets/stylesheets/users.scss
invoke scss
create app/assets/stylesheets/scaffolds.scss
name:string
とemail:string
オプションを追加することで、Userモデルの内容が図 2.2の表のとおりになるようにします(なお、id
パラメータはRailsによって自動的に主キーとしてデータベースに追加されるため、追加不要です)。
続いてToyアプリケーションの開発を進めるには、次のようにrails db:migrate
を実行してデータベースをマイグレート(migrate)する必要があります(リスト 2.4)。
$ rails db:migrate
== CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0027s
== CreateUsers: migrated (0.0036s) =============================
リスト 2.4のコマンドは、単にデータベースを更新し、users
データモデルを作成するためのものです(データベースのマイグレーションの詳細については6.1.1以降で説明します)。
リスト2.4のマイグレーションを実行したことで、ローカルWebサーバーを別タブで実行できるようになりました(図 1.17)。クラウドIDEのユーザーは最初に1.3.2と同じ設定を行ってtoyアプリを動くようにしてあるはずです(リスト2.5)。
config/environments/development.rb
Rails.application.configure do
.
.
.
# Cloud9 への接続を許可する
config.hosts.clear
end
続いて、 1.3.2と同様にRailsサーバーを実行します。
$ rails server
これで、1.3.2で説明したとおりにローカルサーバーが動作するはずです。特に、 /
(1.3.4で説明したように“スラッシュ”と読みます)を開くと、図1.22と同様に“hello, world!”ページが表示されるはずです。
2.2.1 ユーザーページを探検する
Usersリソースをscaffoldで生成したことで(2.2)、ユーザー管理用のページが多数追加されます。たとえば、/usersを表示すればすべてのユーザーの一覧が表示されますし、/users/newを表示すれば新規ユーザー作成ページが表示されます。このセクションでは以後、ユーザーに関連するページについて手短に説明します。その際、表 2.1に記載されている、ページとURLの関係を参照するとわかりやすいと思います。
URL | アクション | 用途 |
/users | index |
すべてのユーザーを一覧するページ |
/users/1 | show |
id=1 のユーザーを表示するページ |
/users/new | new |
新規ユーザーを作成するページ |
/users/1/edit | edit |
id=1 のユーザーを編集するページ |
まずはユーザーの一覧を表示するindex
アクションのURL(/users)を見てみましょう。もちろん、この時点ではまだユーザーは登録されていません(図 2.4)。
ユーザーを新規作成するには、図 2.5のnew
ページを表示します。なお、第7章ではこのページがユーザー登録ページに変わります。
テキストフィールドに名前とメールアドレスを入力して[Create User]ボタンを押してください。図 2.6のようにshow
ページが表示されます(緑色のウェルカムメッセージは、7.4.2で解説する flashという機能を使って表示しています)。ここで、URLが /users/1 と表示されていることに注目してください。ご想像のとおり、この1
という数字は図 2.2のid属性そのものです。7.1では、このページをユーザーのプロフィールページに作り変える予定です。
今度はユーザーの情報を変更するため、[Edit]をクリックして/users/1/edit にあるedit
ページを表示してみましょう(図 2.7)。この編集ページ上でユーザーに関する情報を変更し、[Update User]ボタンを押せば、Toyアプリケーション内のユーザー情報が変更されます(図 2.8)。(詳細は第6章で説明しますが、このユーザー情報は、Webアプリケーションの背後にあるデータベースに保存されています)。サンプルアプリケーションでも、ユーザーを編集または更新する機能を10.1で実装します。
ここで /users/new にあるnew
ページに戻り、ユーザーをもう1人作成してみましょう。index
ページを表示してみると、図 2.9のようにユーザーが追加されています。7.1ではもっと本格的なユーザー一覧ページを作成する予定です。
ユーザーの作成、表示、編集方法について説明しましたので、今度はユーザーを削除してみましょう(図 2.10)。図 2.10の[Destroy]リンクをクリックするとユーザーが削除され、indexページのユーザーは1人だけになります(もしこのとおりにならない場合は、ブラウザのJavaScriptが有効になっているかどうかを確認してください。Railsでは、ユーザーを削除するリクエストを発行するときにJavaScriptを使っています)。なお、10.4ではサンプルアプリケーションにユーザーを削除する機能を実装し、管理権限(admin)を持つユーザー以外は削除を実行できないように制限をかけます。
演習
- CSSの知識がある方は、新しいユーザーを作成し、ブラウザのHTML検証機能を使って「User was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
- emailを入力せず、名前だけを入力しようとした場合、どうなるでしょうか?
- 「@example.com」のような間違ったメールアドレスを入力して更新しようとした場合、どうなるでしょうか?
- 上記の演習で作成したユーザーを削除してみてください。ユーザーを削除したとき、Railsはどんなメッセージを表示するでしょうか?
2.2.2 MVCの挙動
これでUsersリソースの概略についての説明が終わりましたが、ここで1.3.3で紹介した MVC(Model-View-Controller = モデル-ビュー-コントローラ)パターンの観点からこのリソースを考察してみましょう。具体的には、「/users にあるindexページをブラウザで開く」という操作をしたとき、内部では何が起こっているかについてMVC(図 2.11)で説明します。
図 2.11で行われている手順の概要を以下に示します。
- ブラウザから「/users」というURLのリクエストをRailsサーバーに送信する。
- 「/users」リクエストは、Railsのルーティング機構(ルーター)によってUsersコントローラ内の
index
アクションに割り当てられる。 index
アクションが実行され、そこからUserモデルに、「すべてのユーザーを取り出せ」(User.all
)と問い合わせる。- Userモデルは問い合わせを受け、すべてのユーザーをデータベースから取り出す。
- データベースから取り出したユーザーの一覧をUserモデルからコントローラに返す。
- Usersコントローラは、ユーザーの一覧を
@users
変数(@はRubyのインスタンス変数を表す)に保存し、index
ビューに渡す。 - indexビューが起動し、ERB(Embedded RuBy: ビューのHTMLに埋め込まれているRubyコード)を実行して HTMLを生成(レンダリング)する。
- コントローラは、ビューで生成されたHTMLを受け取り、ブラウザに返す5。
上の流れをもう少し詳しく見てみることにします。最初にブラウザからのリクエストを見てみましょう。このリクエストは、アドレスバーにURLを入力したりリンクをクリックした時に発生します(図 2.11の①)。
リクエストはRailsルーティングに到達し(②)、ここでURL(とリクエストの種類: コラム 3.2参照)に基づいて適切なコントローラのアクションに割り当てられます(ディスパッチ)。
ユーザーからリクエストされたURLを、Usersリソースで使うコントローラのアクションに割り当てるためのコードは、リスト 2.6のようになります。
このようなマッピングするコードはRailsのルーティング設定ファイル(config/routes.rb)に書きます。Railsではconfig/routes.rbで、URLとアクションの組み合わせ(表 2.1)を効率よく設定していきます(:users
という一見奇妙な記法は、Ruby言語特有の「シンボル」と呼ばれるものです。詳細については4.3.3で説明します)。
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.7のコードを元に書き換えてみましょう。
config/routes.rb
Rails.application.routes.draw do
resources :users
root 'users#index'
end
2.2.1以降で紹介した各ページは、Usersコントローラ内のアクションにそれぞれ対応しています。リスト 2.8は、scaffoldで生成したコントローラの骨格です。class UsersController < ApplicationController
という記法では、Rubyのクラス継承の文法をそのまま使っていることにご注目ください(継承の概略については2.3.4、詳細については4.4で説明します)。
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
ページの数よりもアクションの数の方が多いことにお気付きでしょうか。index
、show
、new
、edit
アクションはいずれも2.2.1のページに対応していますが、それ以外にもcreate
、update
、destroy
アクションがあります。通常、これらのアクションは、ページを出力せずにデータベース上のユーザー情報を操作します(もちろんページを出力しようと思えばできますが)。
表 2.2は、RailsにおけるRESTアーキテクチャ(コラム 2.2)を構成するすべてのアクションの一覧です。RESTは、コンピュータ科学者Roy Fieldingによって提唱された「REpresentational State Transfer」という概念に基づいています6。表 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 のユーザーを削除するアクション |
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.9でindex
アクションを整理してみました(コラム 1.2でも紹介しましたが、たとえ完全に理解できなくても、コードの読み方について学ぶことは非常に重要です)。
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.10にあります。驚くほどシンプルな内容ですが、継承(2.3.4および4.4)によって多くの機能が備わっています。特に、Active RecordというRubyライブラリのおかげで、リスト 2.10のUserモデルはUser.all
というリクエストに対して、DB上のすべてのユーザーを返すことができます。
app/models/user.rb
class User < ApplicationRecord
end
@users
変数にユーザー一覧が保存されると、コントローラはリスト 2.11の ビュー を呼び出します(⑥)。
@
記号で始まる変数をRubyではインスタンス変数と呼び、Railsのコントローラ内で宣言したインスタンス変数はビューでも使えるようになります。この場合、リスト 2.11のindex.html.erb
ビューは@users
の一覧を並べ、1行ごとにHTMLの行として出力します(今はこのコードの意味がわからなくても問題ありません。これはあくまで説明のためのものです)。
app/views/users/index.html.erb
<p id="notice"><%= notice %></p>
<h1>Users</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @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 %>
</tbody>
</table>
<br>
<%= link_to 'New User', new_user_path %>
ビューはその内容をHTMLに変換し(⑦)、コントローラがブラウザにHTMLを送信して、ブラウザでHTMLが表示されます(⑧)。
2.2.3 Usersリソースの欠点
scaffoldで作成したUsersリソースは、Railsの概要を手っ取り早く説明するには良いのですが、次のようなさまざまな問題点を抱えています。
- データの検証が行われていない。 このままでは、ユーザー名が空欄であったり、でたらめなメールアドレスを入力したりしても通ってしまいます。
- ユーザー認証が行われていない。 ログイン、ログアウトが行われていないので、誰でも無制限に操作できてしまいます。
- テストが書かれていない。 厳密にはこれは正しい表現ではありません。というのも、scaffoldで生成したコードにはごく簡単なテストが一応含まれているからです。ただ、scaffoldのテストコードはデータ検証やユーザー認証、その他の必要な要求を満たしていません。
- レイアウトやスタイルが整っていない。 サイトデザインも操作法も一貫していません。
- 理解が困難。 scaffoldのコードを理解できたら「熟練」(コラム 1.2)に達していると言っても良いほど複雑です。
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のデータモデルを実装してみます7。
$ 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
新しいデータモデルでデータベースを更新するには、2.2のときと同様にマイグレーションを実行します。
$ rails db:migrate
== CreateMicroposts: migrating =============================
-- create_table(:microposts)
-> 0.0023s
== CreateMicroposts: migrated (0.0026s) ======================
これでMicropostsを作成する準備ができました。作成方法は2.2.1と同じです。既に気づいたかもしれませんがscaffoldジェネレータによってRailsのroutesファイルが更新され、リスト 2.12のようにMicropostsリソース用のルールが追加されました8。ユーザーの場合と同様に、resources :microposts
というルーティングルールは、表 2.3で示すようにマイクロポスト用のURIを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 のマイクロポストを削除する |
Micropostsコントローラ自体の構造をリスト 2.13に示します。リスト 2.13の内容は、UsersController
がMicropostsController
に置き換わっていること以外はリスト 2.8と完全に同一である点に注目してください。これは、RESTアーキテクチャが2つのリソースに同じように反映されていることを示しています。
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
マイクロポストのインデックスページで[New Micropost]をクリックして/microposts/newページをブラウザで開き(図 2.12)、新しいマイクロポストの情報を入力してマイクロポストを実際にいくつか作成してみましょう(図 2.13)。
ここではひとまずマイクロポストを1つか2つ作成し、少なくとも片方のuser_id
が1
になるようにして、2.2.1で作成した最初のユーザーのidと同じにします。結果は図 2.14のようになるはずです。
演習
- CSSの知識がある方は、新しいマイクロポストを作成し、ブラウザのHTML検証機能を使って「Micropost was successfully created.」の箇所を調べてみてください。ブラウザをリロードすると、その箇所はどうなるでしょうか?
- マイクロポストの作成画面で、ContentもUserも空にして作成しようとすると、どうなるでしょうか?
- 141文字以上の文字列をContentに入力した状態で、マイクロポストを作成しようとするとどうなるでしょうか?(ヒント: WikipediaのRubyの記事にある設計思想の引用文が140文字を超えているので、これをコピペしてみましょう)
- 上記の演習で作成したマイクロポストを削除してみましょう。
2.3.2 マイクロポストをマイクロにする
マイクロポストのマイクロという名前にふさわしくなるように、何らかの方法で文字数制限を与えてみましょう。Railsではこのようなとき、バリデーション(validation)を使って簡単に制限を加えることができます。例えばTwitterの元の設計に倣って140文字制限を加えたいときは、lengthを使うだけです。テキストエディタかIDEを使ってapp/models/micropost.rb
を開き、リスト 2.14の内容を書き加えてみてください。
app/models/micropost.rb
class Micropost < ApplicationRecord
validates :content, length: { maximum: 140 }
end
リスト 2.14のコードは、これで本当に動作するのかと思えるかもしれませんが、ちゃんと動作します(検証機能については6.2でさらに詳しく説明します)。141文字以上の新規マイクロポストを投稿してみればわかります。図 2.15に示したとおり、マイクロポストの内容が長すぎるというエラーメッセージがRailsによって表示されます(エラーメッセージの詳細については7.3.3で説明します)。
2.3.3 ユーザーはたくさん
マイクロポストを持っている
異なるデータモデル同士の関連付けは、Railsの強力な機能です。ここでは、1人のユーザーに対し複数のマイクロポストがあるとしましょう。UserモデルとMicropostモデルをそれぞれリスト 2.15とリスト 2.16のように更新することでこの関連付けを表現できます。
app/models/user.rb
class User < ApplicationRecord
has_many :microposts
end
app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
validates :content, length: { maximum: 140 }
end
この関連付けを図で表したものが図 2.16です。microposts
テーブルにはuser_id
カラムを作成してあったので、それによってRailsとActive Recordがマイクロポストとユーザーを関連付けることができるようになっています。
第13章と第14章では、関連付けられたユーザーとマイクロポストを同時に表示し、Twitterのようなマイクロポストのフィードを作成する予定です。ここでは、Railsのconsoleを使って、ユーザーとマイクロポストの関連付けを確認するにとどめます。Railsのconsoleは、Railsアプリケーションを対話的に操作することができる便利なツールです。
まず、ターミナルでrails console
コマンドを入力します。続いて、User.first
と打ち込んでデータベースから1人目のユーザー情報を取り出し、first_user
変数に保存します(リスト2.17)9。
(最後の行にはexit
を置いて、コンソールを終了する方法を示しています: ほとんどのシステムでは、Ctrl-Dを押して終了することもできます)10。
$ rails console
>> first_user = User.first
(0.5ms) SELECT sqlite_version(*)
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC
LIMIT ? [["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2019-08-20 00:39:14", updated_at: "2019-08-20 00:41:24">
>> first_user.microposts
Micropost Load (3.2ms) SELECT "microposts".* FROM "microposts" WHERE
"microposts"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content:
"First micropost!", user_id: 1, created_at: "2019-08-20 02:04:13", updated_at:
"2019-08-20 02:04:13">, #<Micropost id: 2, content: "Second micropost",
user_id: 1, created_at: "2019-08-20 02:04:30", updated_at: "2019-08-20
02:04:30">]>
>> micropost = first_user.microposts.first
Micropost Load (0.2ms) SELECT "microposts".* FROM "microposts" WHERE
"microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ?
[["user_id", 1], ["LIMIT", 1]]
=> #<Micropost id: 1, content: "First micropost!", user_id: 1, created_at:
"2019-08-20 02:04:13", updated_at: "2019-08-20 02:04:13">
>> micropost.user
=> #<User id: 1, name: "Michael Hartl", email: "michael@example.org",
created_at: "2019-08-20 00:39:14", updated_at: "2019-08-20 00:41:24"
>> exit
リスト 2.17では多くのログが表示されていますが、こういったログを頑張って読み取こうとする姿勢は「熟練」になるための欠かせない要素です(コラム 1.2)。ログには実行したSQL(Structured Query Language)や、結果として取得したRubyオブジェクトの情報が含まれています。少しだけ説明しましょう。
冒頭のfirst_user = User.first
を実行した後、リスト 2.17では次の3つを実行しています。
(1)first_user.microposts
を実行し、最初のユーザーが投稿したすべてのマイクロポストを取得します。つまりuser_id
がfirst_user
のid(ここでは 1
)と一致するマイクロポストをすべて出力しています。
(2)上記(1)で取得できる投稿(マイクロポスト)の中から、最初の投稿をmicropost
変数に保存しています。
(3)上記(2)で保存したmicropost
変数から、その投稿をしたユーザー情報をmicropost.user
で取得しています。
細かなRubyの文法は第4章の 2.17で、ユーザーとマイクロポストの関連付けは第13章と第14章で詳しく説明するので、今は雰囲気を掴むだけで十分です。
演習
- ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください(コラム 1.2で紹介した技術の出番です)。うまく表示できたかどうか、/users/1 にアクセスして確認してみましょう。
- リスト 2.18は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう(図 2.17のようになっていると成功です)。
- リスト 2.19の
(コードを書き込む)
となっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください(図 2.18)。
app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
validates :content, length: { maximum: 140 },
presence: true
end
app/models/user.rb
class User < ApplicationRecord
has_many :microposts
validates (コードを書き込む), presence: true # (コードを書き込む)の中身を書き換えてください
validates (コードを書き込む), presence: true # (コードを書き込む)の中身を書き換えてください
end
2.3.4 継承の階層
最後に、Toyアプリケーションで使っているRailsのコントローラとモデルのクラス階層について簡単に解説します。この節を理解するには、多少なりともオブジェクト指向プログラミング(OOP)の経験が必要です(特にクラスを理解している必要があります)。「まだクラスを知らない」という方も心配しないでください。4.4でクラスの概念についてじっくり解説するので、ここでの説明が理解できなくても問題ありません。
最初に、モデルの継承構造について説明します。リスト 2.20とリスト 2.21を比較してみると、UserモデルとMicropostモデルはいずれも、ApplicationRecord
というクラスを継承しています(Rubyでは継承関係を<
記号で表現します)。また、ApplicationRecord
クラスは、Active Recordが提供する基本クラス ActiveRecord::Base
を継承しています。図 2.19は、このクラス間の関係をまとめたものです。このActiveRecord::Base
という基本クラスを継承したことによって、作成したモデルオブジェクトはデータベースにアクセスできるようになり、データベースのカラムをあたかもRubyの属性のように扱えるようになります。
User
クラスにおける継承 app/models/user.rb
class User < ApplicationRecord
.
.
.
end
Micropost
クラスにおける継承 app/models/micropost.rb
class Micropost < ApplicationRecord
.
.
.
end
コントローラの継承構造も、モデルの継承構造と本質的には同じです。リスト 2.22とリスト 2.23を比較してみると、UsersコントローラとMicropostsコントローラはいずれもApplicationControllerを継承しています。リスト 2.24をみると気づくように、ApplicationController
がActionController::Base
というクラスを継承していることがわかります。このクラスは、RailsのAction Packというライブラリが提供しているコントローラの基本クラスです。これらのクラス同士の関係を図 2.20に示します。
UsersController
クラスにおける継承 app/controllers/users_controller.rb
class UsersController < ApplicationController
.
.
.
end
MicropostsController
クラスにおける継承 app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
.
.
.
end
ApplicationController
クラスにおける継承 app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
.
.
.
end
モデルの継承関係と同様に、UsersコントローラもMicropostsコントローラも最終的にはActionController::Base
という基本クラスを継承しています。このため、どちらのコントローラもモデルオブジェクトの操作や、送られてくるHTTP requestのフィルタリング、ビューをHTMLとして出力するなどの多彩な機能を実行できるようになっています。Railsのコントローラは必ずApplicationController
を継承しているので、Applicationコントローラで定義したルールは、アプリケーションのすべてのアクションに反映されます。例えば9.1では、ログインとログアウト用のヘルパーメソッドをサンプルアプリケーションのすべてのコントローラで利用できるようにしています。
演習
- Applicationコントローラのファイルを開き、
ApplicationController
がActionController::Base
を継承している部分のコードを探してみてください。 ApplicationRecord
がActiveRecord::Base
を継承しているコードはどこにあるでしょうか? 先ほどの演習を参考に、探してみてください。ヒント: コントローラと本質的には同じ仕組みなので、app/models
ディレクトリ内にあるファイルを調べてみましょう。)
2.3.5 アプリケーションをデプロイする
Micropostsリソースの説明が終わりましたので、ここでリポジトリをGitHubに登録しましょう。
$ 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 create
、git push heroku master
を実行してから上のコマンド操作を実行してください。)
このような場合は、Herokuのログを調べて問題を分析します。
$ heroku logs
ログを上にスクロールしてさかのぼると、以下のような行があるはずです。
ActionView::Template::Error (PG::UndefinedTable: ERROR: relation "users" does
not exist
「users
テーブルが見つからない」という大きなヒントを得られました。ありがたいことに、解決方法についてはリスト 2.4で説明済みです。必要なのは、データベースマイグレーションを実行することだけです(これでmicroposts
テーブルもついでに作られます)。
こうしたコマンドをHerokuで実行するには、以下のように、通常のRailsコマンドの前にheroku run
を追加します。
$ heroku run rails db:migrate
このコマンドを実行すると、先ほど定義したユーザーとマイクロポストのデータモデルを使って、Heroku上のデータベースが更新されます。マイグレーションが完了すれば、Toyアプリを実際のPostgreSQLデータベースをバックエンドに配置した本番環境で利用できるようになっているはずです(図 2.22)。11
最後に、もし2.3.3.1の演習をやっていれば、「最初のユーザーのマイクロポストを表示する」というコードを削除する必要があることに注意してください。削除し忘れていた場合でも慌てないで。該当する行を削除して、もう一度コミットをして、そして、Herokuにpushするだけです。
2.4 最後に
非常に簡単ではありますが、ついにRailsアプリケーションを最後まで完成させました。この章で作成したToyアプリケーションには良いところもありますが、さまざまな弱点もあります。
良い
- Rails全体を高度なレベルで概観できた
- MVCモデルを紹介できた
- RESTアーキテクチャに初めて触れた
- データモデルの作成を初めて行った
- データベースを背後に持つWebアプリケーションを本番環境で動かした
弱点
- レイアウトもスタイルも設定されていない
- “Home” や “About” といった静的の的なページがない
- ユーザーがパスワードを設定できない
- ユーザーが画像を投稿できない
- ログインの仕組みがない
- セキュリティのための仕組みがまったくない
- ユーザーとマイクロポストの自動関連付けが行われていない
- Twitterのような「フォロワー(following)機能」や「フォロー中(followed)機能」がない
- マイクロポストをフィードできない
- 適切なテストがない
- 理解が困難
次の章以降では、本章で学んだ良い点を保ちつつ、弱点を1つ1つ克服していきます。
2.4.1 本章のまとめ
- Scaffoldでコードを自動生成すると、Web上からデータモデルにアクセスし、やりとりできるようになる
- Scaffoldは素早く開発するための機能で、学習用では無いため、ScafolldだけでRailsを理解するのは難しい
- Railsでは、Webアプリケーションの構成にMVC(Model-View-Controller)というアークテクチャーを採用している
- Railsでは、HTTP requestメソッドとその送信先(URL)に応じて、呼び出されるコントローラやアクションが決まる
- Railsでは、データのバリデーション(validation)がサポートされており、データモデルの値に制限をかけることができる
- Railsには、データモデル同士を関連付けするためのさまざまなメソッド(has_manyなど)が用意されている
- Railsコンソールを使うと、コマンドラインからRailsアプリケーションを操作することができる
string
型の上限がかなり大きいので、そこに保存するとかなりの部分が余ってしまいます。text
型ならもっと手頃で、かつ最大長を必要に応じて大きくすることもできます。実際のTwitterは、英語ツイートで140文字という制約を280文字にまで拡張しました。このことは、柔軟性がいかに重要であるかを示す見事な実例となっています。string
は255文字(\( 2^8-1 \))まで利用できるのが普通であり、これは140文字を収容するには十分すぎるほどですが、280文字を収容するには不足です。text
を使えば、どちらの場合にも統一的に対応できるようになります。Users
ではなく)User
と指定します。generate Micropost
と単数形になっていたのはこのためです。2.6.3 :001 >
などと表示されることがありますが、Rubyのバージョンは今後変わるので、例では >>
のようになっています。
Railsチュートリアルは YassLab 社によって運営されています。
コンテンツを継続的に提供するため、書籍・動画・質問対応サービスなどもご検討していただけると嬉しいです。
研修支援や教材連携にも対応しています。note マガジンや YouTube チャンネルも始めたので、よければぜひ遊びに来てください!