sinatraでログイン機能

#はじめに
初学者向けにsinatraで簡単な掲示板作成について話す機会があるので資料として残します。

# Sinatraとは
Sinatraは最小の労力でRubyによるWebアプリケーションを手早く作るための、軽量フレームワーク。

# 1. Sinatraのインストール
Sinatraを使えるようにgemをインストールする。

```terminal
gem install sinatra
```

# 2. test.rbの作成

ディレクトリにtest.rbを作成する。ファイル名はなんでも良い。

```ruby:test.rb

require 'sinatra'   #インストールしたsinatraの呼び出し

get '/' do          #getでルーティングを設定
  'Hello world'
end
```
ターミナル上で `ruby test.rb` を叩くと下記のようにサーバーが起動する。

```terminal
ruby test.rb 
== Sinatra (v2.0.7) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Version 3.12.1 (ruby 2.6.5-p114), codename: Llamas in Pajamas
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://localhost:4567
Use Ctrl-C to stop
```
コントロール+Cでサーバーを終了できる。

# 3. Sinatraにオートリロードを設定する
オートリロードの設定をしないと、test.rbに記述するたびにサーバーを再起動しないといけない。
以下のgemをインストールする。

```terminal
$ gem install sinatra-contrib
```

```ruby:test.rb
require 'sinatra'
require 'sinatra/reloader' #追記する

get '/' do
  'Hello world'
end
```

# 4. データベースに接続する
Macにpostgressqlをインストールする。herokuにデプロイすることを想定してpostgressqlを使う。

```terminal
 # 順にやっていく
$ brew update             
$ brew install postgresql
$ brew services restart postgresql #起動する
```
testというデータベースを作成する。  `\l`で作成したデータベースのリストを確認できる。
デフォルトでpostgresというデータベースは存在する

```terminal
$ psql -d postgres # データベースに入れる            
psql (11.3)
Type "help" for help.

postgres=# CREATE DATABASE test;  #ここでtestというデータベースを作成
postgres=# \c test               #\cでデータベースを変更
You are now connected to database "test" as user "省略".
test=# CREATE TABLE users (           
test(#   id SERIAL NOT NULL PRIMARY KEY ,
test(#   name VARCHAR( 任意の数字 ) NOT NULL ,
test(#   email VARCHAR( 任意の数字 ) NOT NULL ,
test(#   password VARCHAR( 任意の数字 ) NOT NULL ,
test(#   UNIQUE (email)
test(# );
CREATE TABLE
test=#
```

```rb:test.rb
require 'sinatra'
require 'sinatra/reloader'
require 'pg' # postgresを使うため記述する

# clinetという変数にpostgresで使えるように先ほど作成したtestデータベースを接続する
client = PG::connect(
  dbname: 'test'
)
```

# 5.sessionの準備
sessionとはブラウザに状態を保持さておくための機能。
ログインができるのはsessioが状態を保持しておいてくれるからです。たとえばECサイトで商品を選択して購入一覧に表示されますが、これを後日確認しても購入一覧に残っています。これはsessionが状態を保持してくれているからです。ではずsessionはいつ終わるのという疑問が湧きますが、これはsession-timeoutという方法で対応します。[参考](https://tadtadya.com/web-what-is-a-session/)

- sinatraでsessionを扱うには以下の指定を行う

```rb:test
require 'sinatra'
require 'sinatra/reloader'
require 'pg' 

enable :sessions  #これでsessionが使える

-省略-
```

# 6.新規登録機能（サインアップ）のエンドポイントを作る

```rb:test.rb

# サインアップフォームへのエンドポイント
get '/signup' do
  erb :signup
end

# ここのエンドポイントでpostで送られてきたパラメータをusersデーブルに保存する
post '/signup' do
  
  name = params[:name]    #view側で送信されたnameを受け取る
  email = params[:email]  #view側で送信されたemailを受け取る
  pass = params[:pass]    #view側で送信されたpassを受け取る
  confirmation = params[:confirmation] #view側で送信されたconfirmationを受け取る

  redirect '/signup' if pass != confirmation  #もしpassとconfirmationが違う時 /signupにリダイレクトされる。

  client.exec_params('insert into users(name, email, password) values($1, $2, $3)', [name, email, pass]) # usersテーブルに上で定義した変数(name,email,pass)の中身を入れる。

  redirect '/'   # '/' にリダイレクトされる
end
```

# 7.新規登録(サインアップ)のviewを作る
1. 送信フォームはformタグで囲む。ここでHTTPメソッドパスを指定してあげることが大切。
2. labelはラベル
3. inputタグで入力欄が生成される。
4. この時 `type=〇〇`と指定してあげることでtest.rb側のparamsで受け取ることができる。

```rb:signup.erb
<h1>サインアップ画面</h1>

<form action="/signup" method="POST">  　　　#test.rbのpostメソッドの /signupに飛ぶように指定している。
  <label for="input_name">Neme： </label>
  <input id="input_name" type="text" name="name" required><br>　#typeの指定でparamsで受け取れる
  <label for="input_email">Email： </label>
  <input id="input_email" type="email" name="email" required><br>　
  <label for="input_pass">PASSWORD </label>
  <input id="input_pass" type="password" name="pass" required><br>
  <label for="input_re_pass">Confirmation PASSWORD </label>
  <input id="input_re_pass" type="password" name="confirmation" required><br>
  <input type="submit" value="送信">
</form>

```

# 8.ログイン機能のエンドポイントを作る
sessionを使う


```rb:test.rb

# サインアップフォームへのエンドポイント
get '/login' do
  erb :login
end

# ここのエンドポイントでpostで送られてきたパラメータを元にsessionに状態を保存するか条件分岐させる。
post '/login' do
  name = params[:name]
  email = params[:email]
  pass = params[:pass]

  @res = client.exec_params('select * from users where name = $1 and email = $2 and password = $3', [name, email, pass]).first # @resにparamsで受け取ったname、email、passを元にusersテーブルからとってきて代入する。

  redirect '/login' if @res.nil?　#@resがnilだと /loginにリダイレクト

  session[:user_id] = @res['id'].to_i  #@res['id'].to_iは@res['id']と取得されるデータが文字列なので整数に変換してsession[:user_id]に代入している。これでsessionに現在ログインしているuseが保持される。

  redirect '/'
end
```
`clinet.exec_params('select * from users where name = $1 and email = $2 and password = $3', [name, email, pass]).first` について解説。whereの後に `$1や$2や$3` などを記述している理由はSQLインジェクション対策である。後ろの[name,email,pass]が順番に引数としてwhereに渡される。sqlインジェクションとは


# 9.ログインのviewを作る

 ```rb:login.erb

<h1>ログイン画面</h1>

<form action="/login" method="POST">
  <label for="input_name">Neme： </label>
  <input id="input_name" type="text" name="name" required><br>
  <label for="input_email">Email： </label>
  <input id="input_email" type="email" name="email" required><br>
  <label for="input_pass">PASSWORD </label>
  <input id="input_pass" type="password" name="pass" required><br>
  <input type="submit" value="送信">
</form>
```

# 参考
[sinatraでオートリロードを設定する](https://qiita.com/izumin5210/items/cd2f9f48fbe1fdcaf628)
[ぺけみさお sinatraでsessionを使う方法](https://www.xmisao.com/2013/11/09/how-to-use-session-in-sinatra.html)
[プログラマが知るべき97のこと。ドメイン特化言語](https://xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com/%E3%82%A8%E3%83%83%E3%82%BB%E3%82%A4/%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E7%89%B9%E5%8C%96%E8%A8%80%E8%AA%9E/)
[sessionとは.分かりそうで分からないIT辞典](https://wa3.i-3-i.info/word1791.html)