アプリを作る　ユーザーの更新

##ユーザーの更新
```
git checkout -b updating-users
```
###app/controllers/users_controller.rb 
```app/controllers/users_controller.rb
class UsersController < ApplicationController
  
  def show
    @user = User.find(params[:id])
    # @user インスタンス変数
    # params[:id]の部分はユーザーidの1に置き換わります
    # Rubyを使ってユーザー名とメールアドレスを表示するにはインスタンス変数であることを前提とするr
  end
 
  def new
    @user = User.new
    # 新しくインスタンスを生成
    # 新規ユーザーを作る
  end
  
  def create
    @user = User.new(user_params) # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
      log_in @user
      # インスタンス変数に変更し、ユーザーページに移動させる
      flash[:success] = "ようこそ自作アプリへ成功しました！"
      redirect_to @user
      # 表示させるユーザーページを
    else
      render 'new'
    end
  end
  
  def edit
    @user = User.find(params[:id])
    # モデル.find(件数..)
      # IDを指定してレコードを取得
      # 指定したIDの中に存在しないIDが1つでもあると例外が発生  
 
    # params[:パラメータ名]
      # URLから送られてきた値やフォームで入力した値
      # リンクによるパラメータの受け渡し,
      # フォームによるパラメータの受け渡し
    
    # idの情報を基に特定のユーザーを見つける
  end
  
  private
  
    def user_params
    # ストロングパラメータ
    # 使用制限をつける　user属性を使うときに
      params.require(:user).permit(:name, :email, :password, 
                                     :password_confirmation)
    end
    
    # ログイン済みユーザーかどうか確認
    def logged_in_user
      unless logged_in?
        store_location
        #アクセスしようとしたURLを覚えておく
        flash[:danger] = "Please log in."
        redirect_to login_url
      end
    end

    def correct_user
    # 正しいユーザーかどうか確認
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end
end
```
###app/views/users/edit.html.erb 　ユーザーの編集のページを作成
```app/views/users/edit.html
.erb

<% provide(:title, "Edit user") %>

<h1>Update your profile</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @user, local: true) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>

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

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

      <%= f.label :password_confirmation, "Confirmation" %>
      <%= f.password_field :password_confirmation, class: 'form-control' %>

      <%= f.submit "Save changes", class: "btn btn-primary" %>
    <% end %>

    <div class="gravatar_edit">
      <%= gravatar_for @user %>
      <a href="https://gravatar.com/emails" target="_blank">change</a>
    </div>
  </div>
</div>
```
###app/views/layouts/_header.html.erb
```app/views/layouts/_header.html
.erb

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "自作アプリ", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "ホーム", root_path %></li>
        <% if logged_in? %>
        　<li><%= link_to "ユーザー", '#' %></li>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
              Account <b class="caret"></b>
            </a>
            <ul class="dropdown-menu">
              <li><%= link_to "プロフィール", current_user %></li>
              <li><%= link_to "設定", edit_user_path(current_user) %></li>
              <!--edit_user_path(current_user) 
                  レイアウトの “Settings” リンクを更新するf-->
              <li class="divider"></li>
              <li>
                <%= link_to "ログアウト", logout_path, method: :delete %>
              </li>
            </ul>
          </li>
        <% else %>
          <li><%= link_to "ログイン", login_path %></li>
        <% end %>
      </ul>
    </nav>
  </div>
</header>
```

###app/controllers/users_controller.rb
```app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:edit, :update]
  # 編集と更新の前にlogged_user_userを行う
  before_action :correct_user,   only: [:edit, :update]

  def show
    @user = User.find(params[:id])
    # @user インスタンス変数
    # params[:id]の部分はユーザーidの1に置き換わります
    # Rubyを使ってユーザー名とメールアドレスを表示するにはインスタンス変数であることを前提とするr
  end
 
  def new
    @user = User.new
    # 新しくインスタンスを生成
    # 新規ユーザーを作る
  end
  
  def create
    @user = User.new(user_params) # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
      log_in @user
      # インスタンス変数に変更し、ユーザーページに移動させる
      flash[:success] = "ようこそ自作アプリへ成功しました！"
      redirect_to @user
      # 表示させるユーザーページを
    else
      render 'new'
    end
  end
  
  def edit
    @user = User.find(params[:id])
  end
  
  def update
    @user = User.find(params[:id])
    # 探す
    if @user.update(user_params)
    # 更新
      # 更新に成功した場合を扱う。
      flash[:success] = "Profile updated"
      # 成功時のメッセージ
      redirect_to @user
      # ユーザーページを表示させる
      # redirect_to(オプション={}, レスポンスオプション={})
        # URLを指定して表示
    else
      render 'edit'
      # 更新できなかった場合　編集前の編集ページに戻る
    end
  end

  private
  
    def user_params
    # ストロングパラメータ
    # 使用制限をつける　user属性を使うときに
      params.require(:user).permit(:name, :email, :password, 
                                     :password_confirmation)
    end
    
    # ログイン済みユーザーかどうか確認
    def logged_in_user
      unless logged_in?
        flash[:danger] = "Please log in."
        redirect_to login_url
      end
    end

    def correct_user
　　　　　　　　# 正しいユーザーかどうか確認
      @user = User.find(params[:id])
      redirect_to(root_url) unless @user == current_user
      # @userがcurrent_userじゃなければホーム画面に移動する
    end
end
```
###編集のページの失敗のテストを作成
```
ubuntu:~/environment/my_app (updating-users) $ rails generate integration_test users_edit
Running via Spring preloader in process 5960
      invoke  test_unit
      create    test/integration/users_edit_test.rb
```

###test/integration/users_edit_test.rb
```test/integration/users_edit_test.rb
require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

  def setup
  # テストユーザーを使う
    @user = users(:michael)
  end

  test "unsuccessful edit" do
  # 失敗してもまた編集ぺージが表示されていることを確かめるテスト
    log_in_as(@user)
    # ログインする
    get edit_user_path(@user)
    # ユーザーの編集ページに行くことを要求する
    assert_template 'users/edit'
    # 編集ページを表示さえれていることを確かめる
    # 表示されていたら次の行動
    patch user_path(@user), params: { user: { name:  "",
                                              email: "foo@invalid",
                                              password:              "foo",
                                              password_confirmation: "bar" } }
    # 以上の情報を入力する
    assert_template 'users/edit'
    # 入力に失敗しているのでまた編集ページが表示されていることを確かめる
  end

  test "successful edit" do
  # 編集成功時の動作のテスト
    log_in_as(@user)
    # ログインする
    get edit_user_path(@user)
    # 編集ページに行くことを要求
    assert_template 'users/edit'
    # 編集ページが表示されたことを確かめる
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    # 以上の情報を入力する
    assert_not flash.empty?
    # フラッシュが存在するか？　　存在すれば次
    assert_redirected_to @user
    # ユーザーページがリダイレクト先になっているかどうかを確かめる
      # リダイレクトとはサイトやページなどを新しいURLに変更した際、自動的に転送をする仕組みのこと。
    # assert_redirected_to(オプション={}, メッセージ=nil)
      # リダイレクト先がどうなっているか確認
    @user.reload
    # モデル.reload(オプション=nil)
      # レコードを再取得
    assert_equal name,  @user.name
    # 上のnameと@user.nameが一緒かどうか確かめる
    assert_equal email, @user.email
  end
  
  test "successful edit with friendly forwarding" do
　　　
    get edit_user_path(@user)
    # 編集ページに移動を要求
    log_in_as(@user)
    # ログインさせる
    assert_redirected_to edit_user_url(@user)
    # 編集ページに移動する
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    # 情報を入力
    assert_not flash.empty?
    # flashが空じゃないか確認
    assert_redirected_to @user
    # ユーザーページがどうなっているか確認
    # assert_redirected_to(オプション={}, メッセージ=nil)
      # リダイレクト先がどうなっているか確認
    @user.reload
    # モデル.reload(オプション=nil)
      # レコードを再取得
    assert_equal name,  @user.name
    assert_equal email, @user.email
end
```
###app/models/user.rb
```app/models/user.rb
class User < ApplicationRecord
# 継承させる
  attr_accessor :remember_token
  before_save { self.email = email.downcase }
  # 保存する前に　メアドは小文字にする
  validates :name, presence: true, length: { maximum: 50 }
  # nameカラムが存在する
  # validates(:name, presence: true)
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  # 正規表現
  validates :email, presence: true, length: { maximum: 255 },
                    # 存在性、長さの検証
                    format: { with: VALID_EMAIL_REGEX },
                    # メールフォーマットの検証
                    uniqueness: { case_sensitive: false } 
                    # :case_sensitive	大文字と小文字を区別しないで一意性を検証する
  has_secure_password
  # 仮想的なpassword属性とpassword_confirmation属性に対してバリデーションをする機能も（強制的に）追加
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
  # 最低でも六文字異常 存在性のある文字列をパスワードととする。
  # 渡された属性が存在していることを検証する
  # allow_nil: true 
    # 対象の値がnilの場合にバリデーションをスキップ
  # 渡された文字列のハッシュ値を返す
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    # コストパラメータはテストでは最小にする
    BCrypt::Password.create(string, cost: cost)
    # stringはハッシュ化する文字列
    # cost ハッシュ化する計算コストを表す
  end
  
  # ランダムなトークンを返す
  def User.new_token
    SecureRandom.urlsafe_base64
  end
  
  # 永続セッションのためにユーザーをデータベースに記憶する
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end
  
  # 渡されたトークンがダイジェストと一致したらtrueを返す
  def authenticated?(remember_token)
    return false if remember_digest.nil?
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end
  
  # ユーザーのログイン情報を破棄する
  def forget
    update_attribute(:remember_digest, nil)
  end
end
```
####成功、失敗しても編集ページに行かないようになっている
```
ubuntu:~/environment/my_app (updating-users) $ rails t
Running via Spring preloader in process 7508
Run options: --seed 17621

# Running:

.........F

Failure:
UsersEditTest#test_successful_edit [/home/ubuntu/environment/my_app/test/integration/users_edit_test.rb:22]:
expecting <"users/edit"> but rendering with <[]>


rails test test/integration/users_edit_test.rb:20

F

Failure:
UsersEditTest#test_unsuccessful_edit [/home/ubuntu/environment/my_app/test/integration/users_edit_test.rb:11]:
expecting <"users/edit"> but rendering with <[]>


rails test test/integration/users_edit_test.rb:9

.............

Finished in 2.401827s, 9.9924 runs/s, 22.4829 assertions/s.
24 runs, 54 assertions, 2 failures, 0 errors, 0 skips
```
そのためにtest "unsuccessful edit" ,"test_successful_edit"に 

```rb
log_in_as(@user)
```
を追加してログインさせる

###test/controllers/users_controller_test.rb
```test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest
  
  def setup
  # テストユーザー
    @user = users(:michael)
    @other_user = users(:archer)
  end
  
  test "should get new" do
    get signup_path
    # users/newでgetリクエスト
    assert_response :success
    # リクエストを受けることができましたか？
  end
  
  test "should redirect edit when not logged in" do
  # ログインなしでユーザー情報を編集させないテスト
    get edit_user_path(@user)
    # 編集ページに行くことを要求
    assert_not flash.empty?
    # flashの中身はあるか確認？
    assert_redirected_to login_url
    # ログインページに移動する
  end

  test "should redirect update when not logged in" do
  # ログインなしでユーザー情報を更新させないテスト
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
    # 以上の情報を入力してユーザーページを更新
    assert_not flash.empty?
    # flashに中身はあるか？
    assert_redirected_to login_url
    # ログインページに移動させる
  end

  test "should redirect edit when logged in as wrong user" do
  # テストユーザーが間違えているからホーム画面に行くのかな？
    log_in_as(@other_user)
    get edit_user_path(@user)
    assert flash.empty?
    assert_redirected_to root_url
  end

  test "should redirect update when logged in as wrong user" do
    log_in_as(@other_user)
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
    assert flash.empty?
    assert_redirected_to root_url
  end
end
```
###test/fixtures/users.yml テストユーザーの追加
```test/fixtures/users.yml
michael:
# テストデータ
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  
archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
```

###app/helpers/sessions_helper.rb
```app/helpers/sessions_helper.rb
module SessionsHelper
  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
    # sessionはログインを表す
  end

  # ユーザーのセッションを永続的にする
  def remember(user)
    user.remember
    cookies.permanent.signed[:user_id] = user.id
    cookies.permanent[:remember_token] = user.remember_token
  end
  
  # 記憶トークンcookieに対応するユーザーを返す
  def current_user
    if (user_id = session[:user_id])
      @current_user ||= User.find_by(id: user_id)
    elsif (user_id = cookies.signed[:user_id])
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        log_in user
        @current_user = user
      end
    end
  end
  
  def current_user?(user)
  # 渡されたユーザーがカレントユーザーであればtrueを返す
    user && user == current_user
    # ユーザーが有効且つユーザーがログイン中にする
  end
  
  # ユーザーがログインしていればtrue、その他ならfalseを返す
  def logged_in?
  # これで判定させて表示内容を決める
    !current_user.nil?
    # ログイン中のユーザーは空じゃないか確認
  end
  
  # 現在のユーザーをログアウトする
  def log_out
    session.delete(:user_id)
    # sessionからidを削除する
    @current_user = nil
    # ログイン済みのユーザーを空にする
  end
  # 永続的セッションを破棄する
  def forget(user)
    user.forget
    cookies.delete(:user_id)
    cookies.delete(:remember_token)
  end

  # 現在のユーザーをログアウトする
  def log_out
    forget(current_user)
    session.delete(:user_id)
    @current_user = nil
  end

  # 記憶したURL（もしくはデフォルト値）にリダイレクト
  def redirect_back_or(default)
    redirect_to(session[:forwarding_url] || default)
    # redirect_to(オプション={}, レスポンスオプション={})
      # URLを指定して表示
    session.delete(:forwarding_url)
    # 
  end

  # アクセスしようとしたURLを覚えておく
  def store_location
    session[:forwarding_url] = request.original_url if request.get?
    # アクセスの要求をしたものを:forwarding_urlとするのかな？
    # original_url　アクセスするURL
    # request.メソッド()
      # リクエストを送ってきたユーザのヘッダー情報や環境変数を取得
  end
end
```
###app/controllers/sessions_controller.rb
```app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
  def new
  # 新しいセッションのページ（ログイン）
  end
  
  def create
    user = User.find_by(email: params[:session][:email].downcase)
    # 小文字にする
    if user && user.authenticate(params[:session][:password])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
      # 有効且つパスワードを認識する
      log_in user
      # ログインさせる
      params[:session][:remember_me] == '1' ? remember(user) : forget(user)
      redirect_back_or user
      #
    else
      flash.now[:danger] = 'Invalid email/password combination' # 本当は正しくない
      # エラーメッセージを作成する
      #.nowで何かすると消えるようにする
      render 'new'
    end
  end

  def destroy
  # セッションの削除（ログアウト）
    log_out if logged_in?
    redirect_to root_url
    # 削除した後 ホーム画面に戻る
  end
end
```