Sinatraで家族用todoリストを作る


Sinatraで家族用todoリストを作ってみる

Sinatraでアプリを一つ作ってみようということで作成しました。
と言っても、ベースとしての機能はこちらを参考にしています。

Sinatra入門 (全17回) - プログラミングならドットインストール


今回は、家族で使えるtodoリストになるように機能を追加します。
追加する主な機能としては以下の4点となります。

  • 投稿時間の表示
  • ユーザーを選択するセレクトボックス
  • ユーザーに対応したtodo表示
  • ユーザーの新規登録

sinatra入門からのステップアップとして良い題材になると思います。
まず初めに完成アプリの画面です。

以降コードの解説です。

投稿時間の表示

rbファイルにDateクラスの読み込み。

tlist.rb
require date

SQLファイルで作成したテーブルにcreated_atを追加(レコードの生成時間を自動で取得してくれる)。

table.sql
create table todos
(
  id integer PRIMARY key,
  users_id integer,
  body text,
  created_at
);

インスタンス変数の@todosに対してeachメソッドで順番にbody要素を表示するブロック内に、投稿時間を表記するブロックを追加。
To_timeメソッドでTimeクラスに変更し、strftimemeメソッドで見やすく整形してあげる。

index.erb
<div class=“time”>
  <%= todo.created_at.to_time.strftime(%Y/%m/%d %H:%M:%S)%>
  <span class=“delete delete_todo>[x]</span>
</div>

ユーザーを指定するセレクトボックスの設置

SQLファイルにusersテーブルを作成。

table.sql
create table users
(
  id integer primary key,
  userName text
);

ActiveRecord::Baseメソッドでテーブル情報をUsersクラスに格納する。

tlist.rb
class Users < ActiveRecord::Base
  validates :userName, presence: true
end

Usersクラスをインスタンス変数に渡す。

tlist.rb
get / do
  @title = “やることリスト”
  @todos = Todos.all
  @users = Users.all
  erb :index
end

Eachメソッドでselectボックスに値を順次表示させることで、セレクトボックスでユーザーを選択できるようにする。

index.erb
<select name=“users_id” >
  <% @users.each do |user|%>
    <option value=<%= user.id%>><%= user.userName%></option>
  <% end %>
</select>

ユーザーに対応したtodo項目の表示

テーブルの1対多を用いた実装方法がわからなかったため、if文でidの一致するときの結果を表記するようにした。
先ほどのselectで 送信される値は、usersテーブルのidであり、それがtodosテーブルのusers_idに渡されるように設定する。



postで送られたデータをtodosテーブルに追加。

tlist.rb
post /create_todo do
  Todos.create(body: params[:body],users_id: params[:users_id])
  redirect to(/)
end



それぞれのユーザーに対するtodoリストの表示。
@users.eschの部分で順番にユーザーを表示していく
次に与えられたusersテーブルのidとtodosテーブルのusers_idが一致する場合、そのtodoリストを表示するように指定してあげる。

index.erb
  <% @users.each do |user| %>
    <div class=“user_box” data-user_id = <%=user.id%> data-token = <%= Rack::Csrf.csrf_token(env)%>>
        <div class=“user_name”>
          <%= Rack::Utils.escape_html(user.userName)%>
          <span class=“delete delete_user>[x]</span>
        </div>
        <ul>
          <% @todos.each do |todo|%>
            <%if user.id == todo.users_id%>
              <li data-id = <%= todo.id%> data-token = <%= Rack::Csrf.csrf_token(env)%>>
                <div class=“todo_body”>
                  <%= Rack::Utils.escape_html(todo.body)%>
                </div>
              </li>
          <% end%>
        </ul>
      <% end %>
    </div>
  <% end %>

ユーザーの新規登録機能

Todoリストの入力フォームと同様にフォームを作成、入力した値をuserNameとして返すように設定。

index.erb
<div class=“user_form”>
  <form action=“/create_user” method = “post”>
    <%= Rack::Csrf.csrf_tag(env)%>
    <span>新規ユーザー名:</span>
    <input type=“text” name=“userName” class=“user_input_box”>
    <input type=“submit” value=“新規登録” class = ‘btn btn_user>
  </form>
</div>

usersテーブルにデータを生成。

tlist.rb
post /create_user do
  Users.create(userName: params[:userName])
  redirect to(/)
end

以上で大半は完成です。
その他トークン処理、ユーザー削除機能について、ここでは割愛します。
制作物はgithubにあげていますのでそちらをご覧ください。

その他メモ

Csrf(リクエスト強要)対策について
※(CSRF:Cross-site Request Forgery)
ユーザーの意図に反してリクエストが送信されることを防止するために設ける。
リクエスト送信先のページで正規のページからリクエストが送信されたか確認すれば良い。
処理としてはinputデータにhidden要素でトークンを仕込んで、リクエスト送信先でそのトークンが正式なものであるか確認をする。

参考:CSRF対策をひよっこエンジニアがまとめてみた(随時更新予定)|クロノドライブ