ユーザーの更新
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"に
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