はじめに
前回は
i18nで日本語化 として
- I18n
- t メソッド
- l メソッド
について学びました。
今回は
Deviseの導入 として
- ログイン・ログアウト機能
- ナビゲーションバー
について学びます。
では、はじめていきましょう。
1. Deviseのインストール
gemの追加
Deviseとは認証機能を一式追加できるgemです。
かなりの高機能なので、全てを覚えるのは大変ですが、今回は最もベーシックな機能だけを使ってみます。
早速Gemfileに gem 'Devise'
を追加します。
Gemfile【追記】
(略)
gem "capybara"
gem "selenium-webdriver"
end
gem "enum_help"
gem "devise" #←ここに追加
書けたら、
todo_app % bundle install
を実行します。
todo_app % bundle install
(略)
Bundle complete! 18 Gemfile dependencies, 91 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
todo_app %
インストールされたら、次にアプリケーション内にインストールします。
ターミナルで
todo_app % bundle install
と打ってみましょう。
以下のようなメッセージが出てくるはずです。
todo_app (main)% rails g devise:install
create config/initializers/devise.rb
create config/locales/devise.en.yml
===============================================================================
Depending on your application's configuration some manual setup may be required:
1. Ensure you have defined default url options in your environments files. Here
is an example of default_url_options appropriate for a development environment
in config/environments/development.rb:
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
In production, :host should be set to the actual host of your application.
* Required for all applications. *
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
* Not required for API-only Applications *
3. Ensure you have flash messages in app/views/layouts/application.html.erb.
For example:
<p class="notice"><%= notice %></p>
<p class="alert"><%= alert %></p>
* Not required for API-only Applications *
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
* Not required *
===============================================================================
todo_app (main %)%
以下の場所に、設定ファイルと翻訳ファイルが作成されました。
create config/initializers/devise.rb
create config/locales/devise.en.yml
また、使いかたの説明もあるようです。4項目ありますね。
- 1: 開発環境でのデフォルトのURLオプションを設定する。
- 2: ルートのURLを設定する。
- 3: フラッシュメッセージを追加する。
- 4: DeviseのViewをカスタマイズする方法。
概ねこのような内容です。
2: のルートは既に設定していますし、その他は進めながら実装していきますので、ここは一旦無視します。
以下の訂正線部分は無視して下さい。
Deviseのバージョンの違いにより、設定が不要となっています。
@jnchito(Junichi Ito)様 よりコメントでご指摘頂きました。
Turbo対応の部分に関しての変更点が書かれた記事も頂いていますので、以前触ったけど大変だった・・という方はチェックしていただけると幸いです。
↓↓↓ 触った事ある方は、こちらご一読下さい。
Rails7の場合
あとは、Rails7で使われている turbo_stream
に対応させるために、config
ファイルに設定が必要となります。
config/initializes/devise.rb
の 266
行目あたりを見てみましょう。
config/initializes/devise.rb【確認】
(略)
# The "*/*" below is required to match Internet Explorer requests.
# config.navigational_formats = ['*/*', :html, :turbo_stream]
(略)
config.navigational_formats = ['*/*', :html, :turbo_stream]
このコメントアウトされている行が Turbo
用の設定です。
このコメントを外しておきましょう。
config/initializes/devise.rb【編集】
(略)
# The "*/*" below is required to match Internet Explorer requests.
config.navigational_formats = ['*/*', :html, :turbo_stream]
(略)
これでOKです。
次に、認証機能とその対象となるモデルを作成します。
2. 認証機能を持つモデルを作成
ルーティング
Userモデルで認証機能を作ります。
todo_app % rails g devise user
と打ってみましょう。カラム名などの指定は必要ありません。
すると、
todo_app % rails g devise user
invoke active_record
create db/migrate/20231108012222_devise_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
insert app/models/user.rb
route devise_for :users
このようなログが流れて、マイグレーションファイルやモデル、その他ルーティングも作成されている事が分かります。
ルーティングを確認していきましょう。
config/routes.rb【確認】
Rails.application.routes.draw do
devise_for :users #←自動追加されている
root "tasks#index"
resources :tasks
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check
end
devise_for :users
というルーティングが出来ていますね。 これも確認しておきましょう。
rails routes -c devise
とターミナルで打ってみます。
-c
はコントローラー名で絞り込みをするオプションで、 devise
と名のつくコントローラーを表示させます。
todo_app (main *%)% rails routes -c devise
Prefix Verb URI Pattern Controller#Action
new_user_session GET /users/sign_in(.:format) devise/sessions#new
user_session POST /users/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
new_user_password GET /users/password/new(.:format) devise/passwords#new
edit_user_password GET /users/password/edit(.:format) devise/passwords#edit
user_password PATCH /users/password(.:format) devise/passwords#update
PUT /users/password(.:format) devise/passwords#update
POST /users/password(.:format) devise/passwords#create
cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel
new_user_registration GET /users/sign_up(.:format) devise/registrations#new
edit_user_registration GET /users/edit(.:format) devise/registrations#edit
user_registration PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
POST /users(.:format) devise/registrations#create
これだけのルーティングが出来ているのはすごいですね。
ちなみに resources :tasks
で作られている、 tasks
のルーティングの場合だと、
todo_app % rails routes -c tasks
Prefix Verb URI Pattern Controller#Action
root GET / tasks#index
tasks GET /tasks(.:format) tasks#index
POST /tasks(.:format) tasks#create
new_task GET /tasks/new(.:format) tasks#new
edit_task GET /tasks/:id/edit(.:format) tasks#edit
task GET /tasks/:id(.:format) tasks#show
PATCH /tasks/:id(.:format) tasks#update
PUT /tasks/:id(.:format) tasks#update
DELETE /tasks/:id(.:format) tasks#destroy
このようになっています。
オプションなしの rails routes
だと、かなり見にくくなるので、必要なものだけ取り出すと良いでしょう。
モデル
次にモデルを確認していきます。
app/models/user.rb【確認】
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
end
Taskモデルの時と違って少し記述があるようです。
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
特にこの部分は、追加する機能設定となる部分で、Viewの表示やルーティングに関わります。
今回メールによるパスワード再設定等は行いませんので、 :recoverable
は外しておきましょう。
app/models/user.rb【編集】
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:rememberable, :validatable
end
マイグレーションファイル
続いて、マイグレーションファイルも確認しておきましょう。
db/migrate/xxxxxxxxxxxxxx_devise_create_user.rb【確認】
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[7.1]
def change
create_table :users do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
# t.integer :sign_in_count, default: 0, null: false
# t.datetime :current_sign_in_at
# t.datetime :last_sign_in_at
# t.string :current_sign_in_ip
# t.string :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps null: false
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
基本的に使うのは、
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
email
encrypted_passdord
この、2つです。もちろん名前など必要であれば、migration前に追加しておく事も可能ですし、追加も可能です。
また、
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
この部分は先程モデルで消した機能のカラムです。パスワードのリセット等に使っているカラムで、使わない場合消すことも可能です。
今回はどちらも触る必要がないので、このまま進めます。
では、マイグレーションを実行しましょう。
task_app % rails db:migrate
todo_app % rails db:migrate
== 20231108012222 DeviseCreateUsers: migrating ================================
-- create_table(:users)
-> 0.0012s
-- add_index(:users, :email, {:unique=>true})
-> 0.0006s
-- add_index(:users, :reset_password_token, {:unique=>true})
-> 0.0003s
== 20231108012222 DeviseCreateUsers: migrated (0.0020s) =======================
出来たら起動してみましょう。
todo_app % bin/dev
でサーバーを起動します。(すでに起動している場合は再起動しましょう。)
そして、users/sign_in
にアクセスしましょう。
ログイン画面となります。 sign_up
のリンクを押下すると、
サインアップ画面になります。直接見るなら、users/sign_up
ですね。
適当に情報をを入力してサインアップしてみましょう。
すると、
トップページに遷移しました。これはDeviseのデフォルト設定で、サインアップ後にはルートのパスにリダイレクトするためです。
で、ここからなのですが、ログアウトする方法がありませんので、仮にログアウトのボタンを作ってみます。
ログアウトは、先程 rails routes
でルーティングを調べた際に、
destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
となっていましたので、
<%= button_to "logout", destroy_user_session_path, method: :delete %>
このように、 destroy_user_session
というprefixに _path
をつけてurlヘルパーで相対パスを作ります。
Rails7の場合、 delete
は button_to
で作ります。(link_toでも出来なくはないのですが今は省きます)
これを、tasks/index.html.erb
に書き足します。
app/views/tasks/indes.html.erb【追記】
<p style="color: green"><%= notice %></p>
<h1><%= t "activerecord.models.task" %>リスト</h1>
<div id="tasks">
<% @tasks.each do |task| %>
<%= render task %>
<% end %>
</div>
<%= link_to "新規#{ t 'activerecord.models.task' }の作成", new_task_path, class: "btn btn-primary me-2" %>
<!-- ↓ここに足す -->
<%= button_to "logout", destroy_user_session_path, method: :delete %>
この状態で、ブラウザの更新をすると、
ボタンが出てきますので、押すと、
緑のメッセージが出てきてログアウトが完了します。
(現在このメッセージは ja
の翻訳ファイルがないので、読めないという表示が出ています。)
これで、リダイレクトされずに、ログイン画面に入る事が出来ます。
i18n
上記の 翻訳の不具合も、
devise-i18n
というgem があるのですが、
ja
のファイルだけでも
こちらから作成可能なので、localeに追加しておきましょう。
config/locales/devise.ja.yml【新規作成】
ja:
activerecord:
attributes:
user:
confirmation_sent_at: パスワード確認送信時刻
confirmation_token: パスワード確認用トークン
confirmed_at: パスワード確認時刻
created_at: 作成日
current_password: 現在のパスワード
current_sign_in_at: 現在のログイン時刻
current_sign_in_ip: 現在のログインIPアドレス
email: Eメール
encrypted_password: 暗号化パスワード
failed_attempts: 失敗したログイン試行回数
last_sign_in_at: 最終ログイン時刻
last_sign_in_ip: 最終ログインIPアドレス
locked_at: ロック時刻
password: パスワード
password_confirmation: パスワード(確認用)
remember_created_at: ログイン記憶時刻
remember_me: ログインを記憶する
reset_password_sent_at: パスワードリセット送信時刻
reset_password_token: パスワードリセット用トークン
sign_in_count: ログイン回数
unconfirmed_email: 未確認Eメール
unlock_token: ロック解除用トークン
updated_at: 更新日
models:
user: ユーザー
devise:
confirmations:
confirmed: メールアドレスが確認できました。
new:
resend_confirmation_instructions: アカウント確認メール再送
send_instructions: アカウントの有効化について数分以内にメールでご連絡します。
send_paranoid_instructions: メールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます。
failure:
already_authenticated: すでにログインしています。
inactive: アカウントが有効化されていません。メールに記載された手順にしたがって、アカウントを有効化してください。
invalid: "%{authentication_keys}またはパスワードが違います。"
last_attempt: もう一回誤るとアカウントがロックされます。
locked: アカウントはロックされています。
not_found_in_database: "%{authentication_keys}またはパスワードが違います。"
timeout: セッションがタイムアウトしました。もう一度ログインしてください。
unauthenticated: ログインもしくはアカウント登録してください。
unconfirmed: メールアドレスの本人確認が必要です。
mailer:
confirmation_instructions:
action: メールアドレスの確認
greeting: "%{recipient}様"
instruction: 以下のリンクをクリックし、メールアドレスの確認手続を完了させてください。
subject: メールアドレス確認メール
email_changed:
greeting: こんにちは、%{recipient}様。
message: メールアドレスの(%{email})変更が完了したため、メールを送信しています。
message_unconfirmed: メールアドレスが(%{email})変更されたため、メールを送信しています。
subject: メール変更完了
password_change:
greeting: "%{recipient}様"
message: パスワードが再設定されました。
subject: パスワードの変更について
reset_password_instructions:
action: パスワード変更
greeting: "%{recipient}様"
instruction: パスワード再設定の依頼を受けたため、メールを送信しています。下のリンクからパスワードの再設定ができます。
instruction_2: パスワード再設定の依頼をしていない場合、このメールを無視してください。
instruction_3: パスワードの再設定は、上のリンクから新しいパスワードを登録するまで完了しません。
subject: パスワードの再設定について
unlock_instructions:
action: アカウントのロック解除
greeting: "%{recipient}様"
instruction: アカウントのロックを解除するには下のリンクをクリックしてください。
message: ログイン失敗が繰り返されたため、アカウントはロックされています。
subject: アカウントのロック解除について
omniauth_callbacks:
failure: "%{kind} アカウントによる認証に失敗しました。理由:(%{reason})"
success: "%{kind} アカウントによる認証に成功しました。"
passwords:
edit:
change_my_password: パスワードを変更する
change_your_password: パスワードを変更
confirm_new_password: 確認用新しいパスワード
new_password: 新しいパスワード
new:
forgot_your_password: パスワードを忘れましたか?
send_me_reset_password_instructions: パスワードの再設定方法を送信する
no_token: このページにはアクセスできません。パスワード再設定メールのリンクからアクセスされた場合には、URL をご確認ください。
send_instructions: パスワードの再設定について数分以内にメールでご連絡いたします。
send_paranoid_instructions: メールアドレスが登録済みの場合、パスワード再設定用のメールが数分以内に送信されます。
updated: パスワードが正しく変更されました。
updated_not_active: パスワードが正しく変更されました。
registrations:
destroyed: アカウントを削除しました。またのご利用をお待ちしております。
edit:
are_you_sure: 本当によろしいですか?
cancel_my_account: アカウント削除
currently_waiting_confirmation_for_email: "%{email} の確認待ち"
leave_blank_if_you_don_t_want_to_change_it: 空欄のままなら変更しません
title: "%{resource}編集"
unhappy: 気に入りません
update: 更新
we_need_your_current_password_to_confirm_your_changes: 変更を反映するには現在のパスワードを入力してください
new:
sign_up: アカウント登録
signed_up: アカウント登録が完了しました。
signed_up_but_inactive: ログインするためには、アカウントを有効化してください。
signed_up_but_locked: アカウントがロックされているためログインできません。
signed_up_but_unconfirmed: 本人確認用のメールを送信しました。メール内のリンクからアカウントを有効化させてください。
update_needs_confirmation: アカウント情報を変更しました。変更されたメールアドレスの本人確認のため、本人確認用メールより確認処理をおこなってください。
updated: アカウント情報を変更しました。
updated_but_not_signed_in: あなたのアカウントは正常に更新されましたが、パスワードが変更されたため、再度ログインしてください。
sessions:
already_signed_out: 既にログアウト済みです。
new:
sign_in: ログイン
signed_in: ログインしました。
signed_out: ログアウトしました。
shared:
links:
back: 戻る
didn_t_receive_confirmation_instructions: アカウント確認のメールを受け取っていませんか?
didn_t_receive_unlock_instructions: アカウントのロック解除方法のメールを受け取っていませんか?
forgot_your_password: パスワードを忘れましたか?
sign_in: ログイン
sign_in_with_provider: "%{provider}でログイン"
sign_up: アカウント登録
minimum_password_length: "(%{count}字以上)"
unlocks:
new:
resend_unlock_instructions: アカウントのロック解除方法を再送する
send_instructions: アカウントのロック解除方法を数分以内にメールでご連絡します。
send_paranoid_instructions: アカウントが見つかった場合、アカウントのロック解除方法を数分以内にメールでご連絡します。
unlocked: アカウントをロック解除しました。
errors:
messages:
already_confirmed: は既に登録済みです。ログインしてください。
confirmation_period_expired: の期限が切れました。%{period} までに確認する必要があります。 新しくリクエストしてください。
expired: の有効期限が切れました。新しくリクエストしてください。
not_found: は見つかりませんでした。
not_locked: はロックされていません。
not_saved:
one: エラーが発生したため %{resource} は保存されませんでした。
other: "%{count} 件のエラーが発生したため %{resource} は保存されませんでした。"
すると、
メッセージが日本語化されました。
では次に、よりスムーズに ログイン・ログアウト等の認証を行うために、ナビゲーションバーを作っていきたいと思います。
3. ナビゲーションバーに動的なボタンを作る
ナビゲーションバー
ナビゲーションバーはパーシャルで作っていきます。
下記のファイルを作成して下さい。
app/views/layouts/_global_navigation.html.erb【新規作成】
<nav class="navbar bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="/"><%= t "activerecord.models.task" %>リスト</a>
<div>
<% if current_user %>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<%= button_to "ログアウト", destroy_user_session_path, class: "nav-link", method: :delete %>
</li>
</ul>
<% else %>
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<%= link_to "ログイン", new_user_session_path, class: "nav-link" %>
</li>
</ul>
<% end %>
</div>
</div>
</nav>
ここの条件式でボタンを変更する事ができます。
<% if user_signed_in? %>
ログインしている時に有効なView
<% else %>
それ以外の時に有効なView
<% end %>
モデル名_signed_in?
は Deviseで実装されているヘルパーで、ログインしているかどうかを true
false
で返すメソッドです。
同様にDeviseで実装されている current_モデル名
でも代用可能ですが、こちらはログインしている User
の インスタンス
を返すか nil
を返しますので、 条件式で同様な動きとなります。
<% if current_user %>
ログインしている時に有効なView
<% else %>
それ以外の時に有効なView
<% end %>
これでも同じ動きです。
そして、呼び出し側は layouts/application.html.erb
ですので、
app/views/layouts/application.html.erb【確認】
<!DOCTYPE html>
<html>
<head>
<title>TodoApp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<div class="container my-4">
<%= yield %>
</div>
</body>
</html>
この記述の上に入れますので、
<div class="container my-4">
<%= yield %>
</div>
app/views/layouts/application.html.erb【編集】
<!DOCTYPE html>
<html>
<head>
<title>TodoApp</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<!--↓ここに追加-->
<%= render "layouts/global_navigation" %>
<div class="container my-4">
<%= yield %>
</div>
</body>
</html>
これでOKです。
これで認証機能が完成です。
最後に、先程テストで作ったボタンは消しておきましょう。
app/views/tasks/index.html.erb【編集】
<p style="color: green"><%= notice %></p>
<h1><%= t "activerecord.models.task" %>リスト</h1>
<div id="tasks">
<% @tasks.each do |task| %>
<%= render task %>
<% end %>
</div>
<%= link_to "新規#{ t 'activerecord.models.task' }の作成", new_task_path, class: "btn btn-primary me-2" %>
4. git
git
ここまでを保存しておきましょう。
todo_app % git add .
todo_app % git commit -m "Deviseの導入"
GitHub
Githubの設定ができているのであれば、同名のリポジトリを作成し、pushしておきましょう。
おわりに
本チャプターはここまでとなります。
次回は
- カスタムビュー
- Bootstrapの適用
- フラッシュメッセージ
を学びます。
ここまでお疲れ様でした。