📌

[Rails]複数権限でのdeviseを用いたゲストログイン機能の実装

2023/07/18に公開2

複数権限(例:Admin,Member等..)でゲストログイン機能を作ろうと思った時にroutes周辺でエラーになって困ったので残しておきます。


前提

  • devise導入済み
  • Admin,Member をdeviseで用いている。

行いたいこと

  • ゲストユーザーとしてログインできるようにする。
  • プロフィールの編集機能の制限(nickname等を変更されてしまうとログインできなくなってしまう為)

ルーティングの設定

まずはじめにゲストログイン用にルーティングをconfig/routes.rbに記述します。

config/routes.rb
 Rails.application.routes.draw do
 ...
  devise_scope :member do
   post "members/guest_sign_in", to: "public/sessions#guest_sign_in"
  end
 ...
 end

devise_scopeは、deviseを使用して新しくルーティングを指定したい時に必要となります。

ここではsign_inのHTTPメソッドはユーザ情報をサーバへ送るので"POST"になります。
その右に実際の任意のURLの指定を行います。ここではわかりやすくguest_sign_inとしています。
to:の後はコントローラーのアクションの指定となり、今回はpublic/sessions_controller.rbにゲストログインアクションを記述していきます。

members_guest_sign_in POST /members/guest_sign_in(.:format) public/sessions#guest_sign_in

ルーティングの確認ができました。

アクションを定義する

わかりやすく以下のguest_sign_inのようにアクションを定義します。

public/sessions_controller.rb
 # frozen_string_literal: true
 class Public::SessionsController < Devise::SessionsController
   # before_action :configure_sign_in_params, only: [:create]
 
   def guest_sign_in
     member = Member.guest
     sign_in member
     redirect_to members_path, notice: "Guestでログインしました。"
   end
  ...
 end

guestメソッドはこの後Memberモデルに定義をしていきます。
アクション内で使用しているsign_inメソッドはdeviseが提供している機能でサインイン状態にしてくれる便利なメソッドです。
最後にリダイレクト先とお好みでサクセスメッセージを指定します。

https://zenn.dev/yuna0960740/articles/dbea6e1ad45b01

モデルに定義する

先ほどのguestメソッドをMemberモデルに定義していきます。

models/members.rb
 class Member < ApplicationRecord
 # Include default devise modules. Others available are:
 # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
 devise :database_authenticatable, :registerable,
        :recoverable, :rememberable, :validatable
  ...
  GUEST_MEMBER_EMAIL = "guest@example.com"
   def self.guest
     find_or_create_by!(email: GUEST_MEMBER_EMAIL) do |member|
       member.password = SecureRandom.urlsafe_base64
       member.nickname = "guestmember"
     end
   end

   def guest_member?
     email == GUEST_MEMBER_EMAIL
   end
   ...
 end

この後ゲストかどうかを判定する時にこのアドレス(guest@example.com)を記述する場面が多くあり、処理の度に同じ記述を書くとメールアドレスを変更したい時等に修正に弱いコードとなってしまいます。ここではRailsのDRY原則に則り、以下のようにGUEST_MEMBER_EMAILにメールアドレスを代入し汎用性の高いコードを意識します。"GUEST_MEMBER_EMAIL = guest@example.com"

find_or_create_byとは?

こちらはRailsのメソッドで、データの検索と作成を自動的に判断して処理を行ってくれます。
今回は条件として(email: GUEST_MEMBER_EMAIL)を与えています。
処理としては、まず条件として指定したデータが存在するかを判断し、存在したらそのデータを返し、存在しなければ新規作成をしてくれます。判断と処理をこのメソッドで行えるので便利なメソッドです。
find_or_create_byの末尾に!をつけているのは処理が成功しなかった時にエラーが発生しやすくなるためにつけています。これにより不具合を検知しやすくなります。

SecureRandom.urlsafe_base64とは?

こちらはRubyのメソッドの一種で、ランダムな文字列を生成してくれます。
こちらを使いパスワードを生成します。

今回のmembersテーブルにはnicknameカラムを作ってあるのでそちらをguestuserと固定します。

下のguest_member?メソッドはviewでゲストユーザーには表示させたくないもの(編集ボタン等)を条件分岐で表示させないようにする時に使うため、ここで定義しておきます。

ここまでで機能部分の作成は完了しました。

ビューファイルを編集する

まず任意の場所にゲストログインボタンを配置します。

app/views/layouts/_header.html.erb
 <ul>
  <% unless member_signed_in? %>
   ...
   <li>
    <%= link_to 'ゲストログイン', members_guest_sign_in_path, method: :post %>
   </li>
   ...
  <% end %>
 </ul>

今回はヘッダーに配置しました。Topページ等でも良いと思います。

次にプロフィールの編集制限をしていきます。

app/views/public/members/show.html.erb
 <% if current_member.guest_member? %>
  <%= image_tag current_member.get_icon(150,150) %>
 <% else %>
  <%= link_to members_information_edit_path do %>
    <%= image_tag current_member.get_icon(150,150) %>
    <p>編集</p>
  <% end %>
 <% end %>

今回はアイコンを編集ボタンとして設置していたので先程Memberモデルに定義したguest_member?を使いゲストユーザーの場合には編集ページへのリンクを表示させないように条件分岐させます。

最後に編集画面のURLから直接編集画面に行けないようにします。

app/controllers/public/members_controller.rb
 class Public::MembersController < ApplicationController
  before_action :ensure_guest_member, only: [:edit]
  ...
  private
  ...
   def ensure_guest_member
    if current_member.guest_member?
      redirect_to member_path(current_member), notice: "ゲストユーザーはプロフィール編集できません。"
    end
   end
  end

members_controllerへensure_guest_memberと定義します。こちらも任意で名前を変えていただいて大丈夫です。'before_action'でeditアクション実行前にこの処理を行わせます。
これでURLを直接叩かれても編集画面へ遷移できないようにできました。

こちらで実装は以上となります。

最後に

再び自分で記事を書いてみて、改めてたくさん投稿されている方に尊敬の念がつきません。。
時間を作り、もっと記事を投稿したいと思います。
学習のアウトプットと振り返りがメインですが、どなたかの参考になれば幸いです。。

なにか間違い(誤字脱字、技術面等)があればご指摘ください。
最後まで見てくださりありがとうございました。一緒に勉強がんばりましょう!

Discussion

がんもがんも

今後実装するだろう機能なのでめちゃめちゃ助かります!
参考にさせてもらいます♪

YnnnYnnn

そう言っていただけると嬉しいです!
為になるような情報をアップロードできるよう精進いたします😁