Sinatraでデータを修正、更新する（CRUDのUpdate機能）

Rubyでシステムを構築する際に、Railsだとちょっとボリュームが多いので、そんな場合は軽量フレームワークとしてSinatra（PythonのFlaskみたいなもの）というものがあります。

そして、色々と使い方を解説したページや記事もあるのですが、なぜかデータの修正、更新機能については触れていないことが多く、それではどうやって作るのかをひたすら調べ、ようやく解決したので、備忘録として置いておきます。

まずは、この記事（自分とは別人です）のように登録機能と削除機能を作ってみてください。自分の記事はそこから補足説明となります。

- [sinatraでの最小限のアプリを作る](https://qiita.com/yukihigasi/items/284418046b8aac55d05b)

## なぜ、データ修正機能の記事が殆どないのか？
MVCの観点を鑑みて、全ての機能を懇切丁寧に解説しているものは日本語サイトで皆無だったので、英語サイトも隈なく探してみたのですが、そういったサイトは見つかりませんでした（この記事を作ろうとしたきっかけです）。

その理由は明白で、SinatraはRailsとは異なりコントローラの自動生成が簡単にできない上にRailsと記述が若干異なります。その上、公式ページの解説がかなり不親切なので、実際に機能作成を行った事例が少なく、またそのような例もコントローラ部分の解説にとどまっていることが多く、テンプレート周りの記述事例がほとんどありませんでした（昔は、後述する特記すべき注意事項がなくても動かせていたようです）。

また、普通にサーバ起動してからデータ更新処理を行うと、処理がもたつきます。したがってRackサーバを使って立ち上げる方が能率的なのですが、そもそもSinatraは軽量が売りでありそこまで大掛かりなものは作らないので、利用目的としては矛盾している（実用的価値が少ない）のもありそうです。このへんも踏まえて解説できたらと思います。

- [Rackとは何か](https://qiita.com/k0kubun/items/248395f68164b52aec4a)

# ツリー構造
最低限のデータ構造はこうなっています。Rackを使うので、そのためにはconfig.ruが必要となります。config.ruは自動生成されないので、適宜作成します。

```
- 任意のプロジェクト名
    - db
    　　 sindb.db #今回使用するDB 
    - public
    - vendor
    - views
        edit.erb  #編集用
        index.erb #リスト
    config.ru #Ruckの設定ファイル
    app.rb #コントローラ
```

# Rackの設定
Rackサーバの設定はconfig.ruで行います。色々記事がありますが、最低限これだけ必要です。

```config.ru
require './app' #コントローラ
run Sinatra::Application
```

また、Rackサーバの起動コマンドは以下の通りとなります(ローカルサーバ接続)。また、rackのデフォルトポートは9292となるので、sinatraと同じように4567にしています(bashを使ってbundle execを省略した場合、rackupコマンドで起動可能)。

```
# bundle exec rackup -o 0.0.0.0 -p 4567
```

# コントローラのメソッド記述
データの更新作業は以下のプロセスを経由します。

一覧画面から修正ボタンを押下　→　編集画面に遷移　→　編集画面でデータの編集　
→　編集画面から更新ボタンを押下　→更新処理　→　一覧画面に遷移

このうち、編集画面に遷移するにはgetメソッドを使用し、部分的なデータ修正を反映させるにはpatchメソッドを使用します。また、今回はレコード名がuserとなっているので、以下のディレクトリで処理が行われます。

```app.rb
# 編集データを編集画面(edit.erb)に遷移
get '/users/:id/edit' do
	@user = User.find_by_id(params[:id]) #該当IDのデータ取得
	erb :edit #編集画面の呼び出し
end

# 編集画面での更新処理を反映
patch '/users/:id' do
	@user = User.find_by_id(params[:id]) #該当IDのデータ情報取得
	@user.name = params[:name] #更新データの代入
	@user.save #データ更新の反映
	redirect to "/" #一覧画面へ
end
```

参考にしたサイト

 - [Sinatra Restful Routes Readme](https://learn.co/lessons/sinatra-restful-routes-readme)  【英語サイト】
 
コントローラの全容はこのようになっています。また、csrf対策のために任意のトークンを発行する必要があります。

```app.rb
require 'sinatra' 
require 'active_record' #モデルからレコード取得
require 'rack/csrf' #csrf制御に不可欠

use Rack::Session::Cookie, secret: "xxxxxxxxxxxxxxxxxxxxxxxxxxx" #任意のトークン
use Rack::Csrf, rise:true

ActiveRecord::Base.establish_connection(
	adapter: 'sqlite3',
	database: './db/sindb.db'
)

class User < ActiveRecord::Base
	validates :name, presence: true
end

get '/' do
	@title = "User List"
	@users = User.all #テーブルレコードの全データ取得
	erb:index
end

# 新規作成
post '/create' do
	User.create(name: params[:name])
	redirect to('/')
end

#編集
get '/users/:id/edit' do
	@user = User.find_by_id(params[:id])
	erb :edit
end

# 編集データの更新
patch '/users/:id' do
	@user = User.find_by_id(params[:id])
	@user.name = params[:name]
	@user.save
	#redirect to 
	redirect to "/"
end

# 削除
post '/destroy' do
	User.find(params[:id]).destroy
end
```


# テンプレートの作成
テンプレートの注意点としてですが、**CSRF対策を行わないと403エラーに悩まされる**ことになります（一覧、編集画面の双方で対応が必要になっているようで、相当悩まされました）。

## 一覧
一覧画面はそこまで難しく考える必要ないですが、actionプロパティの値に変数を埋め込む必要があります。

```html:index.erb
<body>
<h1><%= @title %></h1>
<% @users.each do |user| %>
    <form method="get" action="/users/<%= user.id %>/edit">
    <ul>
	    <li data-id="<%= user.id %>" data-token="<%= Rack::Csrf.csrf_token(env) %>">
			<%= Rack::Utils.escape_html(user.name) %>
			<button type="submit" >修正</button>
			<button class="delete">削除</button>
	    </li>
    </ul>
    </form>
<% end %>

<form action="/create" method="post">
  <%= Rack::Csrf.csrf_tag(env) %> 
  <input type="text" name="name">
  <input type="submit" value="追加">
</form>
</body>
```

## 編集画面
編集画面は以下の通りとなります。大事なポイントはpatch処理が必要なので、メソッドをhiddenで埋め込む必要があります。

```html:edit.erb
<form action="/users/<%= @user.id %>" method="post">
    <%= Rack::Csrf.csrf_tag(env) %> 
    <input id="hidden" type="hidden" name="_method" value="patch">
    <input type="text" name="name" value="<%= @user.name %>">
    <button type="submit">更新</button>
</form>

```

# 実際の動き

## 一覧画面(更新前)
ここではshoujiというデータを編集するので、修正ボタンを押します。

**192.168.11.xx:4567**
![list.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272191/bdf1d560-fcd0-36db-314e-eee4dfc09ef2.jpeg)

## 編集処理
編集画面に遷移するのでshoujiをshinjiに変更します。usersディレクトリを作る必要はなく、編集画面（edit.erb）もindex.erbと同階層に作成されています。

**192.168.11.xx:4567/users/5/edit?**
![edit.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272191/ac45e4e8-0905-86d0-3ce5-1bd7fb100e5a.jpeg)


## 一覧画面（更新後）
データが反映されます。

**192.168.11.xx:4567**
![index2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272191/562d2b85-b6fd-d589-cb19-015238eecea2.jpeg)

# DBで確認
sqlite3で実際に確認してみましょう。sqlite3の場合は、DBファイルをそのまま呼び出します。

```
#sqlite3 db/sindb.db
```

修正前のデータです。

```
sqlite> select * from users;
1|hamana
2|kawaguchi
3|motosu
4|sai
5|shouji
6|saroma
7|shikotsu
8|touya
9|kussharo
10|akan
```

修正後のデータです。データはしっかりと入れ替わっていました。

```
sqlite> select * from users;
1|hamana
2|kawaguchi
3|motosu
4|sai
5|shinji
6|saroma
7|shikotsu
8|touya
9|kussharo
10|akan
sqlite>
```