最初に
自身で学習してきた内容を書き出していきます。
基本的には自分自身でわかるような内容になりますので、ご容赦ください。
また、誤っている点がありましたら、コメントにてご指摘ください。
(要確認)マークがあるものは、「実行前に必ずググる」べき内容。
ログイン機能をgemなしで実装するには
【rails】deviseを使わずにログイン機能を実装する方法
基礎学習として、目を通しておくのが吉。
index
- devise gemの運用方法
- deviseの導入
- 新規登録時、名前登録ができるように設定
- ログイン認証を「名前」と「パスワード」だけでログインできるように設定
- 新規登録時のバリデーションを組み、遷移先を指定する
 
devise gemの運用方法
今回のゴール
- ログイン時に、**「名前」と「パスワード」**だけでログインできるようにする。
- 新規登録時は、「名前」、「メールアドレス」、「パスワード」の登録が必要。
 ※「名前」は[最小2文字まで最大20文字まで]尚且つ[他のuserとの重複はNG]、「メールアドレス」は@がないとNG、パスワードは6文字以上
- ログイン後に遷移ページを指定できる。
今回は、下記の段階に分けて実装。
- deviseの導入
- 新規登録時、名前登録ができるように設定
- ログイン認証を「名前」と「パスワード」だけでログインできるように設定
- 新規登録時のバリデーションを組み、遷移先を指定する
1.deviseの導入
rails newコマンドで新規アプリケーションを作成。
$ rails new sample
gemの追加
gem 'devise' # gemfileの一番下とかに記述
$ bundle install # gemを追加したので、install
$ rails g devise:install # devise gemの初期設定
この時点でエラーが出たら、Railsのアップデートエラーの原因の可能性が高い。
Errno::EPERM: Operation not permitted @ chown_internal -
/var/lib/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/rubygems_integration.rb:408:in 
`block (2 levels) in replace_gem': Error loading the 'sqlite3' Active Record adapter. 
Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. 
Make sure all dependencies are added to Gemfile. (LoadError)
Gemfileのgem 'sqlite3'を、以下の記述に修正したりすると直ることがあるので、その時は焦らず調べる。(要確認)
gem 'sqlite3', '~> 1.3.6' # バージョンに関しては、要注意
Userテーブルの作成
devise gemを使用すると、ユーザテーブルを自動的に作成してくれる。
この場合、「rails g model モデル名」ではなく、
「rails g devise モデル名」といったdevise特有のコマンドになるぞ。
$ rails g devise User # この例ではUserテーブルが作成される
$ # 結果
Running via Spring preloader in process 4704
      invoke  active_record
      create    db/migrate/20180909134236_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
上記で「Userテーブル」と、「マイグレーションファイル」が作成される。
deviseユーザテーブルの項目確認
ログイン機能に必要なカラムは、主に2つ。「メールアドレス」と「パスワード」
他の詳しい記述は、ログインパスワードを忘れてしまったときの再設定リカバリやログイン回数のカウント、ログイン日時を保存するカラムなど。(あまり弄らないコードや設定)
# メールアドレス
t.string :email,              null: false, default: ""
# 暗号化されたパスワード
t.string :encrypted_password, null: false, default: ""
その他のdevise設定を確認
devise gemを使用したことにより、様々なファイルが変更、設定されている。
主だった箇所を下記へ記述。
ルーティング設定
devise_for :usersは、deviseを使用する際にURLとしてusersを含むことを示す
Rails.application.routes.draw do
  devise_for :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
モデル設定
deviseの後ろに :(コロン)で始まる部分がdeviseの機能名。
- デフォルトの記述
- :database_authenticatable(パスワードの正確性を検証)
- :registerable(ユーザ登録や編集、削除)
- :recoverable(パスワードをリセット)
- :rememberable(ログイン情報を保存)
- :validatable(emailのフォーマットなどのバリデーション)
 
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end
devise gemにより作成されたviewをカスタム
$ rails g devise:views #ログイン機能に必要なビューファイルをまとめて作成(すでに作成されてはいる)
/app/views/devise/sessions/new.html.erbなどログイン画面や新規登録画面が確認できる。
2. 名前登録ができるように設定
ユーザ登録の際に名前を登録できるようにする
現状、「メールアドレス」と「パスワード」の2つを登録すれば、ログイン可能となっている。
ここに名前登録を追加する。
…
      ## 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.string :name # わかりやすいようにここあたりに記述
      t.timestamps null: false
    end
    add_index :users, :email,                unique: true
…
きちんとマイグレーションファイルを読み込む。
$ rails db:migrate
$ # 結果としての例
== 20180909134236 DeviseCreateUsers: migrating ================================
-- create_table(:users)
   -> 0.0020s
-- add_index(:users, :email, {:unique=>true})
   -> 0.0007s
-- add_index(:users, :reset_password_token, {:unique=>true})
   -> 0.0008s
== 20180909134236 DeviseCreateUsers: migrated (0.0065s) =======================
viewの編集
名前登録ができるようになったので、ログイン画面と新規登録画面に名前登録フォームを追加してあげる。
まずは新規登録画面から。
下記は、ほんの一例
…
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
      <%= render "devise/shared/error_messages", resource: resource %>
      <!-- ログイン時に名前登録するため追加 ここから -->
      <div>
        <%= f.label :name %><br />
        <%= f.text_field :name, autofocus: true, autocomplete: "name", class: "input-color" %>
      </div>
      <!-- ログイン時に名前登録するため追加 ここまで -->
      <div>
        <%= f.label :email %><br />
        <%= f.email_field :email, autofocus: true, autocomplete: "email", class: "input-color" %>
      </div>
…
ちょっとだけ解説。
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
resourceは、devise独自の記述。
form_forで利用するモデルを取得し、モデル名としてresource_nameを使用できるようにしている。
※今回の場合、rails g devise Userで作成したUserを取得しているぞ
urlは、form_forのオプション。
上記の記述にはないが、<%= form.submit %>をクリックしたときのアクションを設定している。
registration_pathへUserモデルを渡す記述をしています(registration_pathはdeviseの新規登録を行うコントローラ)。
application_controller.rbファイルを編集
このままでは名前はDBに保存されない。(deviseでは初期状態で新規登録時にメールアドレスとパスワードのみを受け取るようにストロングパラメーターが設定してあるから)
deviseのコントローラは、ライブラリで用意されている為、直接修正できない・・・。
ので、
deviseのコントローラに修正が必要なときは、application_controllerに記述していく。
class ApplicationController < ActionController::Base
 
  before_action :configure_permitted_parameters, if: :devise_controller?
  protected
  def configure_permitted_parameters
    # ユーザ登録(sign_up)の際に、データ操作を許可
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :name])
  end
end
追記したところのミニ解説
devise利用の機能(ユーザ登録、ログイン認証など)が使われる場合、その前にconfigure_permitted_parametersが実行。
じゃあconfigure_permitted_parametersはなんぞ?というと、
devise_parameter_sanitizer.permitでnameのデータ操作を許可するアクションメソッドを記述している。
※今回の場合は、ユーザ登録(sign_up)の際に、ユーザ名(name)とメールアドレス(email)のデータ操作が許可されてる。
protectedについて
コントローラーに記述する、Strong Parametersと同様の働き。
privateの場合は、自分のコントローラ内でしか参照できないが、protectedは呼び出された他のコントローラからも参照できる!
以上で、正常に可動していれば、「名前」、「メールアドレス」、「パスワード」の新規登録が可能になる!
3. ログイン認証を「名前」と「パスワード」だけでログインできるように設定
devise.rbを編集
deviseはログイン認証のデフォルトとし、emailとpasswordで認証をしているので、emailの部分をnameに変更。
…
# ==> Configuration for any authentication mechanism
  # Configure which keys are used when authenticating a user. The default is
  # just :email. You can configure it to use [:username, :subdomain], so for
  # authenticating a user, both parameters are required. Remember that those
  # parameters are used only when authenticating and not when retrieving from
  # session. If you need permissions, you should implement that in a before filter.
  # You can also supply a hash where the value is a boolean determining whether
  # or not authentication should be aborted when the value is not present.
  # config.authentication_keys = [:email]
    config.authentication_keys = [:name] # コメントアウトした状態で、追記
…
ログイン画面に名前入力フォームを追記
現状、メールアドレス入力フォームとパスワード入力フォームのみのため、修正してあげる。
下記は、ほんの一例
…
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
      <div class="field  text-center">
        # email認証を解除しているのでコメントアウト
        # <%= f.label :email %>
        # <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
        <!-- 名前でログインしたいためコメントアウトしてname追加 ここから -->
        <%= f.label :name %><br/>
        <%= f.text_field :name, autofocus: true, autocomplete: "nickname", class: "input-color" %>
        <!-- 名前でログインしたいためコメントアウトしてname追加 ここから -->
      </div>
      <div class="field text-center">
        <%= f.label :password %><br />
        <%= f.password_field :password, autocomplete: "current-password", class: "input-color" %>
      </div>
…
さらに、新規登録時に行ったようにコントローラーも追記
class ApplicationController < ActionController::Base
 
  before_action :configure_permitted_parameters, if: :devise_controller?
  protected
  def configure_permitted_parameters
    # ユーザ登録(sign_up)の際に、データ操作を許可
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :name])
    # ログイン登録(sign_in)の際に、データ操作を許可
    devise_parameter_sanitizer.permit(:sign_in, keys: [:name])
  end
end
4. 新規登録時のバリデーションを組み、遷移先を指定する
まずはバリデーション設定
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name,
  uniqueness: { case_sensitive: :false },
  length: { minimum: 2, maximum: 20 }
end
一意性(名前が他のユーザーと被らないように)の検証として、
uniqueness: { case_sensitive: false }で、一意性とメールアドレスの大文字・小文字の区別を無くしている。
バリデーションや正規表現などに関しては奥が深いので、後日記事にまとめたいと思っています。
ログイン後の遷移ページ指定
class ApplicationController < ActionController::Base
 
  before_action :configure_permitted_parameters, if: :devise_controller?
  protected
  def configure_permitted_parameters
    # ユーザ登録(sign_up)の際に、データ操作を許可
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email, :name])
    # ログイン登録(sign_in)の際に、データ操作を許可
    devise_parameter_sanitizer.permit(:sign_in, keys: [:name])
  end
  # ログイン後にマイページに遷移する
  def after_sign_in_path_for(resource)
    user_path(current_user) # 指定ページのパスを記述
  end
end
after_sign_in_path_for(resource)の解説・・・と言ってもその名の通り、
sign_inの後のパスを指定できるぞ。
ここまでで、本記事のゴールを達成できるはず
参考サイト
追加あれば、どんどん更新していきます。
駆け出しとして頑張ります。
