##はじめに
Railsチュートリアル11章の内容を、少しでも理解の助けとなればと思い、割としっかり目に整理しました!
備忘録です。
##前提
Railsチュートリアル1〜10章までの内容が完了していること。
##内容
Railsチュートリアル11章の__アカウント有効化機能の実装手順__を前中後半の3回に分けて整理していきます。
前編である今回は、__アカウント有効化機能を実装するのに必要となるリソースや、データモデルを作成__していきます!
中編→アカウント有効化メール送信機能の実装
後編→アカウント有効化機能の実装
##1.AccountACctivationsリソースを作成する
●セッション機能を使って、アカウントの有効化リソースを作成していく。
➡︎アカウント有効化リソースを作成するにあたり、Userモデルに必要なデータを追加していくことが必要となる。
➡︎注意しなければならない点が1つあって、アカウント有効化リソースは__ユーザーがアプリから受け取ったメールに貼られたリンクを単にクリックした際に発動する__。よってこの場合、ユーザーが送信するリクエストはGETリクエストになるため、updateアクションではなく__editアクションを使う必要がある__。
####1-1.AccountActivationsコントローラの作成
●AccountActivationsリソースを作成するために、まずはAccountActivationsコントローラを生成していく。
➡︎$ rails g controller AccountActivations
●editアクションへアクセスするのに名前付きルートが必要となるため、ルーティングにアカウント有効化に使うリソースを追加する。
➡︎resources :account_activations, only: [:edit]
→︎GETリクエストを受け取り、editアクションへアクセスさせる。
→URLは「/account_activation//edit」を受け取る。
→名前付きルートはedit_account_activation_url(token)。
→_pathではなく、_urlを使うのは、メールからこのURLへアクセスするため。
congig/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
#1-1.AccountActivationsリソース追加。
resources :account_activations, only: [:edit]
end
####1-2.AccountActivationのデータモデルを作成していく。
●前提として、有効化のメールには一意の有効化トークンが必要である。
●有効化トークン作成の方法としては、送信メールとDBの両方に同じ文字列を保存させる方法があるが、DBのデータが漏れた場合の被害が甚大であり危険。
➡︎例えば、第三者が新規登録したユーザーアカウントの有効化トークンを盗み、そのユーザーとしてログインしてしまうなどの危険がある。
●なので今回もパスワードや記憶トークンの実装で行なったように、__仮想の属性とハッシュ化した文字列を使って有効化トークンを作成し、DBに保存されるようにする__方法をとることにする。
➡︎まずは仮想の有効化トークン属性を追加するために、attr_accessor :actvation_tokenというコードを用意しておく。
➡︎次に、DBにactivated属性を追加して、データ型をbooleanとして論理値を取れるようにしたい。
➡︎user.activated?と記述した時に、ユーザーが有効化かどうかをテストできるようにするため。
➡︎上述した__必要なデータモデルをDBへ追加ししたいので、マイグレーションを作成する__コマンドを実行する。
→$ rails g migration add_activation_to_users ¥
> activation_digest:string activated:boolean activated_at:datetime
→activation_digest属性、データ型string。
→activated属性、データ型boolean。
→activated_at属性、データ型datetime(ユーザーを有効にした時の日時)。
➡︎db/migrate/\_add_activation_to_users.rbファイルへ移動し、activated属性のデフォルト値をfalseに設定しておく。
→デフォルトではユーザーを有効化させないため。
➡︎コマンドを実行して、マイグレーションの変更をDBに保存。
●__Activationトークンのコールバックを記述する。__
➡︎ユーザーが新規登録を完了するには必ずアカウントの有効化が必要になる。
➡︎ということは、ユーザーオブジェクトが作成される前にはもう、有効化トークンと有効化ダイジェストが作成されていなければならない。
➡︎オブジェクトが"作成される直前"に何らかの処理を実行したい場合、Userモデル内でコールバックを使用しよう。
→例えばbefore_saveというコールバックを使用すれば、オブジェクトが"保存される直前"、すなわちオブジェクトが作成や更新される瞬間にコールバックが呼び出され、処理を実行してくれる。
➡今回は、__ユーザーが"作成される直前"__なので、before_createコールバックを使用する。
→コールバックの引数にメソッドを渡すことで、Railsはそのメソッドを呼び出し、ユーザーオブジェクトが作成される前にそのメソッドを実行する。
●__有効化トークンと有効化ダイジェストを生成するメソッドの定義。(コールバックで使うやつ)__
→メソッド名はcreate_activation_digestとする。
→ランダムな文字列を生成し、仮想の属性である有効化トークンへ代入する処理を記述する。
→その有効化トークンをハッシュ化して、有効化ダイジェストへ保存する処理を記述する。
(→永続セッション作成用に定義したrememberメソッドでは、記憶ダイジェストに値を保存する際update_attributeメソッドを使ったが、今回は何故update_attributeを使わないのかをここで説明する。)
(→create_activation_digestの場合は、ユーザーが作成される直前に呼び出されるので、更新するダイジェスト属性がそもそも存在しない。要するにユーザーが保存される際、ダイジェストの値も一緒に保存される。)
(→rememberの場合は、記憶トークンや記憶ダイジェストが、既にDBに存在するユーザーのために作成されるから。)
●Userモデルに、上記で示した__アカウント有効化用のトークンを生成__する処理を行うコードを記述する。
➡︎app/models/user.rbファイルヘ移動。
➡︎仮想の有効化トークン属性を、attr_accessorへ適用させたコードを記述。
→attr_accessor :activation_token
➡︎有効化トークンと有効化ダイジェストを作成するためのメソッドを記述。
→Userモデル内でしか使用しないので、外部に公開されないようprivateキーワード内で記述していく。
→上述した通り、メソッド名はcreate_activation_digestとする。
→ランダムな文字列を作成するUser.new_tokenメソッドを呼び出し、有効化トークンへ代入する処理を記述。
→上記の有効化トークンを有効化ダイジェストへ代入する処理を記述。
➡︎before_createコールバックに、create_activation_digestメソッドを適用させる記述を行う。
●ここで一旦、後々テストする際に必要となるサンプルデータとfixtureに変更を加え、__事前にサンプルとユーザーを有効化__しておく。
➡︎サンプルユーザーを有効化する。
→db/seeds.rbへ移動。
→それぞれのユーザーデータにactivated: trueと、activated_at: Time.zone.nowを追加。
➡︎fixtureのユーザーを有効化する。
→test/fixtures/users.ymlへ移動。
→それぞれのユーザーデータにactivated: trueと、activated_at: <%= Time.zone.now %>を追加。
➡︎変更したら、データベースを初期化して、サンプルデータを再度生成し直す。
→$rails db:migrate:reset
→$rails db:seed
>```ruby:ターミナル
#1-2.マイグレーションの作成。
$ rails g migration add_activation_to_users ¥
activation_digest:string activated:boolean activated:datetime
db/migrate/[timestamp]_add_activation_to_users.rb
#1-2.Usersモデルにデータを追加。
class AddActivationToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :activation_digest, :string
add_column :users, :activated, :boolean, default: false
add_column :users, :activated_at, :datetime
end
end
>```ruby:ターミナル
#1-2.マイグレーションの変更をDBに反映させる。
$ rails db:migrate
app/models/user.rb
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email #コールバックにメソッドを渡す(追加事項)。
before_create :create_activation_digest
.
.
.
private
#メアドを小文字化するメソッドを定義する(追加事項)。
def downcase_email
self.email = email.downcase
end
#1-2.有効化トークンの作成し、作成した有効化トークンをハッシュ化しダイジェストへ保存。
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
>```ruby:db/seed.rb
#1-2.サンプルユーザーを有効化しておく。
User.create!(name: "Example User",
.
.
.
activated: true,
activated_at: Time.zone.now)
99.times do |n|
.
.
.
User.create!(name: name,
.
.
.
activated: true,
activated_at: Time.zone.now)
end
test/fixtures/users.yml
#fixtureのテスト用ユーザーを有効化しておく。
michael:
.
.
.
activated: true
activated_at: <%= Time.zone.now %>
archer:
.
.
.
activated: true
activated_at: <%= Time.zone.now %>
lana:
.
.
.
activated: true
activated_at: <%= Time.zone.now %>
malory:
.
.
.
activated: true
activated_at: <%= Time.zone.now %>
<% 30.times do |n| %>
user_<%= n %>:
.
.
.
activated: true
activated_at: <%= Time.zone.now %>
<% end %>
<br>
##最後に
前編の__AccountActivationsリソースの作成__はここまでになります。
中編→[__アカウント有効化用メール送信機能の実装__](https://qiita.com/kurawo___D/items/051ce6f6bb0837e3481a)
後編の[__アカウント有効化機能の実装__](https://qiita.com/kurawo___D/items/3f1873530741b4601ef3)
<br>
##参考
[Railsチュートリアル 第11章アカウントの有効化](https://railstutorial.jp/chapters/account_activation?version=5.1#cha-account_activation)