0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

アプリを作る 基本的ログイン機構

Posted at

###ログイン機能を作る

####そのためにコントローラを作る

ubuntu:~/environment/my_app (basic-login) $ rails generate controller Sessions new
Running via Spring preloader in process 15621
      create  app/controllers/sessions_controller.rb
       route  get 'sessions/new'
      invoke  erb
      create    app/views/sessions
      create    app/views/sessions/new.html.erb
      invoke  test_unit
      create    test/controllers/sessions_controller_test.rb
      invoke  helper
      create    app/helpers/sessions_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/sessions.scss

####ログイン機能のためのルーティング
config/routes.rb

Rails.application.routes.draw do
  get 'sessions/new'
  root 'static_pages#home'
  get  '/signup',  to: 'users#new'
  # signupのurlを入力してuser/newのアクションが動き、htmlが表示される。
  get    '/login',   to: 'sessions#new' <-
  # newアクション
  post   '/login',   to: 'sessions#create' <-
  # creteアクション
  delete '/logout',  to: 'sessions#destroy' <-
  # destroyアクション
  resources :users
  # ユーザー情報を表示するURL(/users/1)を追加するためだけのものではありません
  # ユーザーのURLを生成するための多数の名前付きルートと共に、
  #   アクションが利用できるようになるのです
end

####セッションにアクションできることをアクセスすることを
test/controllers/sessions_controller_test.rb

require 'test_helper'

class SessionsControllerTest < ActionDispatch::IntegrationTest
  test "should get new" do
    get login_path
    # 新しいセッションのページ(ログイン)をgetする
    assert_response :success
    # getリクエストが成功したか確認
  end
end

####そしてセッションページを作成

<% provide(:title, "Log in") %>
<h1>ログイン</h1>
<!--セッションページ-->

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(url: login_path, scope: :session, local: true) 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>

####そこからログイン機能をつける
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])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
      # 有効且つパスワードを認識する
    else
      flash.now[:danger] = 'Invalid email/password combination' # 本当は正しくない
      # エラーメッセージを作成する
      #.nowで何かすると消えるようにする
      render 'new'
    end
  end

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

####統合テストのファイルを生成

ubuntu:~/environment/my_app (basic-login) $ rails generate integration_test users_login
Running via Spring preloader in process 4690
      invoke  test_unit
      create    test/integration/users_login_test.rb

####ログインのテストを書く
test/integration/users_login_test.rb

require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest
  test "login with invalid information" do
    get login_path
    # /loginとリクエストする
    assert_template 'sessions/new'
    # loginページが表示されていることを確認
    post login_path, params: { session: { email: "", password: "" } }
    # 無効なデータでログインさせる
    assert_template 'sessions/new'
    # そうするとログインページに戻らせる
    assert_not flash.empty?
    # エラーメッセージが表示されるか確認
    get root_path
    # .comと入力し、リクエストする
    assert flash.empty?
    # ホーム画面に行くとエラーメッセージが消えているか確認
  end
end

####ApplicationコントローラにSessionヘルパーモジュールを読み込む
app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  include SessionsHelper
  # どのコントローラでも使えるようにする
end

####ログインさせるメソッド
app/helpers/sessions_helper.rb

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

####ユーザーをログインさせる
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
      # ログインさせる
      redirect_to user
      # user_url(user)を表す
      # プロフィールページへのルーティングにしています。
    else
      flash.now[:danger] = 'Invalid email/password combination' # 本当は正しくない
      # エラーメッセージを作成する
      #.nowで何かすると消えるようにする
      render 'new'
    end
  end

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

####現在ログイン中のユーザーとする

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

  # 現在ログイン中のユーザーを返す(いる場合)
  def current_user
    if session[:user_id]
    # sessionsに含まれるなら
      @current_user ||= User.find_by(id: session[:user_id])
      # それを検索し、絵ログイン中のユーザーcurrent_userとする
    end
  end
end

####ログイン中であるかないかでレイアウトを変更する

<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 "Home", root_path %></li>
        <% if logged_in? %>
        <!--ログイン中だったら-->
          <li><%= link_to "Users", '#' %></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 "Profile", current_user %></li>
             <!--リンク ユーザーページを表示させる-->
              <li><%= link_to "Settings", '#' %></li>
              <li class="divider"></li>
              <li>
                <%= link_to "Log out", logout_path, method: :delete %>
                <!--ログアウトのリンクもつける-->
              </li>
            </ul>
          </li>
        <% else %>
        <!--ログインしていなかったら-->
          <li><%= link_to "Log in", login_path %></li>
          <!--ログインしていなかったらログインページに移動させる-->
        <% end %>
      </ul>
    </nav>
  </div>
</header>

####パスワードをハッシュ化させる
app/models/user.rb

class User < ApplicationRecord
.
.
.
  # 渡された文字列のハッシュ値を返す
  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
end

####ユーザーログインのテストで使うfixture

michael:
# テストデータ
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>

####ユーザーログインをテストする

require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest
  
  def setup
  # テストユーザー
    @user = users(:michael)
  end
  
  test "login with invalid information" do
    get login_path
    # /loginとリクエストする
    assert_template 'sessions/new'
    # loginページが表示されていることを確認
    post login_path, params: { session: { email: "", password: "" } }
    # 無効なデータでログインさせる
    assert_template 'sessions/new'
    # そうするとやり直しのページに戻らせる
    assert_not flash.empty?
    # エラーメッセージが表示されるか確認
    get root_path
    # .comと入力し、リクエストする
    assert flash.empty?
    # ホーム画面に行くとエラーメッセージが消えているか確認
  end
end

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

####ユーザー登録中にログインする
app/controllers/users_controller.rb

.
.
.
def create
    @user = User.new(user_params) # 実装は終わっていないことに注意!
    if @user.save
      # 保存の成功をここで扱う。
      log_in @user
       # インスタンス変数に変更し、ユーザーページに移動させる
      flash[:success] = "ようこそ自作アプリへ成功しました!"
      redirect_to @user
      # 表示させるユーザーページを
    else
      render 'new'
    end
end
.
.
.

####テスト中のログインステータスを論理値で返すメソッド

ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'

class ActiveSupport::TestCase
  # Run tests in parallel with specified workers
  parallelize(workers: :number_of_processors)

  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all
  
  # テストユーザーがログイン中の場合にtrueを返す
  def is_logged_in?
    !session[:user_id].nil?
    # ログイン中であるかどうかを検索中
  end
  # Add more helper methods to be used by all tests here...
end

####テストユーザーを使い、ログインのテスト
test/integration/users_signup_test.rb

require 'test_helper'

class UsersSignupTest < ActionDispatch::IntegrationTest
.
.
.
  test "valid signup information" do
    get signup_path
    # サインアップページへアクセスする
    assert_difference 'User.count', 1 do
    # ユーザーが一つ追加された
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
      # 有効な情報を登録
    end
    follow_redirect!
    # POSTリクエストを送信した結果を見て、指定されたリダイレクト先に移動するメソッド
    assert_template 'users/show'
    assert is_logged_in?
    # ログイン中であるか?
  end
end

####ログアウトメソッドを作る
app/helpers/sessions_helper.rb

module SessionsHelper
.
.
.
  # 現在のユーザーをログアウトする
  def log_out
    session.delete(:user_id)
    # sessionからidを削除する
    @current_user = nil
    # ログイン済みのユーザーを空にする
  end
end

####そのメソッドを使い、ログアウトの一連の流れを入力する
app/controllers/sessions_controller.rb

class SessionsController < ApplicationController
.
.
.
  def destroy
  # セッションの削除(ログアウト)
    log_out
    # sessions_helperでメソッドから使う
    # セッションとログインを削除する
    redirect_to root_url
    # 削除した後 ホーム画面に戻る
  end
end
require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest
  
  def setup
  # テストユーザー
    @user = users(:michael)
  end
  
  test "login with valid email/invalid password" do
  # ログインをするまでの過程をチェック
    get login_path
    # 名前付きルートでログイン画面に行く
    assert_template 'sessions/new'
    # 要求されたログイン画面が表示されたか?
    post login_path, params: { session: { email:    @user.email,
                                          password: "invalid" } }
    # ログイン画面に無効なテストデータを入力
    assert_not is_logged_in?
    # test_helper ログイン中でない
    assert_template 'sessions/new'
    # 失敗したらまたログインページが表示されるか?
    assert_not flash.empty?
    # flashメッセージは表示されたか?
    get root_path
    # ホーム画面に行くことを要求する?
    assert flash.empty?
    # flashメッセージは表示されなかったか?
  end

  test "login with valid information followed by logout" do
    get login_path
    post login_path, params: { session: { email:    @user.email,
                                          password: 'password' } }
    assert is_logged_in?
    # ログイン中であるか?
    assert_redirected_to @user
    # リダイレクト先がユーザーページであるかどうか?
    follow_redirect!
    # 意味がわからない
    assert_template 'users/show'
    # ユーザーページが表示されているか?
    assert_select "a[href=?]", login_path, count: 0
    # ログイン済みなのでログインリンクが表示されないか?
    assert_select "a[href=?]", logout_path
    # その代わりログアウトリンクが表示されているか?
    assert_select "a[href=?]", user_path(@user)
    # 
    delete logout_path
    assert_not is_logged_in?
    assert_redirected_to root_url
    follow_redirect!
    assert_select "a[href=?]", login_path
    assert_select "a[href=?]", logout_path,      count: 0
    assert_select "a[href=?]", user_path(@user), count: 0
  end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?