SINKCAPITAL
SINKCAPITAL
company Blog
Ruby on Rails チュートリアル第8章をやってみて
techinternlearning

基本的なログイン機構

■第8章
基本的なログイン機構を構築していく。

8.1 セッション

HTTPは状態のないプロトコル。HTTPのリクエスト1つ1つは、それより前のリクエストの情報をまったく利用できない、独立したトランザクションとして扱われる。

ログインするとcreateでセッションを作成して保存。 ログアウトするとdestroyでセッションを破棄。

8.1.1 Sessionsコントローラ

ログインとログアウトの要素を、Sessionsコントローラの特定のRESTアクションにそれぞれ対応付ける。

セッションコントローラの作成。

$ rails generate controller Sessions new

リソースを追加して標準的なRESTfulアクションをgetできるようにする。 以下のコードを追記。

get    '/login',   to: 'sessions#new'
post   '/login',   to: 'sessions#create'
delete '/logout',  to: 'sessions#destroy'

Sessionsコントローラのテストで名前付きルートを使うようにする。

require 'test_helper'

class SessionsControllerTest < ActionDispatch::IntegrationTest

  test "should get new" do
    get login_path
    assert_response :success
  end
end

8.1.2 ログインフォーム

新しいセッションフォームを作成するときには、form_forヘルパーに追加の情報を独自に渡さなければならない。 ログインフォームのコード

<% 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>

8.1.3ユーザーの検索と認証

ログインでセッションを作成する場合、入力が無効な場合の処理を最初に行う。

最小限のcreateアクションをSessionsコントローラで定義し、空のnewアクションとdestroyアクションも作成する。

class SessionsController < ApplicationController

  def new
  end

  def create
    render 'new'
  end

  def destroy
  end
end

ユーザー認証に必要なあらゆる情報をparamsハッシュから簡単に取り出せる。

Active Recordが提供する User.find_byメソッドでデータベースからユーザーを探し、has_secure_passwordが提供する authenticateメソッドでパスワードをチェックする。authenticateメソッドは認証に失敗したときにfalseを返す。

最終的にはこんな感じに。

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
      # エラーメッセージを作成する
      render 'new'
    end
  end

  def destroy
  end
end

8.1.4 フラッシュメッセージを表示する

セッションではActive Recordのモデルを使っていないので、ログインに失敗したときには代わりにフラッシュメッセージを表示する。

ここで書いたコードは誤りで、フラッシュメッセージが消えない状態になるので、一旦次へ。

8.1.5 フラッシュのテスト

統合テストを作成する。

$ rails generate integration_test users_login

以下の流れでテストコードを実装していく

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

フラッシュメッセージの残留をキャッチするテスト。

require 'test_helper'

class UsersLoginTest < ActionDispatch::IntegrationTest

  test "login with invalid information" do
    get login_path
    assert_template 'sessions/new'
    post login_path, params: { session: { email: "", password: "" } }
    assert_template 'sessions/new'
    assert_not flash.empty?
    get root_path
    assert flash.empty?
  end
end

このままだとテスト失敗になるので、テストをパスさせるに、本編のコードでflashflash.nowに置き換える。後者はその後リクエストが発生したときに消滅する。

8.2 ログイン

cookiesを使った一時セッションでユーザーをログインできるようにする。このcookiesは、ブラウザを閉じると自動的に有効期限が切れるものを使う。

ほんとはセッションの実装はめちゃくちゃ大変だけど、Rubyのモジュール機能使えば楽にできる。

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end

8.2.1 log_inメソッド

同じログイン手法を様々な場所で使いまわせるようにするために、 sessionsヘルパーにlog_inという名前のメソッドを定義。

ログインメソッド

module SessionsHelper

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

上から3行目のコードで、ユーザーのブラウザ内の一時cookiesに暗号化済みのユーザーIDが自動で作成される。

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
    else
      flash.now[:danger] = 'Invalid email/password combination'
      render 'new'
    end
  end

  def destroy
  end
end

createアクションを完了し、ユーザーのプロフィールページにリダイレクトする準備ができた。

8.2.2 現在のユーザー

ユーザーIDを一時セッションの中に安全に置けるようになったので、今度は、ユーザーIDを別のページで取り出すことにする。

current_userメソッドを定義して、セッションIDに対応するユーザー名をデータベースから取り出せるようにする。

ユーザーIDが存在しない状態でfindを使うと例外が発生するので、find_byメソッドを使う。

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
end

current_userメソッドが動作するようになったので、ユーザーがログインしているかどうかに応じてアプリケーションの動作を変更するための準備ができた。

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

ユーザーがログイン中の状態とは「sessionにユーザーidが存在している」こと、つまりcurrent_usernilではないという状態を指すので、否定演算子!を使ってチェックをしていく。

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

8.2.4 レイアウトの変更をテストする

以下の手順でテストを作成する。 ・ログイン用のパスを書く

・セッション用パスに有効な情報をpostする

・ログイン用リンクが表示されなくなったことを確認する

・ログアウト用リンクが表示されていることを確認する

・プロフィール用リンクが表示されていることを確認する

上の変更の確認のため、テスト時に登録済みユーザーとしてログインしておく必要がある。

テスト用データをfixture (フィクスチャ) で作成できる。現時点のテストでは、ユーザーは1人いれば十分。

fixture向けのdigestメソッドを追加する。

以下のコードを追加。

# 渡された文字列のハッシュ値を返す
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end
end

digestメソッドができたので、有効なユーザーを表すfixtureを作成できるようになった。

ユーザーのログインで使うfixture。

michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>

このコードでテストユーザー用の有効なパスワードが作成できる。

<%= User.digest('password') %>

fixtureは生のパスワードを参照できないので、テスト用のfixtureでは全員同じパスワード「password」にする。

assert_redirected_to @user

上のコードはリダイレクト先が正しいかどうかをチェックしている。

テストでも問題ないことが確認できた。

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

ユーザー登録中にログインを済ませておくことにする。Usersコントローラのcreateアクションにlog_inを追加するだけで済むらしい。

8.3 ログアウト

ログアウト機能を追加していく。ログアウト用リンクは作成済なので、ユーザーセッションを破棄するための有効なアクションをコントローラで作成する済む。

log_outメソッド。以下を追記。

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

2行目のコードで、現在のユーザーをnilに設定できる。

ここで定義したlog_outメソッドは、Sessionsコントローラのdestroyアクションでも同様に使っていく。

以下を追記。

# 現在のユーザーをログアウトする
def destroy
   log_out
   redirect_to root_url
end

テストの内容も弄って、無事テストGREENに。

感想

ログイン・ログアウト機能の実装はバックエンドよりの作業なのかな? HTTPだったりcookiesの話は応用情報の時にもちょっと触ったので懐かしいですね。 フワフワしたまま進んでいっていますが、頑張ります。

BQにおけるSQL検算を効率化する無料chrome拡張機能をリリースいたしました
櫻井 裕司
2022/09/01 櫻井 裕司
tech
BigQueryのjoin句を含むstandardSQLを入力することで、join前後でのレコード数の変化を返すSQLを自動でクリップボードにコピーする無料chrome拡張機能をリリースいたしました。
社内ドキュメントにNotionを導入して感じた事
櫻井 裕司
2022/04/02 櫻井 裕司
tech
社内ドキュメントをNotionに寄せることで見えてきたメリット・デメリットをまとめていきたいと思います。また使う中で感じたいくつかの要望もまとめていこうと思います。
「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第3章 BIツールに関する知識をつける
白井 透
2022/03/31 白井 透
techinternlearning
【「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第3章】現在長期インターンをさせてもらっているSinkCapitalさんの方で、データ系の業務に携わることになりそうなのですが、それの準備期間として紹介していただいた本をまとめていきたいと思います。
「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第2章 さまざまな分析をしてみよう
白井 透
2022/03/30 白井 透
techinternlearning
【「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第2章】現在長期インターンをさせてもらっているSinkCapitalさんの方で、データ系の業務に携わることになりそうなのですが、それの準備期間として紹介していただいた本をまとめていきたいと思います。
「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第1章 分析ダッシュボードを作ってみよう
白井 透
2022/03/29 白井 透
techinternlearning
【「BIツール」活用 超入門 Google Data Portalではじめるデータ集計・分析・可視化 第1章】現在長期インターンをさせてもらっているSinkCapitalさんの方で、データ系の業務に携わることになりそうなのですが、それの準備期間として紹介していただいた本をまとめていきたいと思います。
Ruby on Rails チュートリアル第14章をやってみて & まとめ
白井 透
2022/02/20 白井 透
techinternlearning
【Ruby on rails 第14章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第13章をやってみて
白井 透
2022/02/20 白井 透
techinternlearning
【Ruby on rails 第13章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第12章をやってみて
白井 透
2022/02/19 白井 透
techinternlearning
【Ruby on rails 第12章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第11章をやってみて
白井 透
2022/02/19 白井 透
techinternlearning
【Ruby on rails 第11章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第10章をやってみて
白井 透
2022/02/18 白井 透
techinternlearning
【Ruby on rails 第10章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第9章をやってみて
白井 透
2022/02/16 白井 透
techinternlearning
【Ruby on rails 第9章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第7章をやってみて
白井 透
2022/02/14 白井 透
techinternlearning
【Ruby on rails 第7章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第6章をやってみて
白井 透
2022/02/13 白井 透
techinternlearning
【Ruby on rails 第6章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第5章をやってみて
白井 透
2022/02/12 白井 透
techinternlearning
【Ruby on rails 第5章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第4章をやってみて
白井 透
2022/02/11 白井 透
techinternlearning
【Ruby on rails 第4章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第3章をやってみて
白井 透
2022/02/08 白井 透
techinternlearning
【Ruby on rails 第3章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第2章をやってみて
白井 透
2022/02/07 白井 透
techinternlearning
【Ruby on rails 第2章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Ruby on Rails チュートリアル第1章をやってみて & 自己紹介
白井 透
2022/02/07 白井 透
techinternlearningpersonal
【Ruby on rails 第1章】インターン先の方から、「これやっとけば、だいぶいい感じだよ!」と言われたので、Ruby on railsのチュートリアルをやってみたいと思います。
Nuxt上でのd3を利用した散布図の作成方法
櫻井 裕司
2021/10/29 櫻井 裕司
techdataAnalytics
クリック可能な散布図をNuxtjs上で作成する場合にd3.jsが汎用性が高く便利でした。利用するにあたって難しかった点などを備考録としてまとめています。
アクセスログを可視化しGAのデータを直感的に理解できる型態にする試み(ネットワーク型)
櫻井 裕司
2021/09/05 櫻井 裕司
techdataAnalytics
ビジネスに活きる分析を進める上で弊社では「理解できる」ことを重要と考えており、特に直感的理解は可視化を進める上で特に重要だと考える内容の一つです。弊社では様々なお客様のデータ分析を進める上で常により示唆の大きい可視化を追求しており、今回はその中で最近試みているネットワーク側の可視化についてまとめたいと思います。
代表櫻井による特別講演会が白陵高等学校で開かれました
櫻井 裕司
2021/07/31 櫻井 裕司
eventpersonal
2021年の夏に兵庫県の私立白陵高等学校において、代表櫻井による特別講演会を開催いたしました。今振り返って高校の頃の自分に伝えたいことについてお話しました。
Nuxtで動的ページを随時追加する場合にNot Foundとなる
櫻井 裕司
2021/05/31 櫻井 裕司
tech
Nuxtで動的ページを登録する方法はありますが、登録後に随時コンテンツが追加される際はNot Foundとなってしまうので、そう言った際の対処方法について
GKEをやめてCloud Runを始めてみました
櫻井 裕司
2021/04/19 櫻井 裕司
tech
firebaseで構築したシステムの裏で動かすバッチの負荷が大きく、cloud functionsで終わらなかったためCloud Runを利用してみました。動作確認までの知見等を雑多にまとめてみました。
AWSをやめてfirebaseを使い始めて感じたメリットやデメリットとそれの対応策(LT登壇内容)
櫻井 裕司
2021/03/26 櫻井 裕司
techeventpersonal
みそかつウェブ・GDG Nagoya主催の「around firebase」とCloud Native Nagoya主演の「Cloud Native Nagoya」にてfirebaseのLTをさせていただきました。そこで会話させていただいたfirebaseを使い始めて感じたメリット・デメリットについてまとめています。
PWA+SPAのwebアプリ作成にnuxtjs+firebaseがめちゃ便利だった
櫻井 裕司
2021/01/16 櫻井 裕司
tech
PWA+SPAのwebアプリを作る際にnuxt.js+firebaseを合わせて利用すると便利だったので知見を書き留めています
s3のhostingでPWAを導入する方法
櫻井 裕司
2020/12/19 櫻井 裕司
tech
アプリ作成時にpwaが比較されることが多かったですが、実際にpwaを実装した経験がなかったため今回自社サイトをPWA化してみました。
dockerでseleniumを動かしてみる(chrome_headless)
櫻井 裕司
2020/12/06 櫻井 裕司
tech
seleniumの相談をいただくことが増えたため、seleniumの勉強もかねてdockerでの実行テストを行いました
THE DECKのイベントにお邪魔させていただきました
本林 秀和
2020/12/05 本林 秀和
eventpersonal
大学コンソーシアム大阪のイベント@The DECK にお邪魔してきました
flutter(dart)を触ってみた感想
櫻井 裕司
2020/11/18 櫻井 裕司
tech
android向けアプリへの対応も考慮してflutter(dart)を触ってみたので、感想をまとめておこうと思います。理解が深まっていく中で定期的にまとめていければと思います。
代表本林による特別講演会が滝高校で開かれました
本林 秀和
2020/11/07 本林 秀和
eventpersonal
2020年11月7日(土)愛知県の私立滝高校において、代表本林による特別講演会を開催いたしました。IT業界やデータサイエンスについてお話しました。
AWS・GCPを選ぶ際の観点
櫻井 裕司
2020/10/28 櫻井 裕司
tech
AWSかGCPを選ぶ際の観点について書き留めておこうと思います
CloudFormationとterraformの比較
櫻井 裕司
2020/10/04 櫻井 裕司
tech
AWS CloudFormationとterraformの両方を使ってみて感じた違いをまとめてみました。
iosのcallkit周りでできること
櫻井 裕司
2020/08/24 櫻井 裕司
tech
新規事業を検討する上でios(swift)の電話周りでできることを調査したため、調査結果をブログとして残しています。
【個人ブログ】CTOの株運用ブログ_順調な滑り出し
櫻井 裕司
2020/07/19 櫻井 裕司
personalstock
長年放置してた株に少し手を出してみました。自分なりに少し情報整理と分析と予想をしたので記事にしてみます。
総務省特定サービス産業実態調査のデータ分析
櫻井 裕司
2020/07/18 櫻井 裕司
techdataAnalytics
総務省がAPIで市場データを公開しており、分析技術向上と市場感を養うことを目的に定期的に分析を行なっていこうと思います。今回は「特定サービス産業実態調査」について見ていこうと思います。
「お絵かきつみ木バトル」をリリースしました
櫻井 裕司
2020/07/12 櫻井 裕司
techapp
タスク管理を二次元的に行うアプリ「お絵かきつみ木バトル」をリリースしました。SinkCapitalはデータコンサルですが、知見蓄積のため様々な媒体での実験的開発を行っています
総務省工業統計調査のデータ分析
櫻井 裕司
2020/07/11 櫻井 裕司
techdataAnalytics
総務省がAPIで市場データを公開しており、分析技術向上と市場感を養うことを目的に定期的に分析を行なっていこうと思います。今回は「工業統計調査」について見ていこうと思います。
総務省サービス産業動向調査のデータ分析
櫻井 裕司
2020/07/08 櫻井 裕司
techdataAnalytics
総務省がAPIで市場データを公開しており、分析技術向上と市場感を養うことを目的に定期的に分析を行なっていこうと思います。初回は「サービス産業動向調査」について見ていこうと思います。
【個人ブログ】CTOが個人的に株をはじめました
櫻井 裕司
2020/07/08 櫻井 裕司
personalstock
長年放置してた株に少し手を出してみました。自分なりに少し情報整理と分析と予想をしたので記事にしてみます。
タスク管理アプリ「タスククロス」をリリースしました
櫻井 裕司
2020/04/08 櫻井 裕司
techapp
タスク管理を二次元的に行うアプリ「タスククロス」をリリースしました。SinkCapitalはデータコンサルですが、知見蓄積のため様々な媒体での実験的開発を行っています
【terraform】gcpでcicd環境を構築する方法
櫻井 裕司
2020/01/04 櫻井 裕司
tech
企業サイトはAWSを利用しているのですが、要件によってはGCPの方が適している場合もあるため、GCPでのcicd構築も行いました。AWSと比較しつつ説明しているため是非ご参考にしてみてください。
【合格体験記】GCP_Cloud_Archtectに受かりました
櫻井 裕司
2019/12/23 櫻井 裕司
personalqualification
Google Professional Cloud Architectに合格したので、勉強法別のコスパをまとめてみました。
AWSでサブドメインなし(wwwなし)からサブドメインあり(wwwあり)へのリダイレクト設定
櫻井 裕司
2019/12/23 櫻井 裕司
tech
もともと企業サイトがサブドメインありで公開していたが、サブドメインなしでもエラーなく接続できるように設計。terraformで作成しているので是非ご参考ください。
マークダウンで記事を書けるようにしてみた
櫻井 裕司
2019/12/16 櫻井 裕司
tech
ホームページのブログをマークダウンを使用してかけるようにしました。gatsbyなどもありますが、今回はお手製cicd+pythonを使用してライトに作成しました。
Copyright © SinkCapital 2022
一緒に働きたい方はこちら