#近況報告
エンジニア転職成功しました。YouTubeもはじめました。
著者略歴
YUUKI
ポートフォリオサイト:Pooks
現在:RailsTutorial2周目
#第2章 難易度 ★ 4時間
挫折しないRailsチュートリアルの進め方を先にお読みください↓↓
この章ではRailsの根幹であるMVCモデルについて解説する。
##アプリケーションの計画
###ユーザーのモデル設計
ユーザーという概念をデータモデルで表すと、以下の通りになる。
users = データベースのテーブル
行 = レコード
列 = カラム
integer = 整数
string = 文字列
idのintegerには重複のない一意のキーを割り当てる。
name,emailのstringにはそれぞれ文字列を割り当てる。
なお、emailはユーザー名としても使われる。
id,name,emailは「属性」と呼ぶ。
integer,stringは「データ型」と呼ぶ。
###マイクロポストのモデル設計
content = idとマイクロポストのテキスト内容を格納するテキスト
text = テキストを入れるデータ型
user_id = マイクロポストと投稿者を関連付ける属性
##Usersリソース
今回の章ではscaffoldジェネレーターと呼ばれる、Railsに標準装備されているチート機能で生成する。
$ rails generate scaffold User name:string email:string
読み方
「scaffoldコマンドでUsersリソースを生成し、name属性、email属性をstring型で定義しますよ」
scaffoldコマンドの引数にはリソース名を単数形にしたものを使う点に注意
これを行うことで、「Userモデル」の内容が先程説明した「usersテーブル」のようになる。
idパラメータはRailsによって自動で主キーとして追加される。
次に、データベースをマイグレートする
$ rails db:migrate
== 20181201023516 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0015s
== 20181201023516 CreateUsers: migrated (0.0022s) =============================
このコマンドで生成したusersデータモデルをデータベースに反映させる。
ここまでできたら、rails serverコマンドで、ローカルでhello worldテキストが表示されているかを確認する。
###ユーザーページを探検する
scaffoldコマンドを使ったことにより、ユーザー管理用のページが自動生成される。
URLから
/users
/users/new
を辿って見ると、ユーザー一覧、新規ユーザー作成ページが表示されることが分かる。
URL | アクション | 用途 |
---|---|---|
/users | index | すべてのユーザーを一覧するページ |
/users/1 | show | id=1のユーザーを表示するページ |
/users/new | new | 新規ユーザーを作成するページ |
/users/1/edit | edit | id=1のユーザーを編集するページ |
出典:第2章 Toyアプリケーション |
/users/new から、新規ユーザーを作成してみる。
こんな感じに作られる。
users/2に表示されている内容はshowアクションの中身である。
今度はeditページを表示してみる。
編集してUpdateしてみる。
別のユーザーを新規作成してusersページを表示。
あとはDestoryして削除してみる。
スッキリ。
このような挙動を作るのは結構大変なんだけれども、こういった仕組みをscaffold commandで一発構築。ただし、初心者はscaffoldに頼ってはいけないとTutorial師匠のお叱りがあるので、これから仕組みを覚えていこう。
##MVCの挙動
Railsの根幹とも言えるMVCの挙動を紹介。
例えば、「/usersにあるindexページをブラウザで開く」という操作をする場合。
これはイメージを頭の中に叩き込んで覚えよう。
①ブラウザから/usersと言うURLリクエストをRailsサーバーに送信
②/usersリクエストは、RouterによってUsersコントローラ内のindexアクションに割り当てられる。
③indexアクションが実行され、user.rbからUserモデルにuser.allで全てのユーザーを取り出すよう問い合わせる
④Userモデルが全ユーザーをデータベースから取り出す
⑤取り出したユーザー一覧をコントローラに返す
⑥Usersコントローラはユーザーの一覧を@users変数に保存し、indexビューに渡す。
⑦indexビューが起動、ERB拡張子を実行して、HTMLを作成する。
⑧コントローラは、ビューで生成されたHTMLを受け取り、ブラウザに返す。
例えば、routes.rbをこのように変更する。
Rails.application.routes.draw do
resources :users
root 'users#index'
end
すると、ルートにアクセスするとusersコントローラのindexアクションの中身が表示される。
それ以外にもcreate、update、destroyアクションがあり、これらはページを出力せずに内部で動いている。
ここで、Usersコントローラのindexアクションのコードを見てみる。
def index
@users = User.all
end
このコードの意味は
「Userモデルから全てのレコードを取り出し、それを@usersと言う変数に代入する」
メソッド内で変数を定義する際は、@を付ける点に注意。
これを「インスタンス変数」と言う。
インスタンス変数には、initializeメソッドとオブジェクトのインスタンスメソッドだけアクセスできる
ちなみに、Active RecordというRubyライブラリのお陰で、userモデルがUser.allというリクエストを実行できる(DBから引っ張ってこれる)
最後に、indexアクションに対応しているviewを見てみる。
<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>
@users.each do |user|の部分に注目。
この部分で、インスタンス変数@usersを読み出し、 each do |user|で、user変数に繰り返し表示している。
user.nameuser.emailでは、User変数の中の名前、メールアドレスを表示している。
|user|の部分はブロックパラメータと呼ぶ。
###Usersリソースの欠点
scaffoldで作成したUsersリソースの問題は以下
- データの検証が行われていない
ユーザー名が空欄だったり、デタラメなメールアドレスでも通る(例えばasdfadsfds←こんなんでも通る)
- ユーザー認証が行われていない
誰でも無制限に操作できてしまう。この問題を回避する為には、ログインログアウト機能を導入する
- テストがお粗末
満足にテストコードが書かれていない
- レイアウトが酷い
見た目がダサいよね。
- 理解が困難
scaffoldで生成されたコードの理解は大変。
これらの問題を、Tutorialを通して解決して学んでいく。
###Micropostsリソース
Micropostsリソースでも同じことをやってみる。
rails generate scaffold Micropost content:text user_id:integer
$ rails db:migrate
== 20181202035043 CreateMicroposts: migrating =================================
-- create_table(:microposts)
-> 0.0051s
== 20181202035043 CreateMicroposts: migrated (0.0060s) ========================
micropostsテーブルが作成された。
ここでroutes.rbを確認。
Rails.application.routes.draw do
resources :microposts
resources :users
root 'users#index'
end
resourcesにmicropostsが追加されている。
このルーティングの意味は、マイクロポスト用のURIをMicropostsコントローラ内のアクションに割り当てる。
ほんとは個別で用意するけどresourcesが自動で割り当てる。
ここで、newページを開いてマイクロポストを作成してみる。
user_id1が作成したmicropostsページが表示された。
####マイクロポストへのバリデーション
マイクロポストの入力欄の最大文字数を制限する
class Micropost < ApplicationRecord
validates :content, length: { maximum: 140 } #contentに入力できる文字数を最大140文字に制限する。
end
マイクロポストで141文字以上の文字列を投稿してみる。
文字列が長すぎるとエラーが出る。
先程設定したバリデーションが機能していることが分かる。
###データモデル同士の関連付け
Userモデルと、Micropostモデルの関連付けを行う。
class User < ApplicationRecord
has_many :microposts #Micropostモデルと関連付け
end
class Micropost < ApplicationRecord
belongs_to :user #Userモデルと関連付け
validates :content, length: { maximum: 140 }
end
has_manyとbelongs_toの関係性は多重度で言うところの1対多である。
一人のユーザーに対し、多くの投稿が存在するイメージ。
###Rails Console
ターミナルでrails console
コマンドを打つと、Railsを実験的に動かすことができる。
今回はDBからuserモデルに格納されている一人目のユーザー情報取り出し、
first_user
変数に格納する。
$ rails c
Running via Spring preloader in process 5117
Loading development environment (Rails 5.1.6)
$ first_user = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ? [["LIMIT", 1]]
=> #<User id: 5, name: "moku", email: "yuukitetsuyanet@gmail.com", created_at: "2018-12-01 07:10:12", updated_at: "2018-12-01 07:10:12">
$ first_user.microposts
Micropost Load (0.1ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ? [["user_id", 5], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 4, content: "こらこら", user_id: 5, created_at: "2018-12-02 10:47:21", updated_at: "2018-12-02 10:47:21">]>
$ 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", 5], ["LIMIT", 1]]
=> #<Micropost id: 4, content: "こらこら", user_id: 5, created_at: "2018-12-02 10:47:21", updated_at: "2018-12-02 10:47:21">
$ micropost.user
=> #<User id: 5, name: "moku", email: "yuukitetsuyanet@gmail.com", created_at: "2018-12-01 07:10:12", updated_at: "2018-12-01 07:10:12">
2.4.1 :005 > exit
初見だと「なんじゃこりゃ!」ってなるかも知れんが実際は簡単なことやってる。
①最初のコマンドでfirst_user
変数にuserモデルの一人目のユーザーを代入。
②first_user.microposts
でid5に関連付いたmicroposts(今回はid4)を読み出す
③micropost
変数にfirst_user.microposts.first
でid4のmicropostsの中にある最初の投稿を代入
④micropost.user
で「id4の投稿」をしたユーザー情報を表示
⑤exit
でrails consoleの終了
Rails Consoleからは実際にデータベースを弄る事も可能なので、micropostsを新規作成したり、userを追加したりすることも可能。この辺は都度調べてやってみると良い。
##継承
これまで、UserとMicropostと言う二つのクラスが紹介されたが、この二つはApplicationRecordとは親子関係に当たる。この親子関係は、以下のように表す。
class User < ApplicationRecord
class Micropost < ApplicationRecord
さらにApplicationRecordはActiveRecord::Baseを継承している。
出典:図 2.18: UserモデルとMicropostモデルの継承階層
コントローラも同じような親子関係になっている。
class UsersController < ApplicationController
class MicropostsController < ApplicationController
出典:図 2.19: UsersコントローラとMicropostsコントローラにおける継承関係
だから何?って話だが、要は親クラスで定義した設定は子クラスでも使えるから、どの子クラスでも扱うような共通事項は親クラスにまとめた方が効率的ですよ、ということ。
この仕組みを「継承」と言う。
継承については第4章で詳解する。
##デプロイ
前回同様、リポジトリをBitbucketとherokuにpushする。
今回の注意点はherokuへのデプロイ。
toy_appを作成してからherokuをインストールしていないので、インストールする。
$ source <(curl -sL https://cdn.learnenough.com/heroku_install)
で、push後にデータベース起動のコマンドを打つ
$ heroku run rails db:migrate
これで、heroku open でアプリケーションをひらけばOK。
ちなみに、「なんでherokuインストールしていないのにpushできるねん?」
と疑問思うかも知れないが、herokuのリモートが登録されていればpushできる、ということを覚えておこう。
#単語集
- Usersリソースとは
データモデルとモデル表示の為のWebインターフェイスを組み合わせたもの。
UsersリソースによってユーザーをHTTPプロトコル(ポート番号80)経由で自由にCRUDできる「オブジェクト」とみなすことができるようになる。
- scaffold
自動でCRUDできて、なおかつコントローラー、モデル、ビュー、ルーティングの仕組みを用意できてしまうチート機能。
- CRUD
CREATE/READ/UPDATE/DELETE のまとまりと言えば分かるでしょう。
- パラメータ
メソッドや関数に定義される値のこと。
関数の定義の中で関数に渡す値はパラメータ、関数を呼び出すときに渡す値を引数、と考えると分かりやすい
- 主キー
idに定義されるキー値のトップ。階層構造で言う上の部分。
英語ではprimary keyと呼ぶ
主キーには他のレコードと同じ値になってはならない(ユニークな情報を持っている)ことや、
NULL(空の情報)を登録できないと言う性質がある。
主キーをリレーションシップで外部キーと繋げて、外部の表から情報を読み込んだりする。
- ビルド
ソースコードを書く所から実際のプログラムを作るまでの一連の工程のこと。
- HTTP requestメソッド
HTTPプロトコルでリクエストされるメソッドのこと。
GET
HEAD
POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
などがある。
これらはHTTPサーバーのレスポンスを処理する。
- REST
REpresentational State Transferの略。Webサービスを設計するときのモデルで、以下の原則がある。
- アドレス指定可能なURIで公開されていること
- インターフェース(HTTPメソッドの利用)の統一がされていること
- ステートレスであること
- 処理結果がHTTPステータスコードで通知されること
- バリデーション
フォームなどから送信される処理に何らかの制限を加える機能のこと。
具体的には、
①入力内容をチェック
②予め設定したルールに反してないか確認する
③true ルールに合致してれば許可 false ルールに反していれば拒否
このような流れで処理が行われる。
- lengthメソッド
文字列の長さを求めるメソッド。
使い方
ruby = "こんにちは" #rubyに文字列"こんにちは"を代入する。
ruby.length() #rubyに格納されている文字列の数を表示する
=> 5
- オブジェクト指向とは?
RubyやJavaの言語のそもそもの仕組み。いわゆる「物」を基準に考えることで、オブジェクト指向のプログラミング言語には以下のような特徴がある。
- オブジェクト指向(OOP)
- クラスやインスタンスの存在
- 継承
- カプセル化
- 多相性