わたしがRailsチュートリアルで学んだこと【8章】

* 注意：プログラミング歴34日の初心者が書いています

* 注意：間違っていたら優しく教えてください（喜びます）

「Ruby on Rails チュートリアル実例を使ってRailsを学ぼう」
 https://railstutorial.jp/

素晴らしいチュートリアルに感謝します。

#8.1 セッション

HTTPは、その場限り。以前の情報を全く持たないリクエストです。
そのため、ログイン情報の保持には、セッションという接続を使います。

Railsでセッションを実装する方法として最も一般的なのは、cookiesを使う方法です。cookiesとは、ユーザーのブラウザに保存される小さなテキストデータです。

セッションについても例に漏れず、Sessionsコントローラで操作します。

コントローラを追加したら、`routes.rb`に対応するルーティングを追加します。今回は、

* new 新しいセッションのページ (ログイン画面)

* create 新しいセッションの作成 (実際のログイン)

* destroy セッションの削除 (ログアウト)

という3つのアクションを、`/login`というURLに紐づけます。


#8.1.2 ログインフォーム

フォームに入力されたログイン情報を保存したい、でもセッションにはUserのときのようなモデル、データベースはありません。

```rb
form_for(:session, url: login_path)
```

そのため上記のように、データを保存するリソース名（ここでは`:session`）とそのパスを指定ながら、`form_for`でformタグを生成します。

```erb
rb
<%= 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 %>
```


送信されたデータは、

* `params[:session][:email]`

* `params[:session][:password]`

で受け取ることができます。こうしてみると、リソース名（ここでは`:session`）をシンボルで記述する意味がわかりますね。

```rb
"session" => { 
            "email" => "foo@bar.com",
            "password" => "something",  
          }
```

送信されるデータはこういうハッシュになってるということですね。

これが、`login_path`のPOST、つまり`create`アクションに受け渡されるということです。


#8.1.3 ユーザーの検索と認証

`session`コントローラの`create`アクションでparamsからデータを受け取ることができます。

##`render`と`redirect_to`について

`flash`で表示させるエラーメッセージは、`redirect_to`の場合は次のページ移動で消えるのに、rederではページ移動しても消えずに残ったままになってしまう。

renderを使用する場合は`flash.now`を使う。



###renderとは

*  `render`は指定したviewを呼び出します。

```rb
  def new
  end
```

これも実は`render`が動いています。
GETアクション内になにも記述がない場合は、

```rb
  def new
    render 'new'
  end
```

に勝手に補完されています。


###redirect_toとは

*  `redirect_to`はHTTPリクエスト（GET）をサーバーに送ります。

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

わかりにくいですが、ここでもRailsの自動補完が効いているのでした。
`redirect_to`の行は、以下と同じ意味です。

```rb
redirect_to user_url(@user)
```


#8.1.5 フラッシュのテスト

```rb
require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest

  test "login with invalid information" do
    get login_path #1
    assert_template 'sessions/new' #2
    post login_path, params: { session: { email: "", password: "" } } #3
    assert_template 'sessions/new' #4
    assert_not flash.empty? #4 flashはemptyでない
    get root_path #5
    assert flash.empty? #6 flashはemptyである
  end
end
```

1. ログイン用のパスを開く
2. 新しいセッションのフォームが正しく表示されたことを確認する
3. わざと無効なparamsハッシュを使ってセッション用パスにPOSTする
4. 新しいセッションのフォームが再度表示され、フラッシュメッセージが追加されることを確認する
5. 別のページ (Homeページなど) にいったん移動する
6. 移動先のページでフラッシュメッセージが表示されていないことを確認する


#8.2 ログイン

##ApplicationコントローラにSessionヘルパーモジュールを読み込む

ApplicationコントローラにSessionヘルパーモジュールを読み込むと、他の全てのコントローラで使用できるようになります。
それは、他の全てのコントローラが、Applicationコントローラを継承しているからです。


#8.2.1 log_inメソッド

##sessionメソッドについて

```ruby
session[:name]
```

sessionメソッドはRailsにあらかじめ組み込まれているメソッドです。
名前をつけて、セッションに登録できます。

これは、8章冒頭に自分で`rails -g`して作ったSessionsコントローラとは無関係です。

```rb
module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id #user.idを`:user_id`という名前で保存
  end
end
```

どのviewでも使うことができるように、ヘルパーに関数を定義します。
sessionメソッドで作成した一時cookiesは自動的に暗号化されます。すごい。


##ブラウザからcookiesの情報を調べてみてください。の演習について

AWS Cloud9を使っている人は、`amazonaws`でcookies検索すると出るかと思います。



#8.2.2 現在のユーザー

##「例外を発生させる」or「例外を発生させない」

```rb
User.find(session[:user_id]) #例外が発生する
User.find_by(id: session[:user_id]) ##例外が発生しない(nillを返す)
```

* `find`メソッドは例外を発生させて処理を中断する。

* `find_by`メソッドはnilを返すため処理は中断されない。

「ユーザページの表示」の場合は、ユーザーのidが無いのは「例外」です。（表示するページもなにもない）

一方、「ユーザーのログイン」の場合、まだログインしていないなど、クッキーにデータがあったりなかったり、両方の状態が考えられます。そのため、「例外」での処理はしません。

##現在のユーザーを定義する

```rb
  def current_user
    if session[:user_id]
      @current_user ||= User.find_by(id: session[:user_id])
    end
  end
```

「もし、`session[:user_id]`が存在するなら、
`@current_user`が`nill`なら`User.find_by(id: session[:user_id])`を代入する」

```rb
 @current_user ||= User.find_by(id: session[:user_id])
```

上記は、以下と同じです。

```rb
if @current_user.nil?
  @current_user = User.find_by(id: session[:user_id])
else
  @current_user
end
```

まず、`@current_user`が`nil`かどうか（値があるかどうか）チェック。
`@current_user`が`nil`（つまりログインしてない）なら、セッションIDからuserを検索して`@current_user`とします。

もし`@current_user`が`nil`でないなら、すでにログイン中ですので、そのまま`@current_user`を返します。


#8.2.3 レイアウトリンクを変更する

ログインしている場合orしていない場合で、レイアウトを変更します。
view内で、`if`を使って表示する内容を変える。

`if`の条件分岐を使用して、`current_user`がtrueかfalseか（ログインしているか否か）で分岐させます。

ユーザーがログイン中の状態とは「sessionにユーザーidが存在している」こと、つまりcurrent_userがnilではないという状態を指します。

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

ログインしているかどうかを確かめるメソッドをヘルパーに定義して、便利にすぐ呼び出せるようにしておきます。

`true`や`false`など真偽値を返すメソッドは名前に「？」をつけて置くのがルールです。


#リスト 8.21: fixture向けのdigestメソッドを追加する

全く意味がわかりません。誰かたすけてください。




#8.3 ログアウト

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

上記の`delete`メソッドでセッションを削除できます。