【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アプリケーションを使いやすいようにするためにも、ログイン機能の実装にチャレンジにしてみてください。