みなさんRuby on Railsの公式チュートリアルって見たことありますか?
物凄くボリュームがあり、かつ内容難しいですよね?
「あれ?チュートリアルって初心者向けじゃないの?」
とツッコミを入れたくなるレベルです。
かく言う私もチュートリアル挫折組ですので偉そうな事言えませんが、
おそらく初学者がチュートリアルに手を出すのは危険です。
120%挫折すると思ってます!
ということで、今回簡単なTODOアプリを作りながら、初学者向けRailsチュートリアルを自作しました!
では早速やっていきましょう!
※Ruby、Railsはインストール済みであることを前提とします。
1. プロジェクトを作成
ではRailsのプロジェクトを作成したいディレクトリに移動して下記コマンドを実行します。
$ rails new "プロエジェクト名"
作成が終わったら作成したディレクトリに移動します。
これ大事です。忘れるとエラーを連発して混乱します。(経験談)
$ cd "プロエジェクト名"
また、こちらのディレクトリをVSCode等のエディターで開いておきましょう。
2. MVCモデル
Railsに限らず、フレームワークを学ぶ上で出てくるMVCモデルの理解が大切です。
プロジェクトディレクトリの「app」ディレクトリの中に「models」「views」「controllers」というディレクトリがあるはずです。
これがMVCモデルを司るディレクトリです。
軽くMVCについて説明しておきます。詳細は割愛するので少し解釈が異なっているように感じるかもしれませんが、大体こういう事だと思って下さい。
Model
「モデル」は、データベースのやり取りだったり、スキーマを担う部分です。
今回作成するTODOアプリでいうと、TODOをデータベースに登録したり、削除したりする機能や、登録するデータの型設定を担います。
ちなみに今回、モデルは作成したあとはあまり意識することはありません。
View
「ビュー」は、その名の通り、画面描写を担う部分です。
今回作成するTODOアプリでいうと、入力欄や作成ボタン、削除ボタン等を描写する機能を担います。
Controller
「コントローラー」は、指示役であり橋渡し役です。
ユーザーから受け取ったリクエストから「モデル」に指示を出し、「モデル」から受け取ったデータを「ビュー」に伝えます。
また、「ビュー」が作成した画面をユーザーに返す役割も担います。つまり縁の下の力持ちです。
今回一番コードを書く部分になります。
3. モデルを作ろう
では早速モデルを作ってみましょう!
$ rails generate model Todos title:string
ちなみに「generate」は「g」というふう省略できるので、今後「g」を使います。
これを実行すると、「models」ディレクトリの中に「todo.rb」というファイルが出来ているはずです。
そして中身はほとんど空の状態です。
class Todo < ApplicationRecord
end
また、「app」ディレクトリと同じ階層に「db」ディレクトリがあると思いますが、「db」ディレクトリの中の「migrate」ディレクトリにも新しくファイルが作成されていると思います。「日時_create_todos.rb」という形式になります。
中身はこんな感じです。
class CreateTodos < ActiveRecord::Migration[7.0]
def change
create_table :todos do |t|
t.string :title
t.timestamps
end
end
end
これはマイグレーションファイルと言って、今回の内容は、「これからこういうテーブルを作りますよ」というものです。
具体的に言うと、「todosというテーブルを追加します。そのテーブルはstring型のtitleと、作成日時、更新日時をカラムとして持ちます」という感じです。
ちなみにマイグレーションファイルは一度作成すると修正ができません。手動で修正するとエラーの原因になるので、内容の変更や追加、削除はまた新たにマイグレーションファイルを作成して行いますが、ここでは説明を控えます。
4. マイグレーションファイルを実行してテーブルを作成する
では先程作成したマイグレーションファイルを用いてテーブルを作成します。
$ rails db:migrate
すると「db」ディレクトリに「schema.rb」というファイルが作成されます。
ここでは中身の説明は割愛しますが、これで無事にデータを格納するテーブルが出来上がりました。
次にルーティング設定を行いましょう!
5. ルーティングを設定する
続いてルーティングを設定していきます。
「app」ディレクトリと同じ階層にある「config」ディレクトリの中の「routes.rb」ファイルを開きます。
おそらくデフォルトでは下記のようになっていると思いますので、コメントアウトの部分は削除してください。
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
# root "articles#index"
end
そして下記のように記述して下さい。
Rails.application.routes.draw do
resources :todos
end
これはすべてのルートを作成します。
ルートは全部で7つあります。以下、それぞれの一般的な役割です。
- index(一覧表示)
- show(詳細表示)
- new(新規作成フォームの表示)
- create(新規作成フォームから受け取ったデータをデータベースに保存)
- edit(編集フォームの表示)
- update(編集フォームから受け取ったデータでデータベースを更新)
- destroy(削除)
そして今回使用するのは「index,new,create,edit,update,destroy」です。
つまり「show」以外全てなのですが、実はルートを限定的に指定する方法があるので、今回はその方法でルーティング設定を行います。
下記のようにコードを書き換えて下さい。
Rails.application.routes.draw do
resources :todos, only: [:index, :new, :create, :edit, :update, :destroy]
end
では次はコントローラーを作っていきます!
6. コントローラーを作ろう
まずは下記コマンドを実行します。
$ rails g controller Todos
すると「app」ディレクトリ配下の「controllers」ディレクトリの中に「todos_controller.rb」ファイルが作成されていると思います。
まだ中身は空なので、こちらに次のように記述してください。
class TodosController < ApplicationController
def index
@todos = Todo.all
end
def new
@todo = Todo.new
end
def create
@todo = Todo.new(todo_params)
if @todo.save
redirect_to todos_path
else
render :new, status: :unprocessable_entity
end
end
def edit
@todo = Todo.find_by(id: params[:id])
end
def update
@todo = Todo.find_by(id: params[:id])
if @todo.update(todo_params)
redirect_to todos_path
else
render :edit
end
end
def destroy
@todo = Todo.find(params[:id])
@todo.destroy
redirect_to todos_path
end
private
def todo_params
params.require(:todo).permit(:title)
end
end
さて、少し長丁場になりますが説明していきますね。
まずはindexの部分ですが、こちらは前に説明した通り、一覧の表示です。
def index
@todos = Todo.all
end
このコードは、データベースからすべてのTodoレコードを取得してインスタンス変数に格納しています。
インスタンス変数は後ほどビューで使用します。
続いてnewですが、これはユーザーからの入力を受け取るための空のインスタンスを作成して、インスタンス変数に格納します。
def new
@todo = Todo.new
end
このインスタンス変数は先程と同様、ビューで使用します。
createは、indexやnewとは少し違う働きをします。
def create
@todo = Todo.new(todo_params)
if @todo.save
redirect_to todos_path
else
render :new, status: :unprocessable_entity
end
end
newと同じくTodo.newでインスタンスを作成していますが、こちらではユーザーがフォームで入力したデータを受け取り、それを変数に格納します。
そして「@todo.save」でデータベースに保存し、成功したら一覧ページに遷移し、失敗したらもう一度入力画面に戻ります。
ちなみに、Todo.newが引数で受け取っている「todo_params」については後述します。
editはnewと同じようにインスタンスを用意していますが、こちらは新しく空のインスタンスを作成しているのではなく、URLから受け取ったパラメーターに含まれるidから、同じidのインスタンスをデータベースから検索し、変数に格納しています。
def edit
@todo = Todo.find_by(id: params[:id])
end
これにより特定のデータを取得することができます。
updateはcreateと似ています。
def update
@todo = Todo.find_by(id: params[:id])
if @todo.update(todo_params)
redirect_to todos_path
else
render :edit
end
end
違いは、createはユーザーが入力したデータをもとに新しくインスタンスを作成してデータベースに登録しているのに対し、updateはユーザーが入力したデータをもとに特定インスタンスのデータを変更し、データベースを更新しています。
destoryは削除ですね。ほとんど説明不要かと思いますが、editやupdateと同様特定のインスタスを取得して削除しています。
def destroy
@todo = Todo.find(params[:id])
@todo.destroy
redirect_to todos_path
end
ここでは失敗時の挙動を書いていませんが、update同様失敗時の挙動を書いても良いかもしれません。
最後に「todo_params」ですが、名前は正直なんでも良いです。
def todo_params
params.require(:todo).permit(:title)
end
これはtodoインスタンスに含まれるべきプロパティは「title」だけであることを示します。
つまり意図しないデータを受け取ってしまわないように、受け取るデータを指定しています。
もちろん下記のように直書きもできますが、これだとフォームからデータを受け取れないので、汎用性を持たせるためにメソッドにしています。
def create
def create
@todo = Todo.create(title: "登録したいタイトルが入る")
if @todo.persisted?
redirect_to todos_path
else
render :new, status: :unprocessable_entity
end
end
end
7. ビューを作ろう!
さあ次は画面描写です。HTMLと少し似ていますが、rubyが入るので若干書き方が異なります。
ただ、ここではあまり深く説明はしません。下記をそのままコピペして使って下さい。
1. 「views」ディレクトリ配下の「todos」配下に「index.html.rb」を作成
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TODOアプリ</title>
</head>
<body>
<div class="container">
<h1>TODO DEMO</h1>
<div class="new-todo">
<a href="/todos/new">TODOを追加する</a>
</div>
<table>
<thead>
<tr>
<th>TODO</th>
<th>アクション</th>
</tr>
</thead>
<tbody>
<% @todos.each do |todo| %>
<tr>
<td><%= todo.id %>. <%= todo.title %></td>
<td>
<%= link_to "編集", edit_todo_path(todo), class:"edit" %>
<%= button_to "削除", todo_path(todo), method: :delete, class: "delete" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</body>
</html>
2. 「views」ディレクトリ配下の「todos」配下に「new.html.rb」を作成
<h1>TODOの追加</h1>
<div class="container">
<% if @todo.errors.any? %>
<div>
<ul>
<% @todo.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_with model: @todo, html:{class: "inner"} do |form| %>
<div class="field flex">
<%= form.label :title, 'TODO' %>
<%= form.text_field :title %>
</div>
<div class="submit">
<%= form.submit '追加' %>
</div>
<% end%>
</div>
3. 「views」ディレクトリ配下の「todos」配下に「edit.html.rb」を作成
<h1>TODOの編集</h1>
<div class="container">
<%= form_with model: @todo, html:{class: "inner"} do |form| %>
<div class="field flex">
<%= form.label :title, 'TODO' %>
<%= form.text_field :title %>
</div>
<div class="update">
<%= form.submit '更新' %>
</div>
<% end%>
</div>
以上でビューの部分完成です!
次はスタイルをあてていきます!
8. スタイルをあてよう!
こちらも適当にスタイルをあてたのでそのままコピペして使って下さい。
「app」ディレクトリ配下の「assets」配下の「stylesheets」配下の「application.css」ファイルを下記のように修正します。
既存のコメントアウト部分は消しちゃって大丈夫です!
body {
background-color: #fcfcfc;
padding: 20px;
}
.container {
max-width: 600px;
margin: 50px auto;
}
h1 {
text-align: center;
}
form {
display: flex;
margin-bottom: 20px;
}
form input[type='text'] {
flex: 1;
padding: 10px;
margin-right: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
form input[type='submit'] {
width: 150px;
padding: 10px 20px;
border: none;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
table {
width: 100%;
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid #ddd;
}
th,
td {
padding: 10px;
}
th {
text-align: center;
background-color: #ccc;
}
th:first-of-type {
width: 70%;
}
tbody td {
font-size: 18px;
}
tbody td:last-of-type {
display: grid;
grid-template-columns: repeat(2, 1fr);
border: none;
}
.button_to {
margin: 0;
padding: 0 !important;
}
.edit,
.button_to,
.delete {
display: inline-block;
padding: 10px;
border: none;
border-radius: 4px;
color: #fff;
text-decoration: none;
margin-right: 5px;
font-size: 16px;
}
.edit {
background-color: #ffc107;
display: flex;
justify-content: center;
align-items: center;
}
.delete {
width: 100%;
background-color: #dc3545;
cursor: pointer;
}
.new-todo {
text-align: center;
}
.new-todo a {
display: inline-block;
margin-bottom: 20px;
padding: 10px 20px;
border: none;
border-radius: 4px;
color: #fff;
background-color: #007bff;
text-decoration: none;
}
.inner {
justify-content: center;
align-items: center;
}
.flex {
display: flex;
align-items: center;
flex-wrap: nowrap;
height: 100%;
gap: 4px;
}
.field label {
font-size: 20px;
font-weight: bold;
}
.submit input {
background-color: #007bff;
}
.update input {
background-color: #ffc107;
}
tr:nth-of-type(n + 2) {
border-top: 1px solid #ddd;
}
急造なのでとても適当ですが許してください。。。
9. 実際にローカルサーバーを起動して挙動を確かめよう!
さあいよいよアプリケーションを動かしてみます!
ちなみに、実際に自分で作る時は一つずつ確認しながら作成した方が無難です!
では下記コマンドを実行してローカルサーバーを立ち上げます。
$ rails server
または下記でもOKです。
$ rails s
起動に成功したら、ブラウザで「 http://localhost:3000/todos 」にアクセスします。
どうでしょうか?画面は表示されましたか?
ここで「TODOを追加する」ボタンをクリックすると「 http://localhost:3000/todos/new 」に遷移します。
TODOを入力して「追加」ボタンをクリックすると元の画面に戻って追加したTODOが表示されているはずです。
また、「編集」ボタンをクリックすれば「 http://localhost:3000/todos/[id]/edit 」に遷移します。
そして「削除」ボタンを押せば当然削除されます。
もし上手く行かない場合はルーティングかコントローラーの設定が誤っている可能性が高いので、もう一度確認してみて下さい。
10. まとめ
とても長丁場になってしまいましたが、公式チュートリアルに比べれば1000分の1くらいです。
ちょっとRails使ってみたいなという人にはこれくらいがちょうど良いと思います。
ここから更に、バリデーションだったり、認証だったり、、、と興味があれば調べて機能を追加してみて下さい!
ここまで読んで頂き、ありがとうございました!