【Ruby on Rails】簡単なメモアプリの作成|自作ログイン機能をgemのdeviseなしで実装

作成日: 更新日:

開発環境

  • Ruby:version 3.1.2
  • Ruby on Rails:version 7.0.4
  • Visual Studio Code:version 1.73.0
  • OS:Windows10

Ruby on RailsでGemなしで自作ログイン機能の実装手順

Ruby on Railsのログイン機能は、ユーザーに適したサービスを提供するために様々なWebアプリケーションで実装されています。 Ruby on Railsのログイン機能については、deviseというgemファイル有名ですが、今回はdeviseなしでログイン機能を自作で実装しています。 今回はメモアプリの作成で使用した「ログイン機能」のコードを用いて解説していきます。

ログイン機能のユーザーモデルの作成

まずはログイン機能で使用するユーザーモデルを作成します。

1rails g model User

次にユーザーモデルのマイグレーションファイルに必要な「名前、メールアドレス、パスワード」のカラムを追加していきます。

1# memo_app\db\migrate\20230108041355_create_users.rb
2class CreateUsers < ActiveRecord::Migration[7.0]
3  def change
4    create_table :users do |t|
5+       t.string :name
6+       t.string :email
7+       t.string :password
8      t.timestamps
9    end
10  end
11end

上記の記述が完了したら、以下のコマンドを叩いてマイグレーションファイルを反映させます。

1rails db:migrate

今回はユーザー機能の作成は省略しますため、ダミーデータをコンソールで挿入していきます。

1rails c
1User.create(name: "テストユーザー", email: "test@example.com", password: "12345678")

パスワードについては、bcryptというgemファイルを使用して暗号化するのがおすすめですが、今回は省略しています。 また、今回は省略していますが、ユーザー機能を作成するときにはそれぞれのカラムに適したバリデーションをかけていきましょう。

ログイン機能のセッションコントローラーの作成

次にログイン機能で使用するセッションコントローラーを作成します。

1rails g controller sessions

ログイン機能のルーティングの作成

次にログイン機能に必要な「ログイン画面の表示、ログイン処理、ログアウト処理」のルーティングを作成していきます。

1# memo_app/config/routes.rb
2Rails.application.routes.draw do
3  root :to => 'memos#index'
4+   get 'login', to: 'sessions#new', as: 'new_sessions'
5+   post 'login', to: 'sessions#create', as: 'create_sessions'
6+   delete 'logout', to: 'sessions#destroy', as: 'destroy_sessions'
7  resources :memos, only: [:index, :update, :destroy, :create]
8  post 'ajax_memos_create', to: 'memos#ajax_create', as: 'ajax_memos_create'
9  get 'search', to: 'memos#search', as: 'search'
10  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
11
12  # Defines the root path route ("/")
13  # root "articles#index"
14end

それぞれnewがログイン画面の表示、createがログイン処理、destroyがログアウト処理のルーティングになります。

ログイン画面のビューの作成

次にログイン画面のビューを作成していきます。

1# memo_app/app/views/sessions/new.erb
2<div class="wrapper">
3    <div class="container">
4        <h2>ユーザーログイン</h2>
5
6        <%= form_with url: create_sessions_path, local: true, method: :post do |f| %>
7            <%= f.label :email, "Email" %>
8            <%= f.email_field :email, class: "form-box" %>
9            <%= f.label :password, "Password" %>
10            <%= f.password_field :password, class: "form-box" %>
11            <%= f.submit "ログイン", class: "btn" %>
12        <% end %>
13    </div>
14</div>

ログインに必要な情報であるEmailとPasswordを入力する画面です。 Webサービスによってはユーザー名とパスワードにしても良いですが、今回はEmailとPasswordでユーザーを判定するように処理をしていきます。

ログイン機能に必要な処理をセッションコントローラーに記述

次にログイン機能に必要な処理をセッションコントローラーに記述していきます。

1# memo_app/app/controllers/sessions_controller.rb
2class SessionsController < ApplicationController
3    before_action :logged_in?, only: [:new, :create]
4
5    def new
6
7    end
8
9    def create
10        @user = User.find_by(email: params[:email], password: params[:password])
11        if @user
12            session[:user_id] = @user.id
13            flash[:notice] = "ログインに成功しました。"
14            redirect_to memos_path
15        else
16            flash[:alert] = "ログインに失敗しました。"
17            render action: "new"
18        end
19    end
20
21    def destroy
22        session[:user_id] = nil
23        redirect_to new_sessions_path
24    end
25
26    private
27
28    def logged_in?
29        if session[:user_id]
30            @user = User.find(session[:user_id])
31            flash[:notice] = "すでにログインしています。"
32            redirect_to memos_path
33        end
34    end
35end

createアクションでは、入力されたEmailとPassWordでユーザーモデルに保存されたユーザーレコードを探してきています。 もし、ユーザーが存在していたらsessionというメソッドでユーザーIDを発行し、セッションに保存したユーザーIDをクライアントのクッキーへ渡しています。 これにより、セッションで保存したユーザーIDとユーザーのクッキーに保存されたユーザーIDを照合してユーザー認証することができます。 また、EmailとPassWordでDBからユーザーを探せなかった場合、ログインできないようにしています。 destroyアクションではsessionメソッドでユーザーIDをnilとしてクライアントのクッキーへ渡すことにより、ログアウト状態にすることができます。 そのため、作成したlogged_in?というメソッドは、sessionメソッドでユーザーIDが存在していればログイン状態だと判断することができます。 また、それぞれの処理にフラッシュメッセージやエラーメッセージ、ログイン後の遷移先もどのようにするか検討してみてもらえればと思います。

ログイン機能によるアクセス制限をmenoコントローラーに記述

次にログイン機能によるアクセス制限をmenoコントローラーに記述していきます。

1# memo_app/app/controllers/memos_controller.rb
2class MemosController < ApplicationController
3+   before_action :current_user
4
5  def index
6    @memo_new = Memo.new
7    @memos = Memo.all
8  end
9
10  def create
11    @memo_new = Memo.new(memos_params)
12    @memos = Memo.all
13    if @memo_new.save
14      redirect_to root_path
15    else
16      render action: "index"
17    end
18  end
19
20  def update
21    @memo = Memo.find(params[:id])
22    @memo.update(memos_params)
23    redirect_to root_path
24  end
25
26  def destroy
27    @memo = Memo.find(params[:id])
28    @memo.destroy
29    redirect_to root_path
30  end
31
32  def ajax_create
33    @memo_new = Memo.new(memos_params)
34    @memos = Memo.all
35
36    if @memo_new.save
37      flash.now[:notice] = "メモの保存に成功しました。"
38    else
39      flash.now[:alert] = "メモの保存に失敗しました。"
40    end
41  end
42
43  def search
44    seach_word = params[:word]
45    @memos = Memo.where("title LIKE ? or description LIKE ?", "%#{seach_word}%", "%#{seach_word}%")
46    if @memos.count > 0
47      flash.now[:notice] = "#{@memos.count}件のメモが見つかりました。"
48    else
49      flash.now[:alert] = "#メモが見つかりませんでした。"
50    end
51  end
52
53  private
54
55  def memos_params
56    params.require(:memo).permit(:title, :description)
57  end
58
59+   def current_user
60+     if session[:user_id]
61+       @user = User.find(session[:user_id])
62+     else
63+       flash[:alert] = "ログインする必要があります。"
64+       redirect_to new_sessions_path
65+     end
66+   end
67
68end

current_userメソッドでユーザーがログインしていない状態だった場合、リダイレクトで遷移先をログインページに飛ばす処理をしています。 before_actionでメモコントローラーが呼び出された場合は必ず実行するようにしておけば、全てのメモコントローラーにアクセス制限をかけるという意味になります。 もし、適用したいアクションを指定したい場合は、exceptやonlyで指定しましょう。

ヘッダーにログインとログアウトボタンを作成

次にヘッダーにログインとログアウトボタンを作成します。

1# memo_app/app/views/layouts/application.html.erb
2
3  <body>
4    <div class="wrapper header-color">
5      <div class="container flex-between">
6+         <div class="header-left">
7+           <h1>MemoApp</h1>
8+         </div>
9+ 
10+         <div class="header-right">
11+           <% if @user.nil? %>
12+             <%= link_to "ログイン", new_sessions_path, method: :get %>
13+           <% else %>
14+             ログインユーザー:<%= @user.name %>さん
15+             <%= link_to "ログアウト", destroy_sessions_path, method: :delete %>
16+           <% end %>
17+         </div>
18+       </div>
19+     </div>
20
21    <div id="notice">
22      <%= render "layouts/notice" %>
23    </div>

もし、ユーザーがログインしていればログアウトボタンを表示、ログアウトしていればログインボタンを表示するという条件分岐になります。 @userにはログインユーザーの情報が代入されているため、もしログインしていればログインユーザーのみユーザー名を表示することで、ログイン状態かわかりやすいようにすることもできます。

ヘッダーのメニューボタンのデザインを調整

最後にヘッダーのメニューボタンのデザインを調整します。

1# memo_app/app/assets/stylesheets/application.css
2
3.header-color {
4    background-color: #4169e1;
5}
6
7+ .header-left {
8+ 
9+ }
10+ 
11+ .header-right {
12+     color: #ffffff;
13+     display: inline-block;
14+     line-height: 60px;
15+ }
16+ .header-right a{
17+     text-decoration: none;
18+     color: inherit;
19+ }
20
21
22.green-color{
23    background-color: green;
24}
25
26.red-color{
27    background-color: red;
28}
29
30.notice-text {
31    color: #ffffff;
32    font-weight: bold;
33    text-align: center;
34    padding: 10px 0;
35}
36
37h1 {
38    font-size: 36px;
39    color: #ffffff;
40}
41
42h2 {
43    font-size: 24px;
44}
45
46h3 {
47    font-size: 24px;
48}
49
50.wrapper {
51    width: 100%;
52}
53
54.container {
55    margin: 0 auto;
56    width: 100%;
57    max-width: 1170px;
58}
59
60.col-box-1{
61    width: 100%;
62    padding: 6px;
63}
64
65.col-box-2{
66    width: 50%;
67    padding: 6px;
68}
69
70.col-box-3{
71    width: 33.3%;
72    padding: 6px;
73}
74
75.col-box-4{
76    width: 25%;
77    padding: 6px;
78}
79
80.flex-start {
81    display: flex;
82    flex-wrap: wrap;
83    justify-content: start;
84}
85
86+ .flex-between {
87+     display: flex;
88+     flex-wrap: wrap;
89+     justify-content: space-between;
90+ }
91

今回はヘッダーにflexでbetweenを適用することにより、左右に展開することができます。 上記のコーディングは簡易的なデザインなので、好みでデザインを調整してもらえればと思います。

おわりに

Ruby on Railsで自作ログイン機能の実装について解説してきましたが、いかがだったでしょうか。 最初はログイン機能を自作で実装するのはとても難しく感じるかもしれませんが、自作ログイン機能が実装できれば基本的なクッキーとセッションの理解も深くなっていくかと思います。 是非、ユーザーがWebアプリケーションを使いやすいようにするためにも、ログイン機能の実装にチャレンジにしてみてください。

【Ruby on Rails】簡単なメモアプリの作成|自作ログイン機能をgemのdeviseなしで実装 | いっしー@Webエンジニア