LoginSignup
2
2

More than 3 years have passed since last update.

コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.3 - Scaffold, RESTful, MVC -

Last updated at Posted at 2020-02-06

はじめに

第三回目の今回は、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のシェルです。シェルというとbashzshなどが思い浮かぶと思いますが、alpine linuxではashです。

これでコンテナの中に入れたかと思います。今までローカルでコマンドを実行するときは$を頭につけていましたが、コンテナ内でコマンドを実行するときは#をつけるようにします。#がついている場合は、上のコマンドを実行してコンテナ内に入っていると思ってください。

何も言わずに以下のコマンドを実行してみましょう。

# rails generate scaffold user name:string email:string
# rails db:migrate

これだけですでに以下のようなアプリができています。 http://localhost:3000/users にアクセスしてみて確かめてみてください。↓のようなアプリが出来上がっているはずです。
image.png
色々と触ってみてください。たった2コマンドでこのアプリができちゃうの強すぎませんか?笑

ではまずコマンドの説明をしておきます。

rails generate scaffold model_name [field:type...field:type]

rails generate scaffoldはScaffoldでRailsアプリを作成するコマンドです。generategと省略できます。ちなみに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)を原則としています。リソースを中心に考えるとそれに対する操作とはいわゆるCRUDCreate/Read/Update/Delete)であるといえます。REST APIではHTTPメソッドのPOST/GET/PATCH(PUT)/DELETEをそれに対応させて定義しています。
RailsでもROAを原則としてアプリを設計していくことが理想です。

3. リソースを一意に識別する「汎用的な構文」

汎用的な構文...なんだこれもうわかりにくくないだろうか。
これは、例えばユーザーモデルであれば/usersのパスで操作ができる、商品モデルなら/purchasesのパスで操作できる、って感じです。

4. アプリケーションの情報と状態遷移の両方を扱うことができる「ハイパーメディアの使用」

もうなんともですね。笑
これはHTTPとか使いましょう。って話です。

まとめると以下のような図になると思ってます。
image.png
このように、「URL」で「リソース」を指定し「HTTPメソッド」で「操作(CRUD)」を指定するイメージですね。
大切な考え方として「リソース」が主軸にあるということを抑えましょう。

先ほどScaffoldで生成したアプリケーションをみてみてもUserモデルを作成して、参照して、更新して、削除する、アプリケーションでした。アプリケーションを考える上でも、このRESTfulを念頭におきながらアーキテクトするとRailsで超絶作りやすくなりますね。

MVC

MVCModelViewControllerの頭文字をとったもので、Webアプリケーションアーキテクチャの1つです。

  • Model: リソース、ロジック
  • View: 画面表示、表現
  • Controller: 入力の受け取りに合わせてModelViewを使う司令塔

Railsは以下のアーキテクチャになっています。
image.png
Controllerの前にRouterがいますが、MVCの形をとっていることがわかると思います。処理を順を追って紹介していきますね。

Routing

image.png
ユーザーからリクエストを受け取ると、RailsアプリケーションのRouterはそのHTTPメソッドとパスから定義に合わせてController(のAction)に処理を渡してくれます。

リソースを処理

image.png
リクエストを受け取ったControllerはデータの操作が必要な場合はModelに指示を出します。例えば、『ID=1のユーザーのモデルオブジェクトをくれ!』みたいなやつです。ModelはそれにしたがってDBを更新したりオブジェクトを検索してControllerに返してあげます。

画面をお返し

image.png
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に書きます。覗いてみましょー!

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にアクセスされた場合、UsersControllerindex 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のように複数形で名前づけされるルールになってますので気をつけてください!

app/controller/users_controller.rb
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をみてみましょうか。

app/controllers/users_controller.rb
...
def index
  @user = User.all
end
...

中身一行だけですね。
これは@userUser.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ファイルが生成されていますので中身を覗いてみましょう。

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

ApplicationRecordを継承しているので、特に何も書かなくてもDBにアクセスしてCRUDできるようになっています。
追加でバリデーションやロジック(デフォルト値や値の計算など)を書くことができます。(今後やっていきます!)

実際にDBに反映されているテーブルの情報はSchemaをみることで確認することができます。

db/schema.rb
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型のnameemail、Datetime型のcreated_atupdated_atusersテーブルに作られた感じがわかりますよね。

View

Viewのソースはapp/views/{controller_name}/ディレクトリに作成します。

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 %>

これはindexアクションが処理をさせるViewです。ほぼHTMLですね。
ファイルは.html.erb拡張子です。.erbはembedded Rubyの意で、Rubyのコードを埋め込めるHTMLファイルです。
Rubyコードの埋め込みの仕方には2種類あります。

  • <% code %>: このcodeは実行されますが、結果はレンダリングされません。
  • <%= code %>: このcodeは実行され、結果はレンダリングされます。

また、ブロック形式で記述することもできます!index.html.erbの中でも以下のような表現がありますね。

app/views/users/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

こいつは便利だ。ちなみにdestroydと省略することも可能。

しかし、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

Other Hands-on Links

2
2
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
2
2