railsチュートリアル挑戦記 第11章 アカウントの有効化
railsチュートリアルをやりながらメモしたことをそのまま記述しています。
第11章アカウントの有効化
アカウントを有効化するステップを新規登録の途中に差し込むことで、
本当にそのメールアドレスの持ち主なのかどうかを確認できるようにする
①有効化トークンやダイジェストを関連付けておいた状態で
②有効化トークンを含めたリンクをユーザーにメールで送信し
③ユーザーがそのリンクをクリックすると有効化できるようにする
12章ではパスワードを再設定できる仕組みを実装する
railsの開発環境や本番環境からメールを実際に送信する方法についても学ぶ
アカウントを有効化する段取りは以下の通り
①ユーザーの初期状態は「有効化されていない」(unactivated)にしておく
②ユーザー登録が行われたときに、有効化トークンと、それに対応する有効化ダイジェストを生成する
③有効化ダイジェストはデータベースに保存しておき、有効化トークンはメールアドレスと一緒に、ユーザーに送信する有効化用メールのリンクに仕込んでおく
④ユーザーがメールのリンクをクリックしたら、アプリケーションはメールアドレスをキーにしてユーザーを探し、データベース内に保存しておいた有効化ダイジェストと比較することでトークンを認証する
⑤ユーザーを認証できたら、ユーザーのステータスを「有効化されていない」から「有効化済み」(activated)に変更する
ログイン・記憶トークン。アカウントの有効化・パスワードの再設定で似ている点
検索キー string digest authentication
email password password_digest authenticate(password)
id remember_token remember_digest authenticated?(:remember, token)
email activation_token activation_digest authenticated?(:activation, token)
email reset_token reset_digest authenticated?(:reset, token)
・11 1 AccountActivationsリソース
いつものようにトピックブランチ作成
git checkout -b account-activation
・11 1 1 AccountActivationsコントローラ
rails generate controller AccountActivations
ログ
----------------------------
ec2-user:~/environment/sample_app (account-activation) $ rails generate controller AccountActivations
Running via Spring preloader in process 6083
create app/controllers/account_activations_controller.rb
invoke erb
create app/views/account_activations
invoke test_unit
create test/controllers/account_activations_controller_test.rb
invoke helper
create app/helpers/account_activations_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/account_activations.coffee
invoke scss
create app/assets/stylesheets/account_activations.scss
----------------------------
有効化のメールには次のURLを含めることになる
edit_account_activation_url(activation_token, ...)
アカウント有効化に使うリソース (editアクション) を追加する
config/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
resources :account_activations, only: [:edit]
end
----------------------------
ここでテストスイートがGREENになることを確認
・11 1 2 AccountActivationのデータモデル
以下の3つが必要
activation_digest:string
activated:boolean
activated_at:datetime
rails generate migration add_activation_to_users \
activation_digest:string activated:boolean activated_at:datetime
上を立て続けに入力
ログ
----------------------------
ec2-user:~/environment/sample_app (account-activation) $ rails generate migration add_activation_to_users \
> activation_digest:string activated:boolean activated_at:datetime
Running via Spring preloader in process 6181
invoke active_record
create db/migrate/20200103153528_add_activation_to_users.rb
----------------------------
アカウント有効化用の属性とインデックスを追加するマイグレーション
db/migrate/[timestamp]_add_activation_to_users.rb
----------------------------
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
----------------------------
rails db:migrate
Activationトークンのコールバック
before_create :create_activation_digest
Userモデルにアカウント有効化のコードを追加する green
app/models/user.rb
----------------------------
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
.
.
.
private
# メールアドレスをすべて小文字にする
def downcase_email
self.email = email.downcase
end
# 有効化トークンとダイジェストを作成および代入する
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
----------------------------
サンプルデータとfixtureも更新しておく
Time.zone.nowはサーバーのタイムゾーンに応じたタイムスタンプを返す
サンプルユーザーを最初から有効にしておく
db/seeds.rb
----------------------------
User.create!(name: "Example User",
email: "example@railstutorial.org",
password: "foobar",
password_confirmation: "foobar",
admin: true,
activated: true,
activated_at: Time.zone.now)
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}@railstutorial.org"
password = "password"
User.create!(name: name,
email: email,
password: password,
password_confirmation: password,
activated: true,
activated_at: Time.zone.now)
end
----------------------------
fixtureもTime.zone.nowを入れておく
fixtureのユーザーを有効にしておく
test/fixtures/users.yml
----------------------------
michael:
name: Michael Example
email: michael@example.com
password_digest: <%= User.digest('password') %>
admin: true
activated: true
activated_at: <%= Time.zone.now %>
archer:
name: Sterling Archer
email: duchess@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
lana:
name: Lana Kane
email: hands@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
malory:
name: Malory Archer
email: boss@example.gov
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% 30.times do |n| %>
user_<%= n %>:
name: <%= "User #{n}" %>
email: <%= "user-#{n}@example.com" %>
password_digest: <%= User.digest('password') %>
activated: true
activated_at: <%= Time.zone.now %>
<% end %>
----------------------------
データベースに反映
rails db:migrate:reset
rails db:seed
rails testでGREENになる
よし!順調!
・11 2 アカウント有効化のメール送信
アカウント有効化メールの送信に必要なコードを追加していく
・11 2 1 送信メールのテンプレート
rails generate mailerでメイラーを作れる
rails generate mailer UserMailer account_activation password_reset
ログ
----------------------------
ec2-user:~/environment/sample_app (account-activation) $ rails generate mailer UserMailer account_activation password_reset
Running via Spring preloader in process 6470
create app/mailers/user_mailer.rb
invoke erb
create app/views/user_mailer
create app/views/user_mailer/account_activation.text.erb
create app/views/user_mailer/account_activation.html.erb
create app/views/user_mailer/password_reset.text.erb
create app/views/user_mailer/password_reset.html.erb
invoke test_unit
create test/mailers/user_mailer_test.rb
create test/mailers/previews/user_mailer_preview.rb
ec2-user:~/environment/sample_app (account-activation) $
----------------------------
これで、今回必要となるaccount_activationメソッドとpassword_resetメソッドが生成された
アカウント有効化メイラーのテキストビュー (自動生成)
app/views/user_mailer/account_activation.text.erb
----------------------------
UserMailer#account_activation
<%= @greeting %>, find me in app/views/user_mailer/account_activation.text.erb
----------------------------
アカウント有効化メイラーのHTMLビュー (自動生成)
app/views/user_mailer/account_activation.html.erb
----------------------------
<h1>UserMailer#account_activation</h1>
<p>
<%= @greeting %>, find me in app/views/user_mailer/account_activation.html.erb
</p>
----------------------------
生成されたApplicationメイラー
app/mailers/application_mailer.rb
----------------------------
class ApplicationMailer < ActionMailer::Base
default from: "from@example.com"
layout 'mailer'
end
----------------------------
生成されたUserメイラー
app/mailers/user_mailer.rb
----------------------------
class UserMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.account_activation.subject
#
def account_activation
@greeting = "Hi"
mail to: "to@example.org"
end
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.user_mailer.password_reset.subject
#
def password_reset
@greeting = "Hi"
mail to: "to@example.org"
end
end
----------------------------
実際に有効化メールで使えるようにしていく
mailにsubjectキーを引数として渡しているが、これが件名に当たる
fromアドレスのデフォルト値を更新したアプリケーションメイラー
app/mailers/application_mailer.rb
----------------------------
class ApplicationMailer < ActionMailer::Base
default from: "noreply@example.com"
layout 'mailer'
end
----------------------------
アカウント有効化リンクをメール送信する
app/mailers/user_mailer.rb
----------------------------
class UserMailer < ApplicationMailer
def account_activation(user)
@user = user
mail to: user.email, subject: "Account activation"
end
def password_reset
@greeting = "Hi"
mail to: "to@example.org"
end
end
----------------------------
urlで@を扱うときは%40になる
アカウント有効化のテキストビュー
app/views/user_mailer/account_activation.text.erb
----------------------------
Hi <%= @user.name %>,
Welcome to the Sample App! Click on the link below to activate your account:
<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
----------------------------
アカウント有効化のHTMLビュー
app/views/user_mailer/account_activation.html.erb
----------------------------
<h1>Sample App</h1>
<p>Hi <%= @user.name %>,</p>
<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>
<%= link_to "Activate", edit_account_activation_url(@user.activation_token,
email: @user.email) %>
----------------------------
CGIで
CGI.escate('foo@example.com')
=> "foo%40example.com"
・11 2 2 送信メールのプレビュー
development環境のメール設定
config/environments/development.rb
----------------------------
Rails.application.configure do
.
.
.
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :test
host = 'example.com' # ここはコピペしないこと!
config.action_mailer.default_url_options = { host: host, protocol: 'https' }
.
.
.
end
----------------------------
hostの部分は、
https://うんたらかんたらrtheast-1.amazonaws.com/
を
うんたらかんたらrtheast-1.amazonaws.com/
にしてコピペ
とりあえず開発環境のurlで、https://を抜いたバージョン
Userメイラープレビュー (自動生成)
test/mailers/previews/user_mailer_preview.rb
----------------------------
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
UserMailer.account_activation
end
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
UserMailer.password_reset
end
end
----------------------------
アカウント有効化のプレビューメソッド (完成)
test/mailers/previews/user_mailer_preview.rb
----------------------------
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/account_activation
def account_activation
user = User.first
user.activation_token = User.new_token
UserMailer.account_activation(user)
end
# Preview this email at
# http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset
UserMailer.password_reset
end
end
----------------------------
https://ごにょごにょ/rails/mailers/user_mailer/account_activation
と、
https://ごにょごにょ/rails/mailers/user_mailer/account_activation.txtで表示されるかな?たぶん
Dateがいくつか見てみる
見えたあああああああああああああああ!!!!!
感動!!!!!
Date:
Fri, 03 Jan 2020 16:00:38 +0000
・11 2 3 送信メールのテスト
Userメイラーのテスト (Railsによる自動生成)
test/mailers/user_mailer_test.rb
----------------------------
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "account_activation" do
mail = UserMailer.account_activation
assert_equal "Account activation", mail.subject
assert_equal ["to@example.org"], mail.to
assert_equal ["from@example.com"], mail.from
assert_match "Hi", mail.body.encoded
end
test "password_reset" do
mail = UserMailer.password_reset
assert_equal "Password reset", mail.subject
assert_equal ["to@example.org"], mail.to
assert_equal ["from@example.com"], mail.from
assert_match "Hi", mail.body.encoded
end
end
----------------------------
現在のメールの実装をテストする red
test/mailers/user_mailer_test.rb
----------------------------
require 'test_helper'
class UserMailerTest < ActionMailer::TestCase
test "account_activation" do
user = users(:michael)
user.activation_token = User.new_token
mail = UserMailer.account_activation(user)
assert_equal "Account activation", mail.subject
assert_equal [user.email], mail.to
assert_equal ["noreply@example.com"], mail.from
assert_match user.name, mail.body.encoded
assert_match user.activation_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded
end
end
----------------------------
テストのドメインホストを設定する
config/environments/test.rb
----------------------------
Rails.application.configure do
.
.
.
config.action_mailer.delivery_method = :test
config.action_mailer.default_url_options = { host: 'example.com' }
.
.
.
end
----------------------------
ユーザー登録にアカウント有効化を追加する red
app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
.
.
.
end
----------------------------
rails testがgreenになることを確認
test/mailers/user_mailer_test.rbでCGI.escapeの部分をコメントアウトするとエラーになることを確認
ログ
----------------------------
rails test
Running via Spring preloader in process 7537
Started with run options --seed 31094
FAIL["test_valid_signup_information", UsersSignupTest, 1.2003073949999816]
test_valid_signup_information#UsersSignupTest (1.20s)
expecting <"users/show"> but rendering with <["user_mailer/account_activation", "layouts/mailer", "static_pages/home", "layouts/_rails_default", "layouts/_shim", "layouts/_header", "layouts/_footer", "layouts/application"]>
test/integration/users_signup_test.rb:24:in `block in <class:UsersSignupTest>'
44/44: [==========] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.61597s
44 tests, 175 assertions, 1 failures, 0 errors, 0 skips
ec2-user:~/environment/sample_app (account-activation) $
----------------------------
先に進めすぎたみたい
ちょっと修正戻したらエラー取れた
・11 2 4 ユーザーのcreateアクションを更新
元々プロフィールページにリダイレクトしていたが、
ルートURLに移動するように変更する
ユーザー登録にアカウント有効化を追加する red
app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
UserMailer.account_activation(@user).deliver_now
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
.
.
.
end
----------------------------
ここで、テストが失敗するようになるため、一時的にコメントアウトしておく(さっきのエラーはこれによるもの)
失敗するテストを一時的にコメントアウトする green
test/integration/users_signup_test.rb
----------------------------
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password" } }
end
follow_redirect!
# assert_template 'users/show'
# assert is_logged_in?
end
end
----------------------------
ルートページに、メッセージが表示されるのを確認
サーバーログに表示されたアカウント有効化メールの例
----------------------------
UserMailer#account_activation: processed outbound mail in 292.4ms
Sent mail to michael@michaelhartl.com (47.3ms)
Date: Mon, 06 Jun 2016 20:17:41 +0000
From: noreply@example.com
To: michael@michaelhartl.com
Message-ID: <f2c9222494c7178e@mhartl-rails-tutorial-3045526.mail>
Subject: Account activation
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="--==_mimepart_5755da6513e89_f2c9222494c71639";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_5755da6513e89_f2c9222494c71639
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Hi Michael Hartl,
Welcome to the Sample App! Click on the link below to activate your account:
https://rails-tutorial-mhartl.c9users.io/account_activations/
-L9kBsbIjmrqpJGB0TUKcA/edit?email=michael%40michaelhartl.com
----==_mimepart_5755da6513e89_f2c9222494c71639
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
/* Email styles need to be inline */
</style>
</head>
<body>
<h1>Sample App</h1>
<p>Hi Michael Hartl,</p>
<p>
Welcome to the Sample App! Click on the link below to activate your account:
</p>
<a href="https://rails-tutorial-mhartl.c9users.io/account_activations/
-L9kBsbIjmrqpJGB0TUKcA/edit?email=michael%40michaelhartl.com">Activate</a>
</body>
</html>
----==_mimepart_5755da6513e89_f2c9222494c71639--
images/figures/redirected_not_activated
----------------------------
・11 3 アカウントを有効化する
AccountActivationsコントローラのeditアクションを書いていく
・11 3 1 authenticated?メソッドの抽象化
プログラムでプログラムを作成することをメタプログラミングという
抽象化されたauthenticated?メソッド red
app/models/user.rb
----------------------------
class User < ApplicationRecord
.
.
.
# トークンがダイジェストと一致したらtrueを返す
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
.
.
.
end
----------------------------
rails testで失敗することを確認
ログ
----------------------------
rails test
Running via Spring preloader in process 8569
Started with run options --seed 15291
ERROR["test_authenticated?_should_return_false_for_a_user_with_nil_digest", UserTest, 0.8228407409997089]
test_authenticated?_should_return_false_for_a_user_with_nil_digest#UserTest (0.82s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:33:in `authenticated?'
test/models/user_test.rb:75:in `block in <class:UserTest>'
ERROR["test_current_user_returns_nil_when_remember_digest_is_wrong", SessionsHelperTest, 1.5487051019999853]
test_current_user_returns_nil_when_remember_digest_is_wrong#SessionsHelperTest (1.55s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:33:in `authenticated?'
app/helpers/sessions_helper.rb:25:in `current_user'
test/helpers/sessions_helper_test.rb:17:in `block in <class:SessionsHelperTest>'
ERROR["test_current_user_returns_right_user_when_session_is_nil", SessionsHelperTest, 1.559482666999429]
test_current_user_returns_right_user_when_session_is_nil#SessionsHelperTest (1.56s)
ArgumentError: ArgumentError: wrong number of arguments (given 1, expected 2)
app/models/user.rb:33:in `authenticated?'
app/helpers/sessions_helper.rb:25:in `current_user'
test/helpers/sessions_helper_test.rb:11:in `block in <class:SessionsHelperTest>'
44/44: [==========] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.64842s
44 tests, 172 assertions, 0 failures, 3 errors, 0 skips
ec2-user:~/environment/sample_app (account-activation) $
----------------------------
失敗する理由は、authenticatedを使っている部分が古いコードのままなため
current_user内の抽象化したauthenticated?メソッド red
app/helpers/sessions_helper.rb
----------------------------
module SessionsHelper
.
.
.
# 現在ログイン中のユーザーを返す (いる場合)
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
.
.
.
end
----------------------------
Userテスト内の抽象化したauthenticated?メソッド green
test/models/user_test.rb
----------------------------
require 'test_helper'
class UserTest < ActiveSupport::TestCase
def setup
@user = User.new(name: "Example User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar")
end
.
.
.
test "authenticated? should return false for a user with nil digest" do
assert_not @user.authenticated?(:remember, '')
end
end
----------------------------
rails testでGreenになることを確認
確認した!イイネ!!!
・11 3 2 editアクションで有効化
editアクションでアカウントが作成されるようにする
一度リンクをクリックして有効化されたら有効化されないようにする
アカウントを有効化するeditアクション
app/controllers/account_activations_controller.rb
----------------------------
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.update_attribute(:activated, true)
user.update_attribute(:activated_at, Time.zone.now)
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
----------------------------
有効でないユーザーがログインすることのないようにする
app/controllers/sessions_controller.rb
----------------------------
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) : forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += "Check your email for the activation link."
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
----------------------------
・11 3 3 有効化のテストとリファクタリング
アカウント有効化の統合テストを追加する
正しい情報でユーザー登録を行った場合のテストは既にあるため、若干手を加える
ユーザー登録のテストにアカウント有効化を追加する green
test/integration/users_signup_test.rb
----------------------------
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
def setup
ActionMailer::Base.deliveries.clear
end
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user@invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
end
test "valid signup information with account activation" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user@example.com",
password: "password",
password_confirmation: "password" } }
end
assert_equal 1, ActionMailer::Base.deliveries.size
user = assigns(:user)
assert_not user.activated?
# 有効化していない状態でログインしてみる
log_in_as(user)
assert_not is_logged_in?
# 有効化トークンが不正な場合
get edit_account_activation_path("invalid token", email: user.email)
assert_not is_logged_in?
# トークンは正しいがメールアドレスが無効な場合
get edit_account_activation_path(user.activation_token, email: 'wrong')
assert_not is_logged_in?
# 有効化トークンが正しい場合
get edit_account_activation_path(user.activation_token, email: user.email)
assert user.reload.activated?
follow_redirect!
assert_template 'users/show'
assert is_logged_in?
end
end
----------------------------
rails testでgreenになることを確認
ここも通っていくゥ!!
リファクタリングしていく
Userモデルにユーザー有効化メソッドを追加する
app/models/user.rb
----------------------------
class User < ApplicationRecord
.
.
.
# アカウントを有効にする
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# 有効化用のメールを送信する
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
private
.
.
.
end
----------------------------
ユーザーモデルオブジェクトからメールを送信する
app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
.
.
.
def create
@user = User.new(user_params)
if @user.save
@user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
.
.
.
end
----------------------------
ユーザーモデルオブジェクト経由でアカウントを有効化する
app/controllers/account_activations_controller.rb
----------------------------
class AccountActivationsController < ApplicationController
def edit
user = User.find_by(email: params[:email])
if user && !user.activated? && user.authenticated?(:activation, params[:id])
user.activate
log_in user
flash[:success] = "Account activated!"
redirect_to user
else
flash[:danger] = "Invalid activation link"
redirect_to root_url
end
end
end
----------------------------
rails testでgreenになること
通った!!!
update_columnsを使用するテンプレート
app/models/user.rb
----------------------------
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
.
.
.
# アカウントを有効にする
def activate
update_columns(activated: true, activated_at: Time.zone.now)
end
# 有効化用のメールを送信する
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
private
# メールアドレスをすべて小文字にする
def downcase_email
self.email = email.downcase
end
# 有効化トークンとダイジェストを作成および代入する
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
----------------------------
有効なユーザーだけを表示するコードのテンプレート
app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
.
.
.
def index
@users = User.where(activated: true).paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
redirect_to root_url and return unless @user.activated?
end
.
.
.
end
----------------------------
・11 4 本番環境でのメール送信
今回ここが激ムズかも知れない
まず、Herokuにクレジットカードの登録が必要
https://wp.developapp.net/?p=5250
を参考にした
すぐできた
ここも参考になるかも?
https://qiita.com/daichi_t/items/d34c9ca683d7dd8e77df
さあやるか・・・
下記は13章で必要らしい
これでユーザーネームとパスワードが表示できるらしい
そしていつものようにBitbucketとHerokuにプッシュ
git add -A
git commit -m "Add account activation"
git checkout master
git merge account-activation
rails test
git push
source <(curl -sL https://cdn.learnenough.com/heroku_install)
heroku create
git remote set-url heroku
heroku rename jun-killer-11
https://jun-killer-11.herokuapp.com/
ができた!
heroku maintenance:on
以下のhostに、アプリ名を入れる!
Railsのproduction環境でSendGridを使う設定
config/environments/production.rb
----------------------------
Rails.application.configure do
.
.
.
config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp
host = '<ここ注意ね!自分の作ったURL!!>.herokuapp.com'
config.action_mailer.default_url_options = { host: host }
ActionMailer::Base.smtp_settings = {
:address => 'smtp.sendgrid.net',
:port => '587',
:authentication => :plain,
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:domain => 'heroku.com',
:enable_starttls_auto => true
}
.
.
.
end
----------------------------
本番環境ではSendGridというHerokuアドオンを利用してアカウントを検証する必要がある
本チュートリアルではstarter tierを使う
heroku addons:create sendgrid:starter
失敗するようなら
heroku addons:add sendgrid:starter
を試してみる
今回は上のコマンドでいけた!
そして下記のコマンドで色々表示できた!
heroku config:get SENDGRID_USERNAME
heroku config:get SENDGRID_PASSWORD
git add -A
git commit -m "modify"
git push heroku master
heroku pg:reset DATABASE
jun-killer-11
heroku run rails db:migrate
heroku run rails db:seed
heroku restart
heroku maintenance:off
https://jun-killer-11.herokuapp.com
試しに新規登録したらめちゃくちゃ上手く行った!!!!
気持ちいい!!!!!
・11 5 最後に
次はパスワードの再設定機能
・11 5 1 本章のまとめ
アカウント有効化は、ActiveRecordオブジェクトではないが、セッションと同様にリソースでモデル化できる
Railsはメール送信で扱うActionMailerのアクションとビューを生成することができる
ActionMailerではテキストメールとHTMLメールの両方を利用できる
メイラーアクションで定義したインスタンス変数は、他のアクションやビューと同様に、メイラーのビューから参照できる
アカウントを有効化させるために、生成したトークンを使って一意のURLを作る
より安全なアカウント有効化のために、ハッシュ化したトークン(ダイジェスト)を使う
メイラーのテストと統合テストは、どちらもUserメイラーの振舞いを確認するのに有用
SendGridを使うと、production環境からメールを送信できる
この記事が気に入ったらサポートをしてみませんか?