Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
104
Help us understand the problem. What is going on with this article?
@take18k_tech

Deviseでログイン機能を追加・日本語化・Bootstrap4適用まで

Deviseの日本語化やBootstrap4の導入方法の記事はよく見かけます。ところが……

  • ビューファイルを作成するときに,結局どのコマンドを使えばよいか分からない:sweat:
ターミナル
rails g devise:views
rails g devise:i18n:views
rails g devise:views:bootstrap_templates
  • 全て導入したはずなのにメール文が日本語化されない:cry:

など,順序を間違えると問題が発生します。そこで,ログイン画面を最低限のスタイルで実装するところまでをまとめてみました。

開発環境

  • macOS Catalina 10.15.7
  • Ruby 2.7.2
  • Rails 6.0.3.4 (Rails 5 にも対応)
  • Bootstrap 4.5.0
  • Devise 4.7.1 (confirmableなどの導入過程は記載していません)

YouTube

【注意】 2019年11月時点の録画ですので,現在の記事の構成と変更されている部分が多々あります

0. 準備

  • 以下の前提で進めていきます。
    • データベースを PostgreSQL に指定するオプション -d postgresql はお好みです
    • トップページは homes#index の前提で進めます
    • Rails 5でアプリを作成する場合は, rails newrails _5.2.4.4_ new のような書き方で指定して下さい
ターミナル
rails new devise_sample -d postgresql
cd devise_sample
rails g controller homes index
touch app/views/layouts/_flash_messages.html.erb app/views/layouts/_header.html.erb
config/routes.rb
Rails.application.routes.draw do
  root 'homes#index'
end
  • ナビバーを追加し,レスポンシブ対応のためのmetaタグを追加
app/views/layouts/application.html.erb
 (略)
     <%= csrf_meta_tags %>
     <%= csp_meta_tag %>
+    <meta name="viewport" content="width=device-width,initial-scale=1">
 (略)
  <body>
+    <%= render 'layouts/header' %>
     <%= yield %>
   </body>
app/views/homes/index.html.erb
<%= render 'layouts/flash_messages' %>
app/views/layouts/_flash_messages.html.erb
<% flash.each do |msg_type, msg| %>
  <div class="alert alert-<%= msg_type %>" role="alert" id="alert">
    <a href="#" class="close" data-dismiss="alert">×</a>
    <%= msg %>
  </div>
<% end %>
app/views/layouts/_header.html.erb
<header>
  <nav class="navbar navbar-expand navbar-light">
    <%= link_to "Deviseサンプル", root_path, class: 'navbar-brand' %>
    <div id="Navber">
      <ul class="navbar-nav">
        <% if user_signed_in? %>
          <li class="nav-item active">
            <%= link_to 'アカウント編集', edit_user_registration_path, class: 'nav-link' %>
          </li>
          <li class="nav-item active">
            <%= link_to 'ログアウト', destroy_user_session_path, method: :delete, class: 'nav-link' %>
          </li>
      <% else %>
          <li class="nav-item active">
            <%= link_to "新規登録", new_user_registration_path, class: 'nav-link' %>
          </li>
          <li class="nav-item active">
            <%= link_to "ログイン", new_user_session_path, class: 'nav-link' %>
          </li>
        <% end %>
      </ul>
    </div>
  </nav>
</header>
app/assets/stylesheets/application.scss
/*
 *= require_tree .
 *= require_self
 */

// max-width

.mw-md {
  max-width: 576px;
}

// 「ログインしました」などのフラッシュ用スタイル

.alert-notice {
  @extend .alert-info;
}

.alert-alert {
  @extend .alert-danger;
}

【注意】 この時点でサーバーを起動してもエラーが出ます。1章完了後にサーバーを起動して動作確認をして下さい。

1. Gemの追加

1.1 Rails 6 の場合

  • Gemfileに以下を追加
Gemfile
# ログイン機能
gem 'devise'

# 日本語化
gem 'rails-i18n', '~> 6.0'
gem 'devise-i18n'

# Bootstrap
gem 'devise-bootstrap-views', '~> 1.0'
  • ターミナルから次を実行
ターミナル
bundle install

1.2 Rails 5 の場合

  • Gemfileに以下を追加
Gemfile
# ログイン機能
gem 'devise'

# 日本語化
gem 'rails-i18n', '~> 5.1'
gem 'devise-i18n'

# Bootstrap
gem 'bootstrap', '~> 4.5'
gem 'jquery-rails'
gem 'devise-bootstrap-views', '~> 1.0'
  • ターミナルから次を実行
ターミナル
bundle install

2. Bootstrapの導入

2.1 Rails 6 の場合

 【参考】(Stackoverflow) Setup Bootstrap 4.3 on Rails 6 using Webpack

  • ターミナルから次を実行
ターミナル
yarn add bootstrap jquery popper.js
  • environment.jsを次に置き換える
config/webpack/environment.js
const { environment } = require('@rails/webpacker')

const webpack = require('webpack')
environment.plugins.append('Provide', new webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery',
    Popper: ['popper.js', 'default']
}))

module.exports = environment
  • application.jsの一番下に次を追加
app/javascript/packs/application.js
require("bootstrap/dist/js/bootstrap")
  • application.cssの拡張子cssscssに変更
  • @import "bootstrap/scss/bootstrap";を追加
app/assets/stylesheets/application.scss
/*
 *= require_tree .
 *= require_self
 */

// ***** 次を追加 *****
@import "bootstrap/scss/bootstrap";
// ***** 以上を追加 *****

// max-width

// 略

2.2 Rails 5 の場合

  • application.cssの拡張子をscssに変更
  • application.scssから,*= require_tree .*= require_selfを削除
  • application.scss@import "bootstrap";を追加
app/assets/stylesheets/application.scss
@import "bootstrap";

// max-width

// 略
  • application.jsに3つ追加
app/assets/javascripts/application.js
//= require jquery3
//= require popper
//= require bootstrap-sprockets

3. Deviseの導入

  • 【公式】plataformatec/devise

  • Deviseをインストール(userの箇所は,任意のモデル名でOKです)

  • ターミナルから次を実行

ターミナル
rails g devise:install
rails g devise user
rails db:create db:migrate
  • 問題がなければ,rails sの後, http://localhost:3000 からログイン ボタンを押せば,ログイン画面が表示されます。

スクリーンショット 2019-10-08 9.37.05.png

  • gem 'devise-bootstrap-views'を追加しているので, Bootstrapもある程度適用されています。
    • 公式(hisea/devise-bootstrap-views
    • 横幅一杯表示されるのはちょっと……と思われるかもしれませんが,さらに見た目を整える作業は最後にします。

4. Deviseの日本語化

config/application.rb
module AssociationTutorial
  class Application < Rails::Application
    # 以下を追加すれば日本語に
+   config.i18n.default_locale = :ja
    # タイムゾーンも変更するなら,以下を追加
+   config.time_zone = 'Asia/Tokyo'
  end
end
  • サーバーを落として rails sで再起動すれば日本語に変更されます。

スクリーンショット 2019-10-08 10.04.53.png

5. メールを送信できるようにする(Gmailかつ開発環境のみ対応)

  • 送信元の表示名を変更しておきます。
    • タイトル名はこの設定が反映されますが,メールアドレスの箇所は実際の送信するアドレスになります。
config/initializers/devise.rb
Devise.setup do |config|
-  config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
+  config.mailer_sender = 'タイトル名 <noreply@example.com>'
end
  • Gmailから送信する場合,安全性の低いアプリのアクセスを有効にする必要があります。

  • development.rbconfig.action_mailer.raise_delivery_errors = falseの箇所を置き換えます。

    • メールアドレスやパスワードをgitの管理下に入れるのを避け,credentials管理にしておきます。本番環境で無駄にエラーが出ないようダミー情報を入れておきます。
config/environments/development.rb
  # 削除 config.action_mailer.raise_delivery_errors = false
  # 以下に置き換え
  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

  if Rails.application.credentials.gmail.present?
    mail_address = Rails.application.credentials.gmail[:address]
    password = Rails.application.credentials.gmail[:password]
  else
    mail_address = 'admin@example.com'
    password = 'password'
  end

  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
      enable_starttls_auto: true,
      address: "smtp.gmail.com",
      port: 587,
      user_name: mail_address,
      password: password,
      authentication: "plain"
  }
  • EDITOR=vi rails credentials:editで,credentialsの中に,メールアドレスとパスワードを記載します。
gmail:
  address: 送信に使用するメールアドレス
  password: 送信に使用するパスワード
  • 変更を反映するため,サーバーを落として rails s で再起動します。

  • メール送信の確認をするには,あらかじめ新規登録をした上で,ログイン画面のパスワードを忘れましたか?をクリックしてパスワード再設定メールを送信すればOKです。

スクリーンショット 2019-10-08 13.15.17.png

6. 日本語訳を変更

日本語訳を変更したい場合は,次のコマンドでconfig/locales/devise.views.ja.ymlを作成し,編集すればOKです。

ターミナル
rails g devise:i18n:locale ja
  • 例えばアカウント登録新規登録に変更したい場合は,devise.views.ja.ymlの該当文字を置換すればOKです。

  • パスワードを忘れましたか?も違和感がありますので,パスワードの再設定に置換するのがよいと思います。

7. ログイン画面などの変更

  • まず,次のコマンドでビューファイルを作成します。
ターミナル
rails g devise:i18n:views
rails g devise:views:bootstrap_templates -f
  • 【参考】それぞれのコマンドの最後に例えばuserをつけることで,usersディレクトリ内にファイルを作成することもできますが,その場合は,次の3つの作業を行わないと反映されません。
    • devise.views.ja.yml30行目のdeviseusersに変更
    • config/initializers/devise.rbにあるconfig.scoped_views = falseのコメントアウトを外してtrueに変更
    • サーバーを落としてrails sで再起動

【注意】 ここから先はお好みで採用して下さい。

  • 全体を中央に寄せたいが無駄に横長になっている状態を改善する場合は application.html.erb<%= yield %> を次のような <div> タグで囲みましょう。
app/views/layouts/application.html.erb
    <div class="container-fluid py-4 mw-md">
      <%= yield %>
    </div>

スクリーンショット 2019-10-08 14.54.38.png


【補足】 例えば, devise 関連のページのみ最大の横幅を 576px として,それ以外は別の横幅にしたい場合は,条件分岐で付与するクラスを付けかえるようにしましょう。

app/views/layouts/application.html.erb
    <div class="container-fluid py-4 <%= devise_controller? ? 'mw-md' : 'mw-xl' %>">
      <%= yield %>
    </div>
app/assets/stylesheets/application.scss
// max-width

.mw-md {
  max-width: 576px;
}

// ***** 以下を追加 *****

.mw-xl {
  max-width: 1200px;
}

devise_controller? ? 'mw-md' : 'mw-xl' の箇所はヘルパーにメソッドを作成し,呼び出す方が理想的でしょう。


app/helpers/devise_helper.rb
module DeviseHelper
  def bootstrap_devise_error_messages!
    return "" if resource.errors.empty?

    html = ""
    resource.errors.full_messages.each do |error_message|
      html += <<-EOF
      <div class="alert alert-danger alert-dismissible" role="alert">
        <button type="button" class="close" data-dismiss="alert">
          <span aria-hidden="true">&times;</span>
          <span class="sr-only">close</span>
        </button>
        #{error_message}
      </div>
      EOF
    end
    html.html_safe
  end
end

〜〜〜〜〜変更前〜〜〜〜〜
スクリーンショット 2019-10-08 15.13.37.png
〜〜〜〜〜変更後〜〜〜〜〜
スクリーンショット 2019-10-08 15.13.06.png

  • 次のGemは役目を終えているので削除してもOKです。
Gemfile
-  gem 'devise-bootstrap-views', '~> 1.0'
ターミナル
bundle install
  • ログイン画面だけエラーメッセージが表示されないので,追加しておきます。
app/views/devise/sessions/new.html.erb
 <div class="container-login">
   <h1><%= t('.sign_in') %></h1>
+  <%= render 'layouts/flash_messages' %>

   <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>

スクリーンショット 2019-10-08 15.18.45.png

  • app/views/deviseディレクトリ内のファイルのbtn btn-primarybtn btn-primary btn-blockに置換すればボタンの横幅が自然になります。

〜〜〜〜〜変更前〜〜〜〜〜
スクリーンショット 2019-10-08 15.26.43.png
〜〜〜〜〜変更後〜〜〜〜〜
スクリーンショット 2019-10-08 15.26.51.png

  • バリデーションはフロント側にも簡単に入れられます。
    • f.email_fieldf.password_fieldrequired: trueを入れることで空欄投稿できなくなります。
    • 新規登録(アカウント登録)画面では,例えば,f.password_fieldrequired: true, minlength: @minimum_password_length, maxlength: '30'を追加すれば,文字数のバリデーションを追加できます。
app/views/devise/registrations/new.html.erb
     <div class="form-group">
       <%= f.label :password %>
-      <%= f.password_field :password, autocomplete: 'current-password',
-                                      class: 'form-control' %>
+      <%= f.password_field :password, autocomplete: 'current-password',
+                                      class: 'form-control',
+                                      required: true,
+                                      minlength: @minimum_password_length,
+                                      maxlength: '30' %>

スクリーンショット 2019-10-09 10.04.34.png

  • _links.html.erbを編集して,一番下のリンクをボタンにしてみます。
app/views/devise/shared/_links.html.erb
<hr class="border-dark my-5">
<div class="form-group">
  <%- if controller_name != 'sessions' %>
    <%= link_to t(".sign_in"), new_session_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
    <%= link_to t(".sign_up"), new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
    <%= link_to t(".forgot_your_password"), new_password_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
    <%= link_to t('.didn_t_receive_confirmation_instructions'), new_confirmation_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
    <%= link_to t('.didn_t_receive_unlock_instructions'), new_unlock_path(resource_name), class: 'btn btn-secondary btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.omniauthable? %>
    <%- resource_class.omniauth_providers.each do |provider| %>
      <%= link_to t('.sign_in_with_provider', provider: OmniAuth::Utils.camelize(provider)), omniauth_authorize_path(resource_name, provider), class: 'btn btn-info btn-block' %><br />
    <% end -%>
  <% end -%>
</div>

スクリーンショット 2019-10-08 15.36.29.png

  • さらに,リンクのログイン新規登録(アカウント登録)を次のように変更してみます。
# 上2つを次に置き換え

  <%- if controller_name != 'sessions' %>
    <%= link_to "アカウントをお持ちの方", new_session_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

  <%- if devise_mapping.registerable? && controller_name != 'registrations' %>
    <%= link_to "アカウントをお持ちでない方", new_registration_path(resource_name), class: 'btn btn-info btn-block' %><br />
  <% end -%>

スクリーンショット 2019-10-08 15.41.51.png

  • 最後にアカウント編集機能を修正しておきます。色々中途半端ですね……

(修正前)
スクリーンショット 2019-10-27 21.35.49.png

app/views/devise/registrations/edit.html.erb
-   <h1><%= t('.title', resource: resource_name.to_s.humanize) %></h1>
+     <h1>アカウント編集</h1>

(中略)

<!-- ここから -->
  <p><%= t('.unhappy') %>
    ? <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: {confirm: t('.are_you_sure')}, method: :delete %>
    .</p>

  <%= link_to t('.back'), :back %>
<!-- ここまでを次に置き換える -->

  <hr class="devise-link my-5">
  <div class="form-group">
    <%= link_to "トップページ", root_path, class: 'btn btn-info btn-block mb-4' %>
    <%= link_to t('.cancel_my_account'), registration_path(resource_name), data: {confirm: t('.are_you_sure')}, method: :delete, class: 'btn btn-danger btn-block' %>
  </div>

(修正後)
スクリーンショット 2019-10-27 21.48.08.png

単純なスタイルですが,ログイン機能がだいぶ整ったのではないでしょうか。

104
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
take18k_tech
大学・大学院にて数学を学んで約十年。 小学生から大学生に算数・数学を教えて約十年。 そして,現在は沖縄にてプログラミングスクール「やんばるCODE」の講師とやんばるエキスパートの公式メンターを兼ねつつエンジニアとして活動しております!
yanbaru-expert
エンジニア転職が成功するまで無期限に超手厚いサポートをする頭おかしい講座、やんばるエキスパートです。当講座ではプログラミングの教材学習だけでなく、毎週頻繁にプログラミングやマーケティングに関する勉強会を行ったり、開発チームを組んで開発の実践を行ったりしています。Qiitaでは、日々の学んだことを投稿していきます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
104
Help us understand the problem. What is going on with this article?