LoginSignup
1
1

More than 1 year has passed since last update.

Ruby on Rails チュートリアル 第2章 まとめ

Posted at

はじめに

Ruby on Railsを最近勉強し始めたので、学んだことを自分なりにまとめておこうと思う。

この記事は基本的に、「Ruby on Rails チュートリアル(第6版)」を参考にさせていただき、図や文章などもこのページから引用させていただく。

文中に知らないキーワードがある場合は、その他のサイトから引用する。

前提知識

このチュートリアルを学ぶにあたって、以下の知識をProgateやUdemyなどで学習した。

  • Rubyの基礎
  • HTML/CSSの基礎
  • JavaScriptの基礎
  • Linuxの基礎(コマンドライン)
  • Git/GitHubの基礎
  • SQLの基礎

アプリケーションの計画

まず、rails newコマンドでアプリケーションの骨組みを生成する。

$ cd ~/environment
$ rails _6.0.3_ new toy_app
$ cd toy_app/

次に、Bundlerで扱うGemfileを下記のように編集する。

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',      '6.0.3'
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.4.5', require: false

group :development, :test do
  gem 'sqlite3', '1.4.1'
  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]

--without productionオプションを追加することで、本番用のgemを除いたローカルgemをインストールする。
エラーメッセージが出た場合は、bundle updateを実行する。

$ bundle update
$ bundle install --without production

Gitでこのtoy_appをバージョン管理下におく。

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

GitHubで新しいリポジトリを作成し、生成したファイルをリモートリポジトリにプッシュする。

$ git remote add origin https://github.com/<あなたのGitHubアカウント名>/toy_app.git
$ git push -u origin master

最後に、デプロイの準備をする。
Applicationコントローラ人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にプッシュする。
このとき$$(アンパサンド演算子)を2つ用いて、GitHubとHerokuへのプッシュを連結する。
この場合、2番目のコマンドは1番目のコマンド実行に成功した場合に限って実行される。
実行時に警告メッセージが表示されることもあるが、今は無視する。

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

これで、アプリケーション自体を作成するための下準備が整った、
Webアプリケーションを作る際、アプリケーションの構造を表すための データモデル を最初に作成しておく。
今回は、ユーザーで使うモデルとマイクロポスト(Twitterでいうツイート)で使うモデルを作成する。

ユーザモデルの設計

ユーザモデルを下図のように構成する。

users
出典:第2章 Toyアプリケーション

usersはデータベースのテーブル(table)
id,name,emailの属性はそれぞれテーブルのカラム(column)に相当する。

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

マイクロポストとは、その名の通り比較的短い投稿である。
マイクロポストモデルを下図のように構成する。
マイクロポストとユーザーを関連づける(associate)ために、user_id属性も追加する。

microposts
出典:第2章 Toyアプリケーション

Userリソース

ユーザー用のデータモデルを、そのモデルを表示するためのWebインターフェイスに従って実装する。
データモデルとWebインターフェイスは、組み合わさってUsersリソースとなり、ユーザーというものを、HTTPプロトコル経由で自由に作成/取得/更新/削除できるオブジェクトとみなすことができるようになる。
この章のみ、scaffoldジェネレータを用いて生成する。

Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。scaffoldコマンドの引数には、リソース名を単数形にしたもの(この場合はUser)を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加する。
name:stringemail:stringオプションを追加することで、Userモデルの内容が設計通りになるようにする。(idはRailsによって自動的に 主キー としてデータベースに追加されるため不要)

$ rails generate scaffold User name:string email:string

次に、rails db:migrateを実行してデータベースをマイグレート(migrate)する必要がある。
下記のコマンドは、単にデータベースを更新し、usersデータモデルを作成するためのもの。

$ rails db:migrate

マイグレーションを実行したことで、ローカルWebサーバを別タブで実行できるようになった。
下記のように変更を加え、Cloud9への接続を許可する。

config/environments/development.rb
Rails.application.configure do
  .
  .
  .
  # Cloud9 への接続を許可する
  config.hosts.clear
end

次に、Railsサーバを実行する。
これで、ローカルサーバが動作するはず。

$ rails server

ユーザページの探検

Usersリソースをscaffoldで生成したことで(2.2)、ユーザー管理用のページが多数追加される。
下記の表のように対応するURLを表示すれば関連するページを表示できる。

URL アクション 用途
/users index すべてのユーザーを一覧するページ
/users/1 show id=1のユーザーを表示するページ
/users/new new 新規ユーザーを作成するページ
/users/1/edit edit id=1のユーザーを編集するページ

まずはユーザーの一覧を表示するindexアクションのURL(/users)を見てみる。

user_index
出典:第2章 Toyアプリケーション

新規ユーザを登録するために、newページを表示する。

user_new
出典:第2章 Toyアプリケーション

NameとEmailを入力して[Create User]ボタンを押すと、showページが表示される。
この時、URLが /users/1 となっているのは、このユーザのidが1だから。

users_1
出典:第2章 Toyアプリケーション

元のページに戻ってnewページに再度行けば、ユーザを追加できる。
また、ユーザの削除も[Destroy]リンクをクリックすれば実行できる。

user_destroy
出典:第2章 Toyアプリケーション

MVCの挙動

MVCパターンの観点から考察する。
例として、「/users にあるindexページをブラウザで開く」という操作をした時、内部で何が起こっているのかをMVCで見てみる。

MVC_example
出典:第2章 Toyアプリケーション

  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を受け取り、ブラウザに返す。

このリクエストは、アドレスバーにURLを入力したりリンクをクリックした時に発生する(①)。
リクエストはRailsルーティングに到達し(②)、ここでURLに基づいて適切なコントローラのアクションに割り当てられる(ディスパッチ)。
ユーザーからリクエストされたURLを、Usersリソースで使うコントローラのアクションに割り当てるためのコードは、下記のようになる。
このようなマッピングするコードはRailsのルーティング設定ファイル(config/routes.rb)に書く。

config/routes.rb
Rails.application.routes.draw do
  resources :users # シンボル
  root 'application#hello'
end

ルーティングファイルを変更して、サーバのルートURLにアクセスしたら、デフォルトのページの代わりにユーザ一覧を表示するようにする。
つまり、「/」にアクセスしたら /users を開くようにする。
よって、今回のような場合は、Appplicationコントローラ内のhelloアクションではなく、Usersコントローラ内のindexアクションを使えるようにする。

config/routes.rb
Rails.application.routes.draw do
  resources :users
  root 'users#index'
end

各ページは、Usersコントローラ内のアクションにそれぞれ対応している。
下記は、scaffoldで生成したコントローラの骨格である。
class UsersController < ApplicationControllerという記法では、ApplicationControllerという親クラスから、UsersControllerという子クラスへのクラス継承の文法を使っている。

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以外にも、create,update,destroyアクションがある。

下の表は、Railsにおける REST アーキテクチャを構成するすべてのアクションの一覧である。
RESTは、コンピュータ科学者Roy Fieldingによって提唱された「REpresentational State Transfer」という概念に基づいている。

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のユーザーを削除するアクション

showアクションと updateアクションは、どちらも /users/1 というURLに対応している。
これらのアクション同士の違いは、それらのアクションに対応するHTTP requestメソッドの違いでもある。

ここで、indexアクションについて整理する。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    @users = User.all
  end
  .
  .
  .
end

indexアクションの@users = User.allという行が、Userモデルからすべてのユーザーの一覧を取り出し(④)、@usersという変数に保存する(⑤)。
Userモデルの内容は下記にある。
継承によって多くの昨日が備わっており、特に、Active RecordというRubyライブラリのおかげで、User.allというリクエストに対してDB上の全てのユーザを返すことができる。

app/models/user.rb
class User < ApplicationRecord
end

@users変数にユーザー一覧が保存されると、コントローラは下記のビューを呼び出す(⑥)。
@記号で始まる変数をRubyではインスタンス変数と呼び、Railsのコントローラ内で宣言したインスタンス変数はビューでも使えるようになる。
この場合、下記の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が表示される(⑧).

Userリソースの欠点

scaffoldで作成したUserリソースは以下のような問題点を抱えている。

  • データの検証が行われていない
    空欄とか、メアドが変でも通ってしまう。

  • ユーザー認証が行われていない
    ログイン・ログアウトの概念がないので、誰でも操作可能。

  • テストが書かれていない
    scaffoldのテストコードは簡単なテストは含まれているが、データ検証やユーザ認証などの要求を満たしてない。

  • レイアウトやスタイルが整っていない
    ダサいし、使いにくい。

  • 理解が困難
    まず、scaffoldの中身がわっっっかんない。

Micropostsリソース

Userリソースと同様なことを行っていく

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

Userリソースと同様にMicropostsリソースもscaffoldでコードを生成し、新しいデータモデルでデータベースを更新するために、マイグレーションを行う。

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

これで、Micropostsを作成する準備ができた。
また、下記のようにRailsのrouteファイルが更新され、Micropostsリソース用のresources :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 id=1のマイクロポストを削除する

Micropostsコントローラの構造はapp/controllers/microposts_controller.rbに書かれており、UsersControllerMicropostsControllerに置き換わっている以外は、完全に同一。
これは、RESTアーキテクチャが2つのリソースに同じように反映されているから。

マイクロにする

マイクロになるように文字数制限をかける。
Railsでは、バリデーション(validation)を使って簡単に実現できる。
下記のコードで、140文字に制限する。

app/models/micropost.rb
class Micropost < ApplicationRecord
  validates :content, length: { maximum: 140 }
end

異なるデータモデルの関連付

一人のユーザに対し、複数のマイクロポストがあるとする。
UserモデルとMicropostモデルを以下のように更新することで、関連付を表現できる。

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

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

associate_datamodel
出典:第2章 Toyアプリケーション

rails consoleコマンドを用いて、ユーザとマイクロポストの関連付けを確認する。
下記のように、操作していく。

$ 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

継承の階層

UserモデルとMicropostモデルはいずれも、ApplicationRecordというクラスを継承している。
また、ApplicationRecordクラスは、Active Recordが提供する基本クラスActiveRecord::Baseを継承している。
このActiveRecord::Baseという基本クラスを継承したことによって、作成したモデルオブジェクトはデータベースにアクセスできるようになり、データベースのカラムをあたかもRubyの属性のように扱えるようになる。

inheritance_model
出典:第2章 Toyアプリケーション

UsersコントローラとMicropostsコントローラはいずれもApplicationControllerを継承している。
ApplicationControllerActionController::Baseというクラスを継承しており、このクラスは、RailsのAction Packというライブラリが提供しているコントローラの基本クラス。

inheritance_controller
出典:第2章 Toyアプリケーション

ActionController::Baseという基本クラスを継承しているため、モデルオブジェクトの操作や、送られてくるHTTP requestのフィルタリング、ビューをHTMLとして出力などの多彩な機能を実行できるようになっている。
Railsのコントローラは必ずApplicationControllerを継承しているので、Applicationコントローラで定義したルールは、アプリケーションのすべてのアクションに反映される。

デプロイ

GitHubにpush

$ git status    #追加の前にこうやって状態を確認するのはよい習慣です
$ git add -A
$ git commit -m "Finish toy app"
$ git push

Herokuに展開してもいい。

$ heroku create
$ git push heroku master

本章のまとめ

  • Scaffold機能でコードを自動生成すると、Webのあらゆる部分からモデルデータにアクセスしてやりとりできるようになる
  • Scaffoldは何よりも手っ取り早いのがとりえだが、これを元にRailsを理解するには向いていない

  • RailsではWebアプリケーションの構成にMVC(Model-View-Controller)というモデルを採用している

  • Railsが解釈するRESTには、標準的なURLセットと、データモデルとやりとりするためのコントローラアクションが含まれている

  • Railsではデータのバリデーション(validation)がサポートされており、データモデルの属性の値に制限をかけることができる

  • Railsには、さまざまなデータモデル同士を関連付けを定義するための組み込み関数が多数用意されている

  • Railsコンソールを使うと、コマンドラインからRailsアプリケーションとやりとりすることができる

個人的にわからなくて調べたキーワード

  • マイグレート

SQLを書くことなくRubyでデータベース内にテーブルを作成することができる機能

  • レンダリング
    Webページにおいて、HTML/CSSやスクリプトによる描画内容・動作の記述や、画像ファイルなど外部データなどを組み合わせ、ブラウザのウィンドウ内にページ内容を描画すること。

  • REST

    RailsアプリケーションにおけるRESTとは、アプリケーションを構成するコンポーネント(ユーザーやマイクロポストなど)を「リソース」としてモデル化することを指します。
    これらのリソースは、リレーショナルデータベースの作成/取得/更新/削除(Create/Read/Update/Delete: CRUD)操作と、4つの基本的なHTTP requestメソッド(POST/GET/PATCH/DELETE)の両方に対応しています。

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

おわりに

この章の内容は図がないとわかりづらいと思ったので、かなり引用させていただいた。
これらの図のおかげで非常に理解がしやすい内容となっていた。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1