Ruby
Rails

Railsでタスク管理ができるWebアプリを作成してみた(Rails入門)

はじめに

railsを一通り学んだので、自分の課題として簡単なタスク管理ができるWebアプリケーションを作成しましたので、紹介して行きます。

ちなみに、Webアプリケーションとは、HTTPリクエストの中の4つのメソッドを満たす、その名もCRUDメソッド(GET, POST, PUT, DELETE)を使って、Web上のリソースを操作できるアプリケーションのことです。今回作成するWebアプリケーションもその定義通りのものを作成します。

環境

  • Ruby 2.4.1
  • Rails 5.2.0

プロジェクトの作成

プロジェクト名はtasklistとします。

rails new tasklist --database=mysql --skip-test

コマンドと、コマンドのオプションの説明

  • rails new プロジェクト名:プロジェクトを作成することができます
  • --database=mysql:使用するデータベースをmysqlに指定
  • --skip-test:自動テスト機能を使用しない

RailsでWebアプリを作る際の流れ

  • 1.モデル(Model)の作成
  • 2.ルーティングの設定
  • 3.ルーティングの設定ごとにControllerViewの作成

モデルの作成

RailsとDB(今回はMySQL)の接続を行います。

データベースの作成

Railsが提供しているコマンドでデータベースを作成して行きます。

$ rails db:create
Created database 'tasklist_development'
Created database 'tasklist_test'
mysql> show databases;
+---------------------------+
| Database                  |
+---------------------------+
| tasklist_development      |
| tasklist_test             |
+---------------------------+

(結果を一部省略しています)

tasklist_testも作成されますが、こちらは使用しません。

ここでrails sでサーバ起動したら立ち上がっていること確認できます。

参考までに、データベースに関する接続設定を管理しているファイルはconfig/database.ymlです。
.ymlの拡張子のファイルは、YAML形式の設定ファイルで、キー: 値という形の羅列になっています。
書き方が楽なので、設定ファイルとしてよく使われています。インデントで階層構造を表現するそうですpugみたいだね。

モデルの作成

下記コマンドで、Taskモデルを作成します。Taskモデルはcontentというメッセージ内容(文字列)を持つようにします。

$ rails g model Task content:string

モデルをgenerateしたらマイグレーションファイルというテーブルを管理するファイルが作成されます。

20180606073308_create_tasks.rb
class CreateTasks < ActiveRecord::Migration[5.2]
  def change
    create_table :tasks do |t|
      t.string :content

      t.timestamps
    end
  end
end

ファイルの中身ですが、これはtasksというテーブルを作成する命令が書かれているマイグレーションファイルです。

このファイルの中身を実行して、テーブルを作成してみましょう。

マイグレーションの実行

$ rails db:migrate
== 20180606073308 CreateTasks: migrating ======================================
-- create_table(:tasks)
   -> 0.0052s
== 20180606073308 CreateTasks: migrated (0.0060s) =============================

マイグレーションを実行したので、tasklist_developmentデータベースにtasksテーブルが作成されました。

スクリーンショット 2018-06-06 16.54.11.png

mysql> describe tasks;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| content    | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

指定したのは、contentカラムだけでした。主キーであるid、あと、作成時間を記録するcreated_atと、更新時間を記録するupdated_atは自動で作成されると分かりますね。

rails consoleを使ってレコードを3件追加する

後々、view側で確認するために、rails consoleでレコードを3件追加します。

$ rails c

コンソール内で

  • メソッドnewを使って、モデルのインスタンスを作成
  • メソッドsaveを使って、モデルのインスタンスをDBに保存

を、します。

2.4.1 :004 > task = Task.new(content: 'task01')
 => #<Task id: nil, content: "task01", created_at: nil, updated_at: nil> 
2.4.1 :005 > task.save
   (0.3ms)  BEGIN
  Task Create (0.2ms)  INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task01', '2018-06-06 08:03:49', '2018-06-06 08:03:49')
   (1.8ms)  COMMIT
 => true 
2.4.1 :006 > task = Task.new(content: 'task02')
 => #<Task id: nil, content: "task02", created_at: nil, updated_at: nil> 
2.4.1 :007 > task.save
   (0.1ms)  BEGIN
  Task Create (0.4ms)  INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task02', '2018-06-06 08:03:56', '2018-06-06 08:03:56')
   (1.9ms)  COMMIT
 => true 
2.4.1 :008 > task = Task.new(content: 'task03')
 => #<Task id: nil, content: "task03", created_at: nil, updated_at: nil> 
2.4.1 :009 > task.save
   (0.1ms)  BEGIN
  Task Create (0.2ms)  INSERT INTO `tasks` (`content`, `created_at`, `updated_at`) VALUES ('task03', '2018-06-06 08:04:01', '2018-06-06 08:04:01')
   (1.7ms)  COMMIT
 => true 

SQL内で挿入されたデータを確認しましょう。

mysql> select * from tasks;
+----+---------+---------------------+---------------------+
| id | content | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | task01  | 2018-06-06 08:03:49 | 2018-06-06 08:03:49 |
|  2 | task02  | 2018-06-06 08:03:56 | 2018-06-06 08:03:56 |
|  3 | task03  | 2018-06-06 08:04:01 | 2018-06-06 08:04:01 |
+----+---------+---------------------+---------------------+

これでモデルのインスタンス(レコード挿入)の作成を確認できました。タスク管理アプリの作るのに必要なモデルの作成はこれで完了です。

これから、Router と Controller と Viewを一気に作って行きますが、それらの関係を見てみましょう。

Router と Controller と View の関係を簡単に

  • Routerは、URLのルーティングを一元管理してる
  • Controllerは、HTTPリクエストの処理を担っている
  • Viewは、HTTPレスポンスとして返されるWebページ

ルーティングの設定

CRUDのための基本の7つのルーティングを省略形で書きます。

routes.rb
Rails.application.routes.draw do
  root to: 'tasks#index'

  resources :tasks
end

root to: 'tasks#index'は、/(TOPページ)と/tasksにアクセスしたときのルーティングです。

resources :tasksは、CRUDのための基本の7つのルーティングを省略形で書いたやつです。

現状のルーティングを確認して見ましょう。

$ rails routes
                   Prefix Verb   URI Pattern                                                                              Controller#Action
                     root GET    /                                                                                        tasks#index
                    tasks GET    /tasks(.:format)                                                                         tasks#index
                          POST   /tasks(.:format)                                                                         tasks#create
                 new_task GET    /tasks/new(.:format)                                                                     tasks#new
                edit_task GET    /tasks/:id/edit(.:format)                                                                tasks#edit
                     task GET    /tasks/:id(.:format)                                                                     tasks#show
                          PATCH  /tasks/:id(.:format)                                                                     tasks#update
                          PUT    /tasks/:id(.:format)                                                                     tasks#update
                          DELETE /tasks/:id(.:format)                                                                     tasks#destroy

(一部省略)

root to: 'tasks#index'resources :tasksが展開されていると確認できます。

このresources :tasksで展開された7つのルーティングのことをRESTfulなルーティングと呼びます。RESTfulなルーティングは、リソースのCRUD操作のために必要な7つのアクション(index, show, new, create, edit, update, destroy)です。

Controller と View を一気に作成

Controller

以下のコマンドで、Controllerを生成して行きます。

$ rails g controller Tasks --no-helper --no-assets
      create  app/controllers/tasks_controller.rb
      invoke  erb
      create    app/views/tasks

コントローラ名はモデルの複数形で書きます。Taskモデルに対するコントローラ名はTasksと書きます。

コマンドと、コマンドのオプションの説明

  • --no-helper:Helperを自動生成しないオプションです
  • --no-assets:CSS と JavaScript を自動生成しないオプションです

Controller を RESTful なルーティングに対応させる

ルーティングに対応したアクションを コントローラーに書いて行きましょう。もう一度、現状のルーティングを確認して見ましょう。

$ rails routes
                   Prefix Verb   URI Pattern                                                                              Controller#Action
                     root GET    /                                                                                        tasks#index
                    tasks GET    /tasks(.:format)                                                                         tasks#index
                          POST   /tasks(.:format)                                                                         tasks#create
                 new_task GET    /tasks/new(.:format)                                                                     tasks#new
                edit_task GET    /tasks/:id/edit(.:format)                                                                tasks#edit
                     task GET    /tasks/:id(.:format)                                                                     tasks#show
                          PATCH  /tasks/:id(.:format)                                                                     tasks#update
                          PUT    /tasks/:id(.:format)                                                                     tasks#update
                          DELETE /tasks/:id(.:format)                                                                     tasks#destroy

(一部省略)

コントローラー内でルーティングと同じ名前のメソッド名として定義すれば対応させられます。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end

  def create
  end

  def edit
  end

  def update
  end

  def destroy
  end
end

View

Viewの構成

Viewファイルを作成していきます。ルーティングの中で必要なViewファイルはGETメソッドで指定されたルーティングのみです。(理由:GETメソッド以外はページの表示ではなくリソースの操作だから)

そこで、もう一度、現状のルーティングを確認して見ましょう。

$ rails routes
Prefix Verb   URI Pattern                                                                              Controller#Action
                     root GET    /                                                                                        tasks#index
                    tasks GET    /tasks(.:format)                                                                         tasks#index
                          POST   /tasks(.:format)                                                                         tasks#create
                 new_task GET    /tasks/new(.:format)                                                                     tasks#new
                edit_task GET    /tasks/:id/edit(.:format)                                                                tasks#edit
                     task GET    /tasks/:id(.:format)                                                                     tasks#show
                          PATCH  /tasks/:id(.:format)                                                                     tasks#update
                          PUT    /tasks/:id(.:format)                                                                     tasks#update
                          DELETE /tasks/:id(.:format)                                                                     tasks#destroy

(一部省略)

GETメソッドのみがViewファイル作成の対象なので、 GETメソッドのルーティングであるindex,show,new,editに対応するViewファイルをを作成して行きます。具体的なファイルの場所と名前は下記の通りです。

  • app/views/tasks/index.html.erb
  • app/views/tasks/show.html.erb
  • app/views/tasks/new.html.erb
  • app/views/tasks/edit.html.erb

Viewファイルはrails generateコマンドは無いので、手作業で作成して行きます。

共通部分

Viewファイルのパスはapp/views/laytous/application.html.erbです。

この中の、<%= yield %>の部分が、コントローラのアクションに関連したViewファイル毎に変わります。

tasks#index

Controller

インデックスページは、タスクを一覧で全て取得したいからコントローラは下記のようになる

tasks_controller.rb
def index
  @tasks = Task.all
end

View

index.html.erb
<h1>メッセージ一覧</h1>

<ul>
  <% @tasks.each do |task| %>
  <li><%= task.content %></li>
  <%  end %>
</ul>

確認

現在のtasksテーブルの全レコードはこんな感じ

mysql> select * from tasks;
+----+---------+---------------------+---------------------+
| id | content | created_at          | updated_at          |
+----+---------+---------------------+---------------------+
|  1 | task01  | 2018-06-06 08:03:49 | 2018-06-06 08:03:49 |
|  2 | task02  | 2018-06-06 08:03:56 | 2018-06-06 08:03:56 |
|  3 | task03  | 2018-06-06 08:04:01 | 2018-06-06 08:04:01 |
+----+---------+---------------------+---------------------+
3 rows in set (0.00 sec)

こんな感じに出力されていればOK

スクリーンショット 2018-06-06 21.49.08.png

OK ですね。次は、詳細ページ行きましょう。

tasks#show

こちらは、各タスクの詳細ページです。

Controller

コントローラは、ここの詳細ページのidが取得できればokなので

tasks_controller.rb
def show
  @task = Task.find(params[:id])
end

View

show.html.erb
<h1>id:<%= @task.id %>のタスク詳細</h1>

<p><%= @task.content %></p>

確認

今のとこ~/tasks/1~/tasks/2~/tasks/3でアクセスしたら詳細ページは見れると思います。

index から show へのリンクを追加する

URL直打ちもあれなので、indexページのタスクのidにリンクさせましょう。

index.html.erb
<h1>メッセージ一覧</h1>

<ul>
  <% @tasks.each do |task| %>
    <li><%= link_to task.id, task %>:<%= task.content %></li>
  <% end %>
</ul>

link_toはRailsが提供するメソッドです。

link_to 表示文字列, リンク先 という形式で書きます。

表示文字列はidなので、task.idです。

リンク先は、taskになっています。リンク先として、taskのようにモデルのインスタンス(レコード)を渡すと、自動的にそのインスタンスのshowアクションへとリンクされます。

スクリーンショット 2018-06-06 23.11.24.png

リンク生成のためのメソッド

もう一度、現状のルーティングを確認して見ましょう。

$ rails routes
                   Prefix Verb   URI Pattern                                                                              Controller#Action
                     root GET    /                                                                                        tasks#index
                    tasks GET    /tasks(.:format)                                                                         tasks#index
                          POST   /tasks(.:format)                                                                         tasks#create
                 new_task GET    /tasks/new(.:format)                                                                     tasks#new
                edit_task GET    /tasks/:id/edit(.:format)                                                                tasks#edit
                     task GET    /tasks/:id(.:format)                                                                     tasks#show
                          PATCH  /tasks/:id(.:format)                                                                     tasks#update
                          PUT    /tasks/:id(.:format)                                                                     tasks#update
                          DELETE /tasks/:id(.:format)                                                                     tasks#destroy

(一部省略)

Prefixnew_taskedit_taskがあります。Prefixがあるのは全てGETメソッドに対してのみです。ルーティングを設定すると、自動的にリンク生成のためのメソッドも定義されます。上記のようなルーティングが生成された場合には、下記の表のようにメソッドが自動的に生成されます。

ルーティング リンク生成のメソッドの例 生成されたリンク
index tasks_path /task
show tasks_path(@task) /task/1 など
new new_tasks_path /task/new
edit edit_tasks_path(@task) /tasks/1/edit など

リンク生成のメソッドが、「Prefix & _path」の形式になっています。

次は、タスク作成ページ行きましょう。

tasks#new

newアクションはPOSTメソッドを作成するタスクを入力するフォームのあるページです。

Controller

インスタンスを作成します。

app/controllers/tasks_controller.rb
def new
  @task = Task.new
end

View

フォームはform_forを使用して設置します。

new.html.erb
<h1>タスクの作成</h1>

<%= form_for(@task) do |f| %>
  <%= f.label :content, 'タスク' %>
  <%= f.text_field :content %>

  <%= f.submit '投稿' %>
<% end %>

<%= link_to '一覧に戻る', tasks_path %>

一覧ページ(index)から新規タスク作成ページ(new)へのリンクを追加する

index.html.erb
<h1>タスクの作成</h1>

<%= form_for(@task) do |f| %>
  <%= f.label :content, 'タスク' %>
  <%= f.text_field :content %>

  <%= f.submit '投稿' %>
<% end %>

<%= link_to '一覧に戻る', tasks_path %>

indexページからのリンクができまして、クリックしたら下のようなViewが出力されると思います。

スクリーンショット 2018-06-07 00.04.44.png

完成しました。が、投稿ボタンを押してもcreateアクションを実装していないので何もなりません。

次は、それを実装しましょう。

tasks#create

createアクションは、newアクションからきたViewページから送信されてきたフォームを処理するところです。

Controller

newからcreateへ送られてきたフォームの内容はparams[:task]に入っていますが、そのまま使用するとセキュリティ上の問題があります。なので、ストロングパラメータというフィルタを使用します。

class TasksController < ApplicationController
  #省略

  def create
    @task = Task.new(task_params)

    if @task.save
      flash[:success] = 'タスクが投稿されました'
      redirect_to @task
    else
      flash[:danger] = 'タスクが投稿されません'
      render :new
    end
  end

  def edit
  end

  def update
  end

  def destroy
  end
end

private

def task_params
  params.require(:task).permit(:content)
end

ストロングパラメータに関して

:contentだけが必要なカラムの場合、このように記述します。

privateに関して

privateは明示するものです。何を明示するかというと、それ以降のメソッドがアクションではなく、そのクラス内だけで使用するメソッドだと明示するものです。

条件分岐に関して

なんの条件分岐かというと、
@task.saveが成功した場合、つまり、trueだった場合はif文中のコマンドが実行されます。
@task.saveが失敗した場合、つまり、falseだった場合はelse文中のコマンドが実行されます。
falseになるケースの例でいうと、255文字以上の文字列を入力した場合ですね。varchar(255)で入力文字数の限界が255文字なので。

mysql> describe tasks;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| content    | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

flashに関して

flashは投稿が正常に行われたことを知らせる機能です。後ほどviewを実装して行きます。

redirect_to と render の違い

一番の違いは、redirect_toはHTTPリクエスト(GETメソッド)を発生させ、renderは発生させないところです。

「GET」「/tasks/:id」というHTTPリクエストが送られるので、ルーティングに従って、Tasksコントローラーのshowアクションの処理に移り、その結果として、詳細ページが表示される、ということですね。

参考
https://qiita.com/january108/items/54143581ab1f03deefa1

@task.saveが成功した場合、redirect_to リンク先は、リンク先(@taskのshowルーティング)に強制的に飛ばすメソッドです。
そして、show/idを出力して、flashが出て保存されたことがわかります。

@task.saveが失敗した場合、render :newなので、new.html.erbに直接飛びます。

View

保存に成功した場合、/tasks/:idへリダイレクトします。

show.html.erb
<h1>id:<%= @task.id %>のタスク詳細</h1>

<p><%= @task.content %></p>

<%= link_to 一覧へ戻る,tasks_path %>

ここで、フラッシュメッセージ(flash)を表示させるための、設定をして行きます。

/views/layouts/_flash_tasks.html.erb
<% flash.each do |task_type, task| %>
  <div><%= task %></div>
<% end %>

全てのViewで表示させたいので、/views/layouts/application.html.erbrenderさせます。

/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Tasklist</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= render 'layouts/_flash_tasks' %>

    <%= yield %>
  </body>
</html>

これで、flashに代入されるとメッセージが表示されるようになります。flashは、ControllerからViewへ、ハッシュの形式でメッセージを渡します({ success: 'タスクが投稿されました' })←こんな感じ。successとかdangerにしているのは、bootstrap都合です。

参考
https://qiita.com/youcune/items/12b153c08db695952e47

次は、タスク編集機能へ行きましょう。

tasks#edit

Controller

編集するページのidが知りたいので

tasks_controller.rb
def edit
  @task = Task.find(params[:id])
end

View

newviewファイルとほぼ同じです。

edit.html.erb
<h1>タスクの編集</h1>

<%= form_for(@task) do |f| %>
  <%= f.label :content, 'タスク' %>
  <%= f.text_field :content %>

  <%= f.submit '投稿' %>
<% end %>

<%= link_to '一覧に戻る', tasks_path %>

ではshowviewページから、editviewページにリンクさせましょう。

show.html.erb
<h1>id:<%= @task.id %>のタスク詳細</h1>

<p><%= @task.content %></p>

<%= link_to '一覧へ戻る', tasks_path %>

<%= link_to '編集ページへ', edit_task_path(@task) %>

次は、タスク更新の機能作成を実装しに行きましょう。

tasks#update

コントローラ部分は、createアクションに似ています。

Controller

tasks_controller.rb
def update
  @task = Task.find(params[:id])

  if @task.update(task_params)
    flash[:success] = 'タスクが編集されました'
    redirect_to @task
  else
    flash.now[:danger] = 'タスクが編集されませんでした'
    render :new
  end
end

スクリーンショット 2018-06-07 21.37.36.png

createアクションと異なるところは、①と②の部分です。なぜ違うかというのは、手順を説明すればわかるかと思います。

  • tasks/id(タスク詳細ページ)にある「編集ページへ」リンクをクリック
  • editアクションが呼ばれる
  • 編集して、投稿ボタンを押すとおそらくupdateアクションに行く
  • updateアクションとしては、どのidを編集・更新すべきかわからないのでそれを取得しようとしてる(それが①)
  • 取得したidがストロングパラメータ(task_params)を通過した後、:contentがあればif文中が実行。なければelse(それが②)

View

不要です。

次は、タスク削除に行きましょう。

tasks#destroy

Controller

tasks_controller.rb
def destroy
  @task = Task.find(params[:id])
  @task.destroy

  flash[:success] = 'タスクが削除されました'
  redirect_to tasks_path
end

 リダイレクトする際、今まではprefix_pathを利用してきましたが、リダイレクトの場合だけは~_urlを使用しましょう。

View

削除用のViewは不要です。

ただ、削除用のリンクは必要なので、showページに書きましょう。

show.html.erb
<h1>id:<%= @task.id %>のタスク詳細</h1>

<p><%= @task.content %></p>

<%= link_to '一覧へ戻る', tasks_path %>

<%= link_to '編集ページへ', edit_task_path(@task) %>

<%= link_to '削除する', @task, method: :delete, data:{ confirm: '本当によろしいですか?'} %>

method: :delete はDELETEメソッドを送信することを明示しています。data: { confirm: ... }はJava Scriptの記述です。


これで、自分のRails学習用課題である、全てのCRUDメソッド(GET, POST, PUT, DELETE)を使って、Web上のリソースを操作できるアプリケーション作成が完了です。

レポジトリ

https://github.com/ryosuketter/kadai-tasklist

参考