LoginSignup
0
1

More than 5 years have passed since last update.

railsチュートリアル 第八章

Last updated at Posted at 2019-03-24

はじめに

railsチュートリアルで理解しにくいところや、詰まったところを書いていく記事になります。
なので、手順を示す記事とはなっていません。

セッション

HTTPはステートレス (Stateless) なプロトコルであり、ブラウザのあるページから別のページに移動したときに、ユーザーのIDを保持しておく手段がHTTPプロトコル内にはまったくない。

よって、ユーザーログインの必要なwebアプリケーションではセッション (Session)と呼ばれる半永続的な接続をコンピュータ間 (ユーザーのパソコンのWebブラウザとRailsサーバーなど) に別途設定する必要がある。

セッションコントローラ

まずはログインのフォームをnewアクションで処理するような、コントローラを作成する。

$ rails generate controller Sessions new

createdestroy といったアクションも扱うが、ビューファイルが必要ないため、ここでは newアクション だけを一緒に生成しておく。

routes.rbでは、Usersリソースのように resourcesメソッド を使ってRESTfulなルーティングを自動的にフルセットで利用することはないので、「名前付きルーティング」だけを使う。

routes.rb
Rails.application.routes.draw do
  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  #追加
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

  resources :users
end

このように名前付きルーティングが増えてきた場合は、以下のコマンドを打つことで現状のルーティングを確認することができる。

$ rails routes

ログインフォーム

コントローラとルーティングを定義した次はビューファイルを整えていく。

ログインフォームでは、会員登録をまだしていない人のために、登録のリンクも作っておく。

app/views/sessions/new.html.erb
<% 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>

このソースではform_forタグを使用しているが、form_tagも同じく使用可能

セッションフォームとユーザー登録フォームの違いとして、セッションにはSessionモデルというものがなく、そのため@userのようなインスタンス変数に相当するものもない。

したがって、新しいセッションフォームを作成するときには、form_forヘルパーに追加の情報を独自に渡す必要がある。↓

<%= form_for(:session, url: login_path) do |f| %>

以下に、 HTMLフォームを示しておく。

login.form
<form accept-charset="UTF-8" action="/login" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <input name="authenticity_token" type="hidden"
         value="NNb6+J/j46LcrgYUC60wQ2titMuJQ5lLqyAbnbAUkdo=" />
  <label for="session_email">Email</label>
  <input class="form-control" id="session_email"
         name="session[email]" type="text" />
  <label for="session_password">Password</label>
  <input id="session_password" name="session[password]"
         type="password" />
  <input class="btn btn-primary" name="commit" type="submit"
       value="Log in" />
</form>

コントローラで、それぞれのアクションを定義する。

app/controllers/sessions_controller.rb
class SessionsController < ApplicationController

  def new
  end

  def create
    render 'new'
  end

  def destroy
  end
end

また、createアクション でユーザーを検証する処理を書く。

app/controllers/sessions_controller.rb
def create
    user = User.find_by(email: params[:session][:email].downcase)
    if user && user.authenticate(params[:session][:password])
      # ユーザーログイン後にユーザー情報のページにリダイレクトする
    else
      # エラーメッセージを作成する
      render 'new'
    end
end

セッションでは、ユーザー登録のように Active Recordモデル を使っていないため、自分でエラーメッセージを作る必要がある。

app/controllers/sessions_controller.rb
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'
      render 'new'
    end
end

↑では、flash[:danger]としてしまうと、一度表示されたメッセージが消えなくなってしまうため
flash.now[:danger]
を使い、一度だけ表示されるようにする。

ログイン

まず、SessionsHelperコントローラに読みこますことで、セッションに必要なメソッドを使えるようにしておく。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end

log_inメソッド

railsで定義済みのsessionメソッドを使って、Sessionsヘルパーにlog_inメソッドを定義する。

app/helpers/sessions_helper.rb
module SessionsHelper

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

これによりsessionコントローラcreateアクション を整えることができる。

app/controllers/sessions_controller.rb
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

redirect_to user
はrailsでは自動的に以下のルーティングに変換される。
user_url(user)

現在のユーザー

セッションIDに対応するユーザーネームを取り出す、 current_userメソッド を定義する。

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

↑のようにすれば、current_userを定義することができるが、もっとコンパクトに定義することができる。
「||=」を使うことで以下のようになる。

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

「||=」は左辺がnilあれば、右辺を代入するという使い方ができる。

よって、current_userメソッド は以下のように定義することができる。

app/helpers/sessions_helper.rb
# 現在ログイン中のユーザーを返す (いる場合)
  def current_user
    if session[:user_id]
      @current_user ||= User.find_by(id: session[:user_id])
    end
  end

レイアウトリンクの変更

ユーザーがログインしているかしていないかで、レイアウトを変更する必要がある。

下記のように logged_in? のメソッドを使えば、レイアウトを変更する必要がある。

<% if logged_in? %>
  # ログインユーザー用のリンク
<% else %>
  # ログインしていないユーザー用のリンク
<% end %>

よって、logged_in? を定義する。
current_userがnilでなければログインしている、という風に解釈できるので↓のように定義できる。

app/helpers/sessions_helper.rb
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

ユーザー登録時にログイン状態

登録が終われば自動的にログインがすんでいる状態になるようにusersコントローラのcreateアクションを追加修正しておく。

app/controllers/users_controller.rb
def create
    @user = User.new(user_params)
    if @user.save
      #追加
      log_in @user
      flash[:success] = "Welcome to the Sample App!"
      redirect_to @user
    else
      render 'new'
    end
end

ログアウト

session.delete(:user_id)

↑で、現在のユーザーをnilにすることができるので、logoutメソッド を以下のように定義する。

app/helpers/sessions_helper.rb
module SessionsHelper
 # 現在のユーザーをログアウトする
  def log_out
    session.delete(:user_id)
    #セキュリティ上、念の為次の一行も追加する
    @current_user = nil
  end
end

次に、Sessionsコントローラdestroyアクション を定義する。

app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
.
.
.

  def destroy
      log_out
      redirect_to root_url
  end
end

終わりに

第八章では、Sessionコントローラでログイン・ログアウトを実装した。

webアプリケーションには必要な機能のためしっかり理解しておこうと思う。

次↓
https://qiita.com/jonnyjonnyj1397/items/9cb25f506404c7ae5b9e

0
1
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
1