railsチュートリアル　第八章

##はじめに
railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。

##セッション
**HTTPはステートレス (Stateless) なプロトコル**であり、ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内にはまったくない。

よって、ユーザーログインの必要なwebアプリケーションでは```セッション (Session)```と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定する必要がある。

###セッションコントローラ

まずはログインのフォームをnewアクションで処理するような、コントローラを作成する。

```
$ rails generate controller Sessions new
```

```create``` や ```destroy``` といったアクションも扱うが、ビューファイルが必要ないため、ここでは ```newアクション``` だけを一緒に生成しておく。


routes.rbでは、Usersリソースのように ```resourcesメソッド``` を使ってRESTfulなルーティングを自動的にフルセットで利用することはないので、**「名前付きルーティング」**だけを使う。

```routes.rb
Rails.application.routes.draw do
  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  #追加
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

  resources :users
end
```

このように名前付きルーティングが増えてきた場合は、以下のコマンドを打つことで**現状のルーティングを確認**することができる。

```
$ rails routes
```

###ログインフォーム

コントローラとルーティングを定義した次は**ビューファイル**を整えていく。

**ログインフォーム**では、会員登録をまだしていない人のために、登録のリンクも作っておく。

```app/views/sessions/new.html.erb
<% provide(:title, "Log in") %>
<h1>Log in</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_for(:session, url: login_path) do |f| %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.submit "Log in", class: "btn btn-primary" %>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>
```
>このソースではform_forタグを使用しているが、form_tagも同じく使用可能

セッションフォームとユーザー登録フォームの違いとして、セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない。

したがって、新しいセッションフォームを作成するときには、**form_forヘルパーに追加の情報を独自に渡す必要がある**。↓

```<%= form_for(:session, url: login_path) do |f| %>```

以下に、 HTMLフォームを示しておく。

```login.form
<form accept-charset="UTF-8" action="/login" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="session_email">Email</label>
  <input class="form-control" id="session_email"
         name="session[email]" type="text" />
  <label for="session_password">Password</label>
  <input id="session_password" name="session[password]"
         type="password" />
  <input class="btn btn-primary" name="commit" type="submit"
       value="Log in" />
</form>
```
コントローラで、それぞれのアクションを定義する。

```app/controllers/sessions_controller.rb
class SessionsController < ApplicationController

  def new
  end

  def create
    render 'new'
  end

  def destroy
  end
end
```

また、```createアクション``` でユーザーを検証する処理を書く。

```app/controllers/sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
    else
      # エラーメッセージを作成する
      render 'new'
    end
end
```

セッションでは、ユーザー登録のように ```Active Recordモデル``` を使っていないため、自分でエラーメッセージを作る必要がある。

```app/controllers/sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
    else
      #追加
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
end
```

↑では、flash[:danger]としてしまうと、一度表示されたメッセージが消えなくなってしまうため
```flash.now[:danger]```
を使い、一度だけ表示されるようにする。

##ログイン

まず、```SessionsHelper```を**コントローラ**に読みこますことで、セッションに必要なメソッドを使えるようにしておく。

```app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end
```


###log＿inメソッド
railsで定義済みのsessionメソッドを使って、Sessionsヘルパーにlog_inメソッドを定義する。

```app/helpers/sessions_helper.rb
module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
  end
end
```

これにより**sessionコントローラ**の ```createアクション``` を整えることができる。

```app/controllers/sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      #追加
      log_in user
      redirect_to user
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
end
```

```redirect_to user```
はrailsでは自動的に以下のルーティングに変換される。
```user_url(user)```

###現在のユーザー
セッションIDに対応するユーザーネームを取り出す、 ```current_userメソッド``` を定義する。

```
if @current_user.nil?
  @current_user = User.find_by(id: session[:user_id])
else
  @current_user
end
```
↑のようにすれば、current_userを定義することができるが、もっとコンパクトに定義することができる。
「||=」を使うことで以下のようになる。

```
@current_user ||= User.find_by(id: session[:user_id])
```
**「||=」は左辺がnilあれば、右辺を代入するという使い方ができる。**

よって、```current_userメソッド``` は以下のように定義することができる。

```app/helpers/sessions_helper.rb
# 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    if session[:user_id]
      @current_user ||= User.find_by(id: session[:user_id])
    end
  end
```

###レイアウトリンクの変更

ユーザーがログインしているかしていないかで、レイアウトを変更する必要がある。

下記のように ```logged_in?``` のメソッドを使えば、レイアウトを変更する必要がある。

```
<% if logged_in? %>
  # ログインユーザー用のリンク
<% else %>
  # ログインしていないユーザー用のリンク
<% end %>
```

よって、```logged_in?``` を定義する。
**current_userがnilでなければログインしている**、という風に解釈できるので↓のように定義できる。

```app/helpers/sessions_helper.rb
module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
  end

  # 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    if session[:user_id]
      @current_user ||= User.find_by(id: session[:user_id])
    end
  end

  # ユーザーがログインしていればtrue、その他ならfalseを返す
  #追加
  def logged_in?
    !current_user.nil?
  end
end
```

##ユーザー登録時にログイン状態

登録が終われば自動的にログインがすんでいる状態になるように**usersコントローラのcreateアクションを追加修正しておく。**

```app/controllers/users_controller.rb
def create
    @user = User.new(user_params)
    if @user.save
      #追加
      log_in @user
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
end
```

##ログアウト


``` session.delete(:user_id) ```

↑で、現在のユーザーを**nil**にすることができるので、```logoutメソッド``` を以下のように定義する。


```app/helpers/sessions_helper.rb
module SessionsHelper
 # 現在のユーザーをログアウトする
  def log_out
    session.delete(:user_id)
    #セキュリティ上、念の為次の一行も追加する
    @current_user = nil
  end
end
```

次に、**Sessionsコントローラ**の ```destroyアクション``` を定義する。

```app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
.
.
.

  def destroy
      log_out
      redirect_to root_url
  end
end
```

##終わりに
第八章では、Sessionコントローラでログイン・ログアウトを実装した。

webアプリケーションには必要な機能のためしっかり理解しておこうと思う。

次↓
https://qiita.com/jonnyjonnyj1397/items/9cb25f506404c7ae5b9e