Rails Tutorial 8章 ログイン処理(current_user等)

現在のユーザー

ユーザーIDをセッションの中に安全におけるようになったので、これを別ページで取り出せるようにしたい。

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

例えば、こんなことをしたい

 <%= current_user.name %>
 redirect_to current_user


現在のユーザーを検索する方法としては、 findメソッドが挙げられる。
しかし、ユーザーIDが存在しない状態でfindメソッドを使うと例外が発生してしまう。(ActiveRecord::RecordNotFoundが出ちゃう)

そこでfind_byメソッドを使いたい。

 User.find_by(id: session[:user_id])

find_byメソッドによって、IDが無効な場合(=ユーザーが存在しない場合)には、メソッドは例外を発生せず nil を返す。


この手法を使ってcurrent_userを定義

 def current_user
  if session[:user_id]
   User.find_by(id:session[:user_id])
  end
 end

nilが返されれば、DBへの問い合わせ回数を抑えられ、負担も減る。


また、Rubyの慣習に従い、実行結果をインスタンス変数に保存している

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

or演算子「||」を使えばこんな簡単に

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


これ、論理値自体は常にTrueになる。
よって、@current_userに何も代入されてない時だけfind_byメソッドが呼び出され、無駄な読み出しが防げる。

これを短縮して、

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


「||=」とは何か?
例)

>>@foo
=>nil
>>@foo = @foo || "bar"
=>"bar"
>>@foo = @foo || "baz"
=>"bar"



以上のコードを適用した結果がこちら

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


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

ユーザーがログインしている時とそうでないときでレイアウトを変更したい。

まず、ログインしているかどうかを判別するメソッドを作成(ERBコードの中で条件分岐を使う)

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

これを機能させるため、logged_in?を作る


ユーザーがログイン中の状態とは、「sessionにユーザーIDが存在している」こと。
つまり、current_userがnilではないということ。
つまり、否定演算子「!」を使用して判定。

app/helpers/sessions_helper.rb
.
.
 #ユーザーがログインしていればtrue、その他ならfalseを返す
 def logged_in?
  !current_user.nil?
 end

これでユーザーのログイン時にレイアウトを変えられる準備が整った。


んで、書いたビューがこれ

app/views/layouts/_header.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "sample app", root_path, id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li><%= link_to "Home", root_path %></li>
        <li><%= link_to "Help", help_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>

レイアウトに新しいリンクを追加したので、Bootstrapのドロップダウンメニュー機能を適用できる状態になった。(dropdown-menu, dropdownとか)

これらの機能を有効にするため、Railsのapplication.jsファイルに指示出す。

app/assets/javascripts/application.js
.
//= require jquery
//= require bootstrap
.

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

log_in @user これだけ

app/controllers/users_controller.rb
.
.
def create
.
if @user.save
 log_in @user
.
.

ログアウト

セッションからユーザーIDを削除ればいい。

session.delete(:user_id)

これで、現在のユーザーをnilにできる。

ログイン処理と同じようにsessionヘルパーに記載

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


ここで定義したlog_outメソッドをsesisonコントローラのdestroyアクションでも使う。

app/controllers/sessions_controller.rb
.
.
def destroy
 log_out
 redirect_to root_url
end