Ruby on Rails チュートリアルの要約「第2章 Toyアプリケーション」

よこのじ(@yokonoji_work)です。

Ruby on Rails チュートリアル「第2章 Toyアプリケーション」の要約記事です。

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

この章では、大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って、アプリケーションをつくる。

それを元にして、RailsプログラミングとWebプログラミングを学ぶ。

3章からは機能を一つずつ作りながら各機能を学んでいくという進み方になっている。

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

railsのバージョンを指定して、アプリケーションを生成する。

$ cd ~/environment
$ rails _5.1.6_ new toy_app
$ cd toy_app/

Gemfileは「リスト2.1」の内容に書き換える。

そして、本番環境用のgemを除いてインストールする。

$ bundle install --without production

赤字でエラーが出たので内容を見てみるとbundle updateするように書かれているので、それに従う。もう一度インストールするとうまくいった。

Gitでアプリケーションをバージョン管理する。

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

Bitbucketで新しいリポジトリを作成して、次のコマンドを入力する。

$ git remote add origin git@bitbucket.org:ユーザー名/リポジトリ名.git
$ git push -u origin --all

まずはHello Worldを表示させる。

# application_controller.rb にアクションを追加
def hello
  render html: "hello, world!"
end

# routes.rb にルーティングを設定
root 'application#hello'

ファイルを変更したら、herokuにプッシュする。

$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
$ git commit -am "Add hello"
$ heroku create
$ git push heroku master

heroku createで表示されたURLにアクセスするとHello Worldを表示できた。

2.1.1 ユーザーのモデル設計

各ユーザーに持たせる情報

  • 重複のないinteger型のID番号 (id)
  • 一般公開されるstring型の名前 (name)
  • string型のメールアドレス (email)

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

マイクロポストのデータモデル

  • integer型のid
  • マイクロポストのテキスト内容を格納するtext型のcontent
  • 投稿者を記録するためのuser_id

2.2 Usersリソース

ユーザー情報を持つUsersは、Railsに標準装備されているscaffoldジェネレータで生成します。

$ rails generate scaffold User name:string email:string
$ rails db:migrate

idパラメータはRailsによって自動的に主キーとしてデータベースに追加されます。

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

rails server でサーバーを立ち上げて、URLの後ろに/usersを付けるとユーザー一覧が確認でき、さらに/newを付けると新規ユーザー作成ページが表示される。

  • /users index すべてのユーザーを一覧するページ
  • /users/1 show id=1のユーザーを表示するページ
  • /users/new new 新規ユーザーを作成するページ
  • /users/1/edit edit id=1のユーザーを編集するページ

演習

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

2.2.2 MVCの挙動

MVC (Model-View-Controller = モデル-ビュー-コントローラ)の観点からusersを見てみる。

「/users にあるindexページをブラウザで開く」ときのMVCの図

mvcの挙動

2.2.2 MVCの挙動

  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。

ここで、「/」にアクセスしたらHello Wordldではなくて、ユーザー一覧を表示するようにルーティングを変更する。

変更前 root 'application#hello'
変更後 root 'users#index'

/ にアクセスすると、ユーザー一覧が表示されました。

REST = REpresentational State Transfer これは、/users/1 という同じURLでもshowアクションとupdateアクション対応していることの違いはHTTP request (POST/GET/PATCH/DELETE) の違いであることを示すもの。詳しくは表2で。

演習

  1. 図 2.11を参考にしながら、/users/1/edit というURLにアクセスしたときの振る舞いについて図を書いてみてください。
    ▶ routerがeditを見つけて、対応するアクションに飛ばす=>id=1のユーザーを見つけるようにmodelに指示する=>ユーザー情報をViewに渡す=>返ってきたHTMLをブラウザに投げる
    ▶editアクションへのルーティング記述がないなと思ったら、resources :users がそれに当たるらしい。
    RESTfulなURLを自動生成(resources)
    Ruby on Rails resourcesメソッドのネスト例
  2. 図示した振る舞いを見ながら、Scaffoldで生成されたコードの中でデータベースからユーザー情報を取得しているコードを探してみてください。
    ▶editアクションが動く前にdef set_user の @user = User.find(params[:id]) でURLからidを取得してユーザーを探している。
  3. ユーザーの情報を編集するページのファイル名は何でしょうか?
    ▶edit.html.erb

2.2.3 Usersリソースの欠点

scaffoldの問題点

  • データの検証が行われておらず、ユーザー名やメールアドレスが空欄やおかしな入力でも登録できる。
  • ユーザー認証が行われていないため、 ログイン、ログアウトという操作がなく、誰でも編集・削除のそうさができる。
  • テストがわずかに書かれているが、上記の問題に対するテストがないので、不十分。
  • レイアウトやスタイルが整っていないので、 サイトデザインも操作法も不揃い。

2.3 Micropostsリソース

ユーザーの扱いをUsersで学んだので、投稿を扱うMicropostsの理解をする。

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

scaffoldでMicropostsを生成する。

$ rails generate scaffold Micropost content:text user_id:integer
$ rails db:migrate

そうすると、routes.rbに resources :microposts が追加されている。これは作成・編集・削除などのアクションが一括で設定される記述。

/microposts/new にアクセスして、投稿内容とユーザーidを設定して登録後、/microposts にアクセスすると投稿一覧が確認できる。

演習

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

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

投稿に文字制限をかけるにはvalidationを使う。

140文字に制限するときは、app/models/micropost.rbに次の記述を追加する。

validates :content, length: { maximum: 140 }

1401文字以上入力すると、ちゃんとエラーがでるようになった。

演習

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

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

1人のユーザーは複数のマイクロポストを持つ。この場合はapp/models/user.rbに次の記述を追加する。

has_many :microposts

1つのマイクロポストは1人のユーザーにのみ属する場合は、app/models/micropost.rbに次の記述を追加する。

belongs_to :user

ユーザーとマイクロポストの関係はこのようになっている。

ユーザーとマイクロソフトの関係

「図 2.15: マイクロポストとユーザーの関連付け」

演習

  1. ユーザーのshowページを編集し、ユーザーの最初のマイクロポストを表示してみましょう。同ファイル内の他のコードから文法を推測してみてください (コラム 1.1で紹介した技術の出番です)。うまく表示できたかどうか、/users/1 にアクセスして確認してみましょう。
    ▶<%= @user.microposts.first.content %>とすると良い
  2. リスト 2.16は、マイクロポストのContentが存在しているかどうかを検証するバリデーションです。マイクロポストが空でないことを検証できているかどうか、実際に試してみましょう (図 2.16のようになっていると成功です)。
    ▶app/models/micropost.rbでvalidates :content, length: { maximum: 140 }, presence: trueと変更すると
    「User must exist」「Content can’t be blank」というエラーを返してくれた
  3. リスト 2.17のFILL_INとなっている箇所を書き換えて、Userモデルのnameとemailが存在していることを検証してみてください (図 2.17)。
    ▶app/models/user.rbにvalidationを追加した
    validates :name, presence: true => Name can’t be blankというエラーメッセージ
    validates :email, presence: true => Email can’t be blankというエラーメッセージ

2.3.4 継承の階層

UserモデルとMicropostモデルは、ApplicationRecordというクラスを継承している。

ApplicationRecordクラスは、ActiveRecord::Base を継承している。

モデルの階層

コントローラの継承も同様で、次のような構造になっている。

コントローラの階層

演習

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

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

Bitbucketに登録する。

$ git status
$ git add -A
$ git commit -m "Finish toy app"
$ git push
$ git push heroku
$ heroku run rails db:migrate

演習で「最初のユーザーのマイクロポストを表示する」を実装した場合は、その記述を削除しておかないとエラーが出る。

演習

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

2.4 最後に

とりあえず、Toy_appというアプリケーションが完成しました。

以降の章では、次のような点を改良していきます。

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

2.4.1 本章のまとめ

  • Scaffold機能でコードを自動生成すると、Webのあらゆる部分からモデルデータにアクセスしてやりとりできるようになる
  • Scaffoldは何よりも手っ取り早いのがとりえだが、これを元にRailsを理解するには向いていない
  • RailsではWebアプリケーションの構成にMVC (Model-View-Controller) というモデルを採用している
  • Railsが解釈するRESTには、標準的なURLセットと、データモデルとやりとりするためのコントローラアクションが含まれている
  • Railsではデータのバリデーション (validation) がサポートされており、データモデルの属性の値に制限をかけることができる
  • Railsには、さまざまなデータモデル同士を関連付けを定義するための組み込み関数が多数用意されている
  • Railsコンソールを使うと、コマンドラインからRailsアプリケーションとやりとりすることができる