えらく多機能なので試しながらメモる.
とりあえず使う
とりあえず使ってみる.
認証をするからにはサンプルが欲しいので用意しよう.
サンプルプロジェクトを考える
メモを管理するサービスということにしよう.
コントローラは
- StaticPages(トップページ管理)
- Notes(メモの管理)
の2つを用意する.
機能は認証とメモ管理のみ.
プロジェクトを作る
$ rails new notes
Deviseのインストール
gem 'devise'
をGemfileへ書いて
$ bundle install
とりあえずトップページとメモ機能を作る
とりあえずなのでscaffoldで.
$ rails g controller StaticPages
$ rails g scaffold note subject:string body:string
$ rails db:migrate
StaticPagesへはconfig/routes.rbを編集してルートも追加.
Rails.application.routes.draw do
root to: 'static_pages#index' #追加
resources :notes
end
雑にトップページも作っておく.
<!-- app/views/static_pages/index.html.erb -->
<h1>Top</h1>
ここまででメモ機能は動作する.
Deviseのセットアップ
ここからDeviseを使っていく.
Deviseを使うにはまず以下のコマンドでセットアップする.
$ rails g devise:install
そうすると丁寧に以下のメッセージを出してくれる.
===============================================================================
Some setup you must do manually if you haven't yet:
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.
2. Ensure you have defined root_url to *something* in your config/routes.rb.
For example:
root to: "home#index"
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>
4. You can copy Devise views (for customization) to your app by running:
rails g devise:views
===============================================================================
1はメーラの設定, Deviseは認証やパスワードリマインダーにメールを使えるのでそのため.
2はルートページURLの設定, これは認証時の遷移先で必要になるだろう.
3はエラーメッセージなどを表示するタグをviewに入れてくれということ.
4はカスタムのviewが欲しい時に使えという事.
Deviseはデフォルトでviewを用意するのでカスタムする時には4のコマンドでまずはviewのファイルを生成してやらないとならない.
(viewをカスタムしない場面もあまりなさそうなものだけど)
2のルートページの設定は上述で行ったので, 1, 3を行います.
カスタムviewは後回し.
1. メーラの設定
# config/environments/development.rb
# 追加
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
3.エラーメッセージタグのviewへの追加
app/views/layout/application.html.erb
<!-- ここから -->
<div class="notice">
<%= notice %>
</div>
<div class="alert">
<%= alert %>
</div>
<!-- ここまで -->
<%= yield %>
認証に使うモデルの作成
認証するためのアカウント情報を保持するためのモデルを作成します.
作成にはDeviseが用意しているコマンドを使います.
$ rails g devise User
作成されたファイルを少し覗いてみます.
# app/models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
deviseの利用するモジュールがわらわら書かれています(後述)
マイグレーションファイルも見てみます.
# frozen_string_literal: true
class DeviseCreateUsers < ActiveRecord::Migration[5.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
すごいわらわら出来てます...
コメントアウトされているものをアンコメントするとConfirmableなどのモジュールも利用できそうな気配.
とりあえずデフォルトの認証機能さえ使えれば良いのでマイグレーション.
$ rails db:migrate
サインインとサインアウトのリンクをつける
サインインとサインアウトの画面へのリンクをトップページに作りたいので, routesを確認してみます.
imai@IMAI-PC:~/lab/projects/notes$ rails routes
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
[中略]
とりあえずusersの部分だけ見てもたくさん生成されています.
とはいえリンクに使えそうなものは以下でしょう.
- new_user_registration
- new_user_session
- destroy_user_session
これらを使ってリンクを用意します.
トップページからのリンクを作る
ユーザ登録(sign up)とログイン(sign in)は必要でしょう.
トップページ(app/views/static_pages/index.html.erb)にリンクを加えます.
<h1>Top</h1>
<%= link_to 'sign in', new_user_session_path %>
<%= link_to 'sign up', new_user_registration_path %>
ログアウトのリンクを作る
ログインしたらログアウトのリンクも欲しいのでレイアウト(app/views/layouts/application.html.erb)へ加えます.
<body>
<!-- ここから -->
<div class="session-control-box">
<% if signed_in? %>
<%= link_to 'sign out', destroy_user_session_path, method: :delete %>
<% end %>
</div>
<!-- ここまで -->
signed_in?はdeviseが用意してくれるヘルパーメソッドです.
その名の通りログイン中だったらtrueを返します.
ログイン後のリダイレクトを設定する
ログイン後にはメモの一覧へ移動してほしいのでヘルパーメソッドの利用を追加します.
app/controllers/application_controller.rbに
- after_sign_in_path_for
- after_sign_out_path_for
の利用を追加します.
名前の通りこのメソッドの戻り値のパスへリダイレクトされます.
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(resource)
notes_path
end
def after_sign_out_path_for(resource)
root_path
end
end
これでログイン後とログアウト後の挙動を変更できます.
ログイン前はアクセスできないようにする
リダイレクトされてもログイン前にメモ管理のエリアにアクセスできてしまっては本末転倒なので, 認証機能を追加します.
class NotesController < ApplicationController
before_action :authenticate_user! #追加
before_action :set_note, only: [:show, :edit, :update, :destroy]
authenticate_[モデル名]メソッドがdeviseで用意されているのでこれを使うだけです.
未認証の場合はログイン画面へリダイレクトされます.
ログインした人間のノートだけ参照できるようにする
このままではログインさえすれば誰のメモでも見れてしまうので, メモに所有者情報を持たせてログインしているユーザのメモだけが見れるように変更します.
まずはmigrationでnoteへuserへの外部キーを追加します.
$ rails g migration AddUserToNotes user:references
$ rails db:migrate
モデルへアソシエーションもつけておきます.
ユーザ側.
# app/models/user.rb
class User < ApplicationRecord
has_many :notes
メモ側.
# app/models/note.rb
class Note < ApplicationRecord
belongs_to :user
end
次にscaffoldで作ったコントローラをちょっと変更.
def index
#[変更]
#@notes = Note.all
@notes = current_user.notes
end
[中略]
def new
#[変更]
#@note = Note.new
@note = current_user.notes.build
end
[中略]
def create
#[変更]
#@note = Note.new(note_params)
@note = current_user.notes.build(note_params)
[中略]
private
# Use callbacks to share common setup or constraints between actions.
def set_note
#[変更]
#@note = Note.find(params[:id])
@note = current_user.notes.find_by(id: params[:id])
redirect_to notes_url if @note.nil?
end
current_[モデル名]ヘルパーメソッドがdeviseによって用意されるのでログイン中のユーザを取得するのは簡単です.
ここまでやると普通にアプリケーションっぽくなります.
i18nに対応する
deviseそのままでは英語メッセージが表示されるのでI18N対応してやる.
なんとそのままの名前のgemがあった.
tigrish/devise-i18n - GitHub
余談だがこの方, kaminariとか色々なgemのi18n対応をやってるみたいで地味にすごい.
Gemfileに追記して更新する.
# 追記
gem 'devise-i18n'
$ bundle install
デフォルトロケールを設定していない場合はこちらも設定.
# config/application.rb
# 以下を記述
config.i18n.default_locale = :ja
これだけでメッセージが日本語化される.ありがたい.
公式にも一応色々記載があるので込み入ったことをしたい場合は読むとよいかも.
専用のwikiもある.
ViewとControllerをカスタマイズする
ちょっと試すぐらいならデフォルトでも良いけど, 流石にこのままのviewでは実用に耐えない.
モデルもちょっといじって, 登録時にユーザ名も登録するようにしてみる.
Userモデルの変更
migrationでユーザ名を追加する.
$ rails g migration AddNameToUsers name:string
$ rails db:migrate
deviseのviewの用意
deviseでviewをカスタマイズするにはまず以下のコマンドでビューを生成する.
$ rails g devise:views users(モデル名)
するとdeviseで使うビューが用意される.
これをカスタマイズしていく.
deviseのcontrollerの用意
controllerもviewと同様で, 下記のコマンドで生成する.
$ rails g devise:controllers users
親切にroutes.rbを変更しろと教えてくれる.
create app/controllers/users/confirmations_controller.rb
create app/controllers/users/passwords_controller.rb
create app/controllers/users/registrations_controller.rb
create app/controllers/users/sessions_controller.rb
create app/controllers/users/unlocks_controller.rb
create app/controllers/users/omniauth_callbacks_controller.rb
===============================================================================
Some setup you must do manually if you haven't yet:
Ensure you have overridden routes for generated controllers in your routes.rb.
For example:
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions'
}
end
===============================================================================
生成されたコントローラがapp/controllers/users/に用意されるが, これらは全てコメントアウトされている.
なのでこれをベースに使うものをアンコメントしていかないとならない.
各コントローラと機能の対応は多分以下の感じじゃないかな.
コントローラ名 |
機能 |
confirmations_controller.rb |
登録後のメールでの確認 |
passwords_controller.rb |
パスワードリマインダー |
registrations_controller.rb |
ユーザ登録 |
sessions_controller.rb |
セッション(ログイン/ログアウト) |
omni_auth_callbacks_controller.rb |
omni_authによるOAuth制御のコールバック |
unlocks_controller.rb |
ログインに一定回数失敗したらアカウントロックするアレ |
とりあえず
だけは使いたいので以下の2つのファイルの中身をアンコメントする.
- registrations_controller
- sessions_controller
routesを変更
コンソールで出た指示通りに.
#devise_for :users
devise_for :users, controllers: {
registrations: 'users/registrations',
sessions: 'users/sessions'
}
ここまででコントローラとビューをカスタムする前と同じ挙動になる.
viewの変更
app/views/users/registrations/new.html.erbを変更して名前欄を追加する.
<!-- ここから -->
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name, autofocus: true %>
</div>
<!-- ここまで -->
<div class="field">
ついでにログイン中のユーザ名を見えるようにしておく.
<!-- app/views/layout/application.html.erbへ追加 -->
<p><%= current_user.name %></p>
controllerの変更
strong parametersを追加するだけですが.
def configure_sign_up_params
#devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])
end
公式にstrong parametersに関する記載があったので, 困ったら読めば良いかな.
localeファイルの編集
普通にjaのymlを用意してActiveRecordの部分に書いた.
# config/locales/ja.yml
ja:
activerecord:
attributes:
user:
name: ユーザ名
これだけでうまく動作します.素敵ですね.
testをしたい
公式にも章が組まれている.
ControllerのテストもIntegrationテストもどちらでもIntegrationHelpersをincludeすると
の両メソッドが利用できるので, これで困ることはほぼないと思う.
# test/controllers/note_controller_test.rb
class NotesControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
sign_in users(:yamada)
end
ちょっと調べる必要があったのがFixturesの書き方で, 以下の様に書いた.
# test/fixtures/users.yml
yamada:
email: yamada@example.com
encrypted_password: <%= Devise::Encryptor.digest(User, 'password') %>
モジュールの一覧
deviseには以下の10個のモジュールがあり, それぞれ用途に応じて使い分ける事ができる.
モジュール名 |
機能 |
デフォルト |
database_authenticatable |
DBに保存するパスワードの暗号化 |
有効 |
registerable |
サインアップ処理 |
有効 |
recoverable |
パスワードリセット機能 |
有効 |
rememberable |
クッキーにログイン情報を保持する |
有効 |
trackable |
サインイン回数・時刻・IPアドレスを保存 |
有効 |
validatable |
メールアドレスとパスワードでのバリデーション |
有効 |
confirmable |
メール送信による登録確認 |
|
lockable |
一定回数ログインに失敗した際のアカウントロック |
|
timeoutable |
一定時間でセッションを削除する |
|
omniauthable |
OmniAuthのサポート |
|
ちょっとすぐに全部は試せそうにない.心折れた.
とはいえこれだけでかなり使えそうです.
参考
Documentation for plataformatec/devise (master)