はじめに
第三回目の今回は、Railsの便利機能を使って簡単なWebアプリケーションを作ってみようと思います。
また、Railsアプリでとても大切な『RESTful』や『MVC』について紹介していきます。
前回のソースコード
前回のソースコードはこちらに格納してます。今回のだけやりたい場合はこちらからダウンロードしてください。
ScaffoldでRailsアプリを作ってみる
ScaffoldはRailsの強力な機能の一つで、数コマンドでRails appを作成することができます。
一方で、数コマンドであまりにも多くのことがなされてしまうので勉強する上では頭が混乱してしまうかもしれません。
ただ、Railsアプリの基本がつまったものですので、一度触ってみましょう!
User管理アプリを作る
Scaffoldを使って、Userを管理するアプリを作ってみます。
まず、webコンテナを立ち上げて、コンテナ内に入って操作していきましょう。
$ docker-compose up -d
$ docker-compose exec web ash
docker-compose exec <service> <command>
は<service>
で<command>
を実行するコマンドです。ash
はalpine linuxのシェルです。シェルというとbash
やzsh
などが思い浮かぶと思いますが、alpine linuxではash
です。
これでコンテナの中に入れたかと思います。今までローカルでコマンドを実行するときは$
を頭につけていましたが、コンテナ内でコマンドを実行するときは#
をつけるようにします。#
がついている場合は、上のコマンドを実行してコンテナ内に入っていると思ってください。
何も言わずに以下のコマンドを実行してみましょう。
# rails generate scaffold user name:string email:string
# rails db:migrate
これだけですでに以下のようなアプリができています。 http://localhost:3000/users にアクセスしてみて確かめてみてください。↓のようなアプリが出来上がっているはずです。
色々と触ってみてください。たった2コマンドでこのアプリができちゃうの強すぎませんか?笑
ではまずコマンドの説明をしておきます。
rails generate scaffold model_name [field:type...field:type]
rails generate scaffold
はScaffoldでRailsアプリを作成するコマンドです。generate
はg
と省略できます。ちなみにScaffoldは『足場』と和訳されます。
Scaffoldは、1つのModelに対してRESTful(後述)なWebアプリケーションを作成します。model_name
にはそのModelの名前を入力します。慣習的にModelの名前は単数形を用います。
さらにfield
(属性)とtype
(型)を定義します。この組み合わせは複数定義できます。
このコマンドを叩くだけで、そのModelの生成だけでなくRESTfulなアプリケーションに必要な全てのファイルが生成されるわけです。
具体的にはこの後で説明していきます。
rails db:migrate
rails generate scaffold
だけで必要なファイルは生成されているのですが、Databaseへの反映(Tableの作成)がまだできていません。
rails db:migrate
はマイグレーションファイルと呼ばれるファイルの内容に沿ってDatabaseを操作するためのコマンドです。
先ほどのrails generate scaffold
で作成されたファイルの中にマイグレーションファイルも含まれています。マイグレーションファイルはrails generate scaffold
で定義したfield:type
に合わせて生成されています。(マイグレーションファイルは次回のハンズオンで触れます)
さて、Scaffoldで作成したRailsアプリを色々と触っていただいたかと思いますが、Railsアプリには重要な考え方やアーキテクチャとして『RESTful』と『MVC』があります。これらについて紹介しますね。
RESTful
RESTという言葉をご存知でしょうか?よくREST APIとか言われますよね。
RESTは『REpresentational State Transfer』の略で、『Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)』で言及されています。Wikipediaによると以下の設計原則を持っています。
- ステートレスなクライアント/サーバプロトコル
- すべての情報(リソース)に適用できる「よく定義された操作」のセット
- リソースを一意に識別する「汎用的な構文」
- アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」
少し表現が難しいっすね。これはあくまでAPIで用いられる設計原則なのですが、RailsはこのRESTの原則にそってシステムを作れるということでRESTful
と表現されますね。
1. ステートレスなクライアント/サーバプロトコル
基本的にステートレスです。サーバー側でクライアントとのステート(状態)を管理せず、同じリクエストであれば同じ結果がいつでも返ってくるように設計することでステート管理の煩わしさをなくしています。
2. すべての情報(リソース)に適用できる「よく定義された操作」のセット
リソースに操作が定義されている...
RESTではROA
(Resource Oriented Architecture)を原則としています。リソースを中心に考えるとそれに対する操作とはいわゆるCRUD
(Create/Read/Update/Delete
)であるといえます。REST APIではHTTPメソッドのPOST/GET/PATCH(PUT)/DELETE
をそれに対応させて定義しています。
RailsでもROAを原則としてアプリを設計していくことが理想です。
3. リソースを一意に識別する「汎用的な構文」
汎用的な構文...なんだこれもうわかりにくくないだろうか。
これは、例えばユーザーモデルであれば/users
のパスで操作ができる、商品モデルなら/purchases
のパスで操作できる、って感じです。
4. アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」
もうなんともですね。笑
これはHTTP
とか使いましょう。って話です。
まとめると以下のような図になると思ってます。
このように、「URL」で「リソース」を指定し「HTTPメソッド」で「操作(CRUD)」を指定するイメージですね。
大切な考え方として「リソース」が主軸にあるということを抑えましょう。
先ほどScaffoldで生成したアプリケーションをみてみてもUser
モデルを作成して、参照して、更新して、削除する、アプリケーションでした。アプリケーションを考える上でも、このRESTfulを念頭におきながらアーキテクトするとRailsで超絶作りやすくなりますね。
MVC
MVC
はModel
、View
、Controller
の頭文字をとったもので、Webアプリケーションアーキテクチャの1つです。
-
Model
: リソース、ロジック -
View
: 画面表示、表現 -
Controller
: 入力の受け取りに合わせてModel
とView
を使う司令塔
Railsは以下のアーキテクチャになっています。
Controller
の前にRouter
がいますが、MVC
の形をとっていることがわかると思います。処理を順を追って紹介していきますね。
Routing
ユーザーからリクエストを受け取ると、RailsアプリケーションのRouterはそのHTTPメソッドとパスから定義に合わせてController(のAction)に処理を渡してくれます。
リソースを処理
リクエストを受け取ったControllerはデータの操作が必要な場合はModelに指示を出します。例えば、『ID=1のユーザーのモデルオブジェクトをくれ!』みたいなやつです。ModelはそれにしたがってDBを更新したりオブジェクトを検索してControllerに返してあげます。
画面をお返し
Controllerはその後、Viewに画面をレンダリングするように指示を出します。この時、先ほどゲットしたModelの情報などもViewに渡し、Viewはその情報も元にして画面を整形してControllerに返却します。
ControllerはユーザーにViewを渡してあげて、ユーザーのブラウザがそれを表示してくれます。
これらがMVCの、そしてRailsの一連の処理の流れになります。こうやってみるととてもシンプルですよね。MVCがそれぞれ独立したことにより、コードの管理も簡単だし、どこに何をやらせればいいのかもシンプルに考えられるようになると思います。
RailsのMVCをScaffoldで確認する
RailsのScaffoldはRailsを体現したようなやつなんですよ。なんで当然MVCなわけですね。
てことでScaffoldのソースコードを少し覗きながらMVCを感じてみましょう!
Router
Router
の設定はconfig/routes.rb
に書きます。覗いてみましょー!
Rails.application.routes.draw do
resources :users
end
え、超シンプルやん。はい、resouces [controller_name]
でそのControllerに対してRailsアプリケーションで標準的に必要とされるルーティングを全て生成してくれちゃう便利なメソッドです。実際に以下のルーティングを生成してくれちゃいます。
HTTP method | Path | Routing | Route name |
---|---|---|---|
GET | /users | users#index | users |
POST | /users | users#create | users |
GET | /users/new | users#new | new_user |
GET | /users/:id/edit | users#edit | edit_user |
GET | /users/:id | users#show | user |
PATCH | /users/:id | users#update | user |
PUT | /users/:id | users#update | user |
DELETE | /users/:id | users#destroy | user |
見方ですが、例えば1行目であれば
『GET
メソッドで/users
にアクセスされた場合、UsersController
のindex action
にルーティングする。Rails内ではusers_path
って名前で定義される』
って感じです。「名前で定義される」ってなんぞ?となると思いますが、この後でてきます。例えばリンク(<a>
)を作る時にRailsではlink_to
ヘルパーが使えるのですがその遷移先としてuser_path
と指定すれば/users
と解釈してくれるんです。これ結構便利なんですよねー。
RailsのRouting設定はコマンドラインから確認することができるっす。
# rails routes
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
...
上の表とおんなじ感じになっていることがわかりますね。
Controller
さて、RouterによってControllerのActionにリクエストがルーティングされましたんで、Controllerの中身を覗いてみましょう!
Controllerのソースコードはapp/controller/
配下にまとまっています。今回はuser
モデルを作ったのでusers_controller.rb
ファイルが生成されていますね。Controllerはその中に複数のactionがあることからusers
のように複数形で名前づけされるルールになってますので気をつけてください!
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
@user = User.all
end
def show
end
def new
@user = User.new
end
def edit
end
def create
@user = User.new(user_params)
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: @user }
else
format.html { render :new }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit }
format.json { render json: @user.errors, status: unprocessable_entity }
end
end
end
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_user
@user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :email)
end
end
ながいっすね。重要なところだけかいつまんで説明します。
まず、def xxx ... end
となってるブロックがいっぱいありますね。def
はRubyにおける関数定義です。で、private
より上にある一つ一つがActionと呼ばれているやつらです。ちょうど先ほどRouterで定義されていたアクションが全てあるのがわかりますね。
その中で1つ、index actionをみてみましょうか。
...
def index
@user = User.all
end
...
中身一行だけですね。
これは@user
にUser.all
の結果を代入してますね。User.all
はまた今度詳しく説明予定ですが、Userモデルのデータを全てリスト形式で取得するメソッドです。なのでUserモデルを全件検索したものを@user
に代入しているんですね。
ここで気になる人がいると思うのですが、@
ってなんやねんと。これは、インスタンス変数と呼ばれるものになります。対して@
がつかない変数はローカル変数と呼びます。
違いは変数の適用範囲です。簡単にいうとローカル変数はViewに引き渡すことができないのです。対してインスタンス変数はViewに引き渡すことができるのです。MVCの考え方からControllerはViewにModelの情報を引き渡してユーザーに描写する画面を作るので、インスタンス変数を使う必要があるのです。
さて、ControllerがModelからデータを受領しているのはここまででなんとかわかると思いますが、Viewの呼び出しはどうやっているのか...
RailsではViewを呼び出すメソッドとしてrender
があります。users_controller.rb
の中にもrender
と書かれているところがありますがないところもあります。これはどういうことか。
実はRailsでは各Actionの最後に暗にActionと同じ名前のViewにrender
をしています。例えばindex
アクションの場合は、処理の最後にrender :index
というようなことがなされており、これによってapp/views/users/index.html.erb
が呼び出されるようになっています。
Model
Modelのソースはapp/models/
に作ります。Scaffoldのおかげでuser.rb
ファイルが生成されていますので中身を覗いてみましょう。
class User < ApplicationRecord
end
ApplicationRecordを継承しているので、特に何も書かなくてもDBにアクセスしてCRUDできるようになっています。
追加でバリデーションやロジック(デフォルト値や値の計算など)を書くことができます。(今後やっていきます!)
実際にDBに反映されているテーブルの情報はSchemaをみることで確認することができます。
ActiveRecord::Schema.define(version: 2020_01_16_115651) do
enable_extention "plpgsql"
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
String型のname
、email
、Datetime型のcreated_at
、updated_at
がusers
テーブルに作られた感じがわかりますよね。
View
Viewのソースはapp/views/{controller_name}/
ディレクトリに作成します。
<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 %>
これはindex
アクションが処理をさせるViewです。ほぼHTMLですね。
ファイルは.html.erb
拡張子です。.erb
はembedded Rubyの意で、Rubyのコードを埋め込めるHTMLファイルです。
Rubyコードの埋め込みの仕方には2種類あります。
- <% code %>: このcodeは実行されますが、結果はレンダリングされません。
- <%= code %>: このcodeは実行され、結果はレンダリングされます。
また、ブロック形式で記述することもできます!index.html.erb
の中でも以下のような表現がありますね。
...
<% @users.each do |user| %>
...
<% end %>
...
この表現は、@users
(これはControllerでUser.all
が代入されていたやつです。)の配列の中から1つずつ取り出しuser
という変数として、end
までの処理をまわすという表現です。
はい。ということでScaffoldで作成されたファイルの中身をみながら「へー。RailsはMVCでRESTだなー。」となっていただけましたかね。
と、いうことでせっかく作ったScaffoldですが、便利すぎてお勉強には不向きなところもあるのでリセットして、次回からは1からアプリ開発をしていきたいと思います。
後片付け
rails generate
で作成したファイルや追加されたコードのほとんどはrails destroy
でもとにもどせます。
# rails destroy scaffold user
こいつは便利だ。ちなみにdestroy
はd
と省略することも可能。
しかし、scaffolds.scss
だけは消えないのでこれは個別に消してあげて!
# rm app/assets/stylesheets/scaffolds.scss
また、DBにはまだテーブルが存在してる状態です。今回は開発環境ですし、データが消えても問題ないのでDBを再作成してclean upしましょう。
# exit
$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset
rails db:migrate:reset
はDBを削除(db:drop
)してまた作り(db:create
)、マイグレーションを適用する(db:migrate
)を一度に実行してくれるコマンドです。最初にDBを削除してしまうので本番環境やテストデータを残しておきたい時は絶対に使ってはいけませんよ。
最後にもう一度立ち上がったdbコンテナを落として今日のハンズオンは終了です。
$ docker-compose down
まとめ
今回はScaffoldというRailsのアーキテクチャが詰まったアプリを作る機能を紹介しました。
その中でMVCやRESTといったRailsの開発思想が学べましたね。この辺りはアプリのシンプルさを保つためにもぜひものにしておきましょう。
次回からは、何回かに分けてTwitterのようなアプリを1からつくってこうと思います。最初は静的なトップページ。
Bootstrap(CSSフレームワーク)を使っていい感じにデザインしてきますよ!
では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
Next: コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.4 - Static pages - - Qiita
本日のソースコード
Reference
- Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
- Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)
- Webサービス開発で知っておきたいRESTfulの意味とは? - WPJ
Other Hands-on Links