[Ruby on Rails] チュートリアル的にToDoアプリを作ってみた話

# はじめに
`Ruby on Rails` を手っ取り早く学習したいと思ってToDoアプリを作りました。
作っていく中で個人的にハマったところなどを忘れないうちにメモっておきたいと思います。

ちなみにこの記事は `fukuokarb` 中に書きました。
初参加でしたが、今後とも継続して参加していきたいと思っております。

## 成果物
こんなの作りました。
どこからどう見ても `CRUD` のお勉強をするのに最適とされるToDoリストアプリですね。
<img width="1432" alt="スクリーンショット 2018-10-10 16.21.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/165355/9a447420-24c2-d63d-367c-f2b80535f381.png">

# 構築
開発環境の構築手順については[**こちら**](https://qiita.com/tosite0345/items/baabf9a325d8100a621a) をご覧ください。

## データベース準備
この辺はご多分に漏れずMigrationファイル作成→Migrate実行という流れになります。
事前に `rake db:create` を行ってデータベースの生成を行っておきましょう。

```bash:console
# Migrationファイル生成
rails g migration create_tasks

# モデル生成
rails g model task
```

なお、Railsの規約によるとモデルは**単数形**、テーブルは**複数形**とのこと。
モデルは `new` などの処理で単数を扱うから、テーブルは複数のレコードを持つからそれぞれそういう区分けになっているみたいです。
この辺はLaravelも一緒ですね。
続いてMigrationファイルを修正していきます。

```ruby:db/migrate/yyyymmddhhiiss_create_tasks.db
class CreateTasks < ActiveRecord::Migration[5.2]
  def change
    create_table :tasks do |t|
      # 設定できるもの
      # string     : 文字列
      # text       : 長い文字列
      # integer    : 整数
      # float      : 浮動小数
      # decimal    : 倍精度小数
      # datetime   : 日時
      # timestamp  : タイムスタンプ
      # timestamps : created_at, updated_at
      # time       : 時間
      # date       : 日付
      # binary     : バイナリデータ
      # boolean    : Boolean

      t.text :state
      t.text :task
      t.date :limit_date

      t.timestamps
    end
  end
end
```

続いてテストデータの作成。
手で書いてもいいけど… `Seeder` から流したほうが楽ですよね。

```ruby:db/seeds.rb
@task              = Task.new
@task.task         = 'task1'
@task.state        = 'todo'
@task.limit_date   = '2018-10-10'
@task.save
```

最後にMigrate実行、Seedingを行います。

```bash:console
rake db:migrate
rake db:seed
# ミスった場合は次のコマンドを投入
rake db:rollback
```
## コントローラー生成
次にコントローラーを生成してきます。
ちなみにコントローラーは複数のリソースを一つのファイルで扱うので**複数形**。

```bash:console
# コントローラーだけを生成する場合
rails g controller tasks

# メソッドも含めてスケルトンを生成する場合
rails g controller tasks index show store update destroy
```

## ルート修正
続いてコントローラーを修正していきます。
RESTfulに書きたかったので。

```ruby:config/routes.rb
Rails.application.routes.draw do
  get    'tasks'     => 'tasks#index'
  post   'tasks'     => 'tasks#store'
  get    'tasks/:id' => 'tasks#show'
  put    'tasks/:id' => 'tasks#update'
  delete 'tasks/:id' => 'tasks#destroy'
  # この書き方でもOKとのこと
  # get '/tasks', to: 'tasks#index'
end
```

間違いなくもっと効率的なルートの記述方法があるので、また調べて追記します。

### 追記
[**こちら**](http://shiro-16.hatenablog.com/entry/2015/03/19/001656) にいい感じの記事がありました。

## サーバーの起動
ここでサーバーを起動してみます。
なお、バックエンドの修正を行った場合、毎回止めてサーバーの再起動をしないといけない…？

```bash:console
bin/rails server -b 0.0.0.0 -p 2000
```


## Viewファイル修正
それぞれのファイルを修正していきますが、細かい部分については下の「各種機能の実装」に記載します。
ただ少し注意点が。

### View内コード実行

```erb:index.html
<% @tasks.each do |t| %>
  <p><%= t.task %></p>
<% end %>
```

### フォームデータを送る場合
Laravelで言うところの `csrf_token` 的なものを記載しておかないと怒られます。
Railsでは `authenticity_token` と呼ばれているようですね。
これは `form_authenticity_token` という関数で生成できるみたいです（[**参考**](https://blog.willnet.in/entry/20080509/1210338845)）。
次のように記述しましょう。

```erb:ruby_form.html
<form method="POST" action="/tasks">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
</form>
```

**参考：Laravelの場合**

```php:php_form.html
<form method="POST" action="/tasks">
  {{ crsf_fileld() }}
</form>
```

### PUT(PATCH), DELETEをする場合
これまたLaravelで言うところのヘルパ関数、 `method_field('method')` 的なものを用意しておかないと全て `POST(GET)` で送られてしまいます。
Railsでも `_method` という隠し要素を送ることで問題なくパスできます。

```erb:ruby_form.html
<form method="POST" action="/tasks">
  <input type="hidden" name="_method" value="PUT">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
</form>
```

**参考：Laravelの場合**

```php:php_form.html
<form method="POST" action="/tasks">
  {{ crsf_fileld() }}
  {{ method_field('PUT') }}
</form>
```

## 各種機能の実装

### タスク一覧取得(GET)
まずはindexページに飛ばします。
スクショに貼り付けている画面ですね。
ルーティングは `/tasks` 、 コントローラーメソッドは `index`。

#### Controller実装

```ruby:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks  = Task.order('limit_date').all
    @status = ['todo', 'doing', 'done']  
  end
  # ...
end
```

特に難しいことはやっていませんが、期限の近いものから順に並べたデータを取得しています。
細かい検索などをやりたければ [**こちら**](https://qiita.com/tono0123/items/576a4a4659b51860f304) の記事が参考になるかと。
ちなみに `@tasks` としているのは、インスタンス変数扱いにしてViewに引き渡すためのようです（[**参考**](https://www.rubylife.jp/ini/class/index4.html)）。
Laravelだと `return view('tasks.index', ['tasks' => $tasks])` と書いていました。
記述を簡略化できる半面、少し慣れるまでに時間がかかりそうという印象。

#### View実装
```erb:app/views/tasks/index.erb
<table>
  <thead>
    <tr>
      <th>State</th>
      <th>Limit</th>
      <th>Task</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <% @tasks.each do |t| %>
    <tr>
      <td><%= t.state %></td>
      <td><%= t.limit_date %></td>
      <td><%= t.task %></td>
      <td>
        <!-- 次のタスク詳細取得画面へのリンク -->
        <a href="/tasks/<%= t.id %>">edit</a>
      </td>
      <td>
        <!-- まだ実装しない -->
      　　 <button type="submit">delete</button>
      </td>
    </tr>
    <% end %>
  </tbody>
</table>
```

こちらも特に難しいことはやっていません。
デザイン部分は全て削ってあります。
`DELETE` 部分はあとで追記します。

### タスク作成(POST)
`app/views/tasks/index.erb` に少し追記。
先頭にタスク作成フォームを追します。

#### View修正

```erb:app/views/tasks/index.erb
<form method="POST" action="/tasks">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
  <div>
    <label for="task">Task Name</label>
    <input id="task" name="task" type="text" required>
  </div>
  <div>
    <select name="state">
      <% @status.each_with_index do |s,i| %>
      <option value="<%= s %>"><%= s %></option>
      <% end %>
    </select>
    <label>Task State</label>
  </div>
  <div>
    <input type="date" name="limit_date" required>
    <label>Task Limit Day</label>
  </div>
  <p>
    <button type="submit">create</button>
  </p>
</div>
</form>

<!-- flashメッセージを表示 -->
<% flash.each do |msg| %>
<p><%= msg %></p>
<% end %>

<table>...</table>
```

#### Controller実装

```ruby:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  # ...
  def store
    task = Task.new
    task.task       = params[:task]
    task.state      = params[:state]
    task.limit_date = params[:limit_date]
    task.save
    redirect_to '/tasks', notice: 'タスクを作成しました。'
  end
  # ...
end
```

`redirect_to` でindexページに差し戻しています（[**参考**](http://railsdoc.com/references/redirect_to)）。
差し戻す際に `notice: 'message'` として `flash` に情報を残しておくと一回こっきりで表示されるメッセージ（例：ユーザーを登録しました）をViewに引き渡せます（[**参考**](https://ruby-rails.hatenadiary.com/entry/20141127/1417086075)）。


### タスク詳細取得(GET)
`index.erb` からのリンクで入ってきます。
ルーティングは `/tasks/:id` 、コントローラーメソッドは `show`。

#### Controller実装

```ruby:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  # ...
  def show
    id      = params[:id]
    @task   = Task.find(id)
    @status = ['todo', 'doing', 'done']
  end
  # ...
end
```

こちらも何も難しいことはしておりません。
indexページから引き渡ってきたパラメータ（`params`）を使っているくらい。

#### View実装

```erb:app/views/tasks/show.erb
<form method="POST" action="/tasks/<%= @task.id %>">
  <input type="hidden" name="_method" value="PUT">
  <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
  <div>
    <label for="task">Task Name</label>
    <input value="<%= @task.task %>" id="task" name="task" type="text" required>
  </div>
  <div>
    <select name="state">
      <% @status.each_with_index do |s,i| %>
      <option value="<%= s %>"><%= s %></option>
      <% end %>
    </select>
    <label>Task State</label>
  </div>
  <div>
    <input type="date" name="limit_date" required>
    <label>Task Limit Day</label>
  </div>
  <p>
    <button type="submit">update</button>
  </p>
</form>
```

次のこともあるので、先にフォームだけ生成しておきます。
トークンとメソッドの上書きを忘れないこと。

### タスク更新(PUT)
フォームは先に生成しているので省略。
こちらも特に難しいことはありません。
ルーティングは `/tasks/:id` 、 コントローラーメソッドは `update`。

#### Controller実装

```ruby:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  # ...
  def update
    id   = params[:id]
    task = Task.find(1)

    task.task         = params[:task]
    task.state        = params[:state]
    task.limit_date   = params[:limit_date]
    task.save

    redirect_to '/tasks', notice: 'タスクを更新しました。'
  end
  # ...
end
```

ちなみにModelにアクセスするメソッドについては [**こちら**](https://ruby-rails.hatenadiary.com/entry/20140724/1406142120#ar-update) を参考にしました。

### タスク削除(DELETE)
はい、最後に削除処理です。
ルーティングは `/tasks/:id` 、 コントローラーメソッドは`destroy`。

#### View修正

```erb:app/views/tasks/index.erb
<table>
  ...
  <tbody>
    <% @tasks.each do |t| %>
    <tr>
      <td><%= t.state %></td>
      <td><%= t.limit_date %></td>
      <td><%= t.task %></td>
      <td>
        <!-- 次のタスク詳細取得画面へのリンク -->
        <a href="/tasks/<%= t.id %>">edit</a>
      </td>
      <td>
　　　　　　　　　　　　  <!-- ここを修正する -->
        <form method="POST" action="/tasks/<%= t.id %>">
          <input type="hidden" name="_method" value="DELETE">
          <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
          <button type="submit">delete</button>
        </form>
      </td>
    </tr>
    <% end %>
  </tbody>
</table>
```

隠しメソッドを `DELETE` で送るためだけのフォームを実装。

#### Controller実装

```ruby:app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  # ...
  def destroy
    task       = Task.find(params[:id])
    task.destroy
    redirect_to '/tasks', notice: 'タスクを削除しました。'
  end
  # ...
end
```

特に悩むことなし。

# 終わりに
ね？簡単でしょ？
全ソースコードは [**こちら**](https://github.com/tosite0345/rails-todo) に置いています。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">（そういやログイン機能は未実装だった）</p>&mdash; ♡まおちゃ♡ (@mao_sum) <a href="https://twitter.com/mao_sum/status/1049959017181671424?ref_src=twsrc%5Etfw">2018年10月10日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

今後は [**これ**](https://railstutorial.jp/) をやっていき