見出し画像

railsチュートリアル魔改造編 第17章 新規登録メール魔改造

第17章 新規登録メール魔改造編

新規登録メールを魔改造していきます。

いつものようにトピックブランチを作成します。

git checkout -b rails-makaizou-17

17.1 新規登録メール魔改造

新規登録後に表示される文字列を魔改造します。

まず、新規登録フォームで登録をクリック処理はどこを通るのでしょうか?


リスト17.01: 新規登録フォーム
/sample_app/app/views/users/new.html.erb
----------------------------
<%#
 # /signup で呼ばれるアクション
 # app/views/users/new.html.erbへ
 def new
   #ユーザーを新規に作成する
   @user = User.new
 end
%>
<%# 見出しを新規登録にする %>
<% provide(:title, '新規登録') %>
<%# 見出しを新規登録にする %>
<h1>新規登録</h1>
<%# Bootstrapの機能で、12列に何を割り当てるか決める %>
<div class="row">
 <%# 空白3-入力スペース6-空白3の構成にする %>
 <div class="col-md-6 col-md-offset-3">
   <%# フォームを作成し、@userにデータを入れていく  %>
   <%= form_for(@user) do |f| %>
     
     <%# エラー文を出力する %>
     <%= render 'shared/error_messages', object: f.object %>
     
     <%# 名前 %>
     <%= f.label :name, "名前" %>
     <%= f.text_field :name, class: 'form-control' %>
     <%# メールアドレス %>
     <%= f.label :email, "メールアドレス" %>
     <%= f.email_field :email, class: 'form-control' %>
     <%# パスワード %>
     <%= f.label :password, "パスワード" %>
     <%= f.password_field :password, class: 'form-control' %>
     <%# パスワード(再入力) %>
     <%= f.label :password_confirmation, "パスワード(再入力)" %>
     <%= f.password_field :password_confirmation, class: 'form-control' %>
     <%# パスワード(再入力) %>
     <%= f.submit "魔改造アカウントを作成する", class: "btn btn-danger" %>
   <% end %>
 </div>
</div>


以下の記事を参考にしました。
https://qiita.com/kitsunecat/items/1d4d07ba590c9f44e2cd

送信ボタンをクリックすると、どこに飛ぶのかパッと見ではわかるようになっていません。
答えは、form_forの仕様でusersコントローラのcreateを呼ぶようになっています。

/sample_app/app/controllers/users_controller.rb
のcreateアクションを見てみます。

@user = User.new(user_params)
という行の、user_paramsとはいったいなんでしょう?

同じファイルのprivateのところに、定義があります。

def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end

paramsは前ページから送信されたデータが入っている特殊なテーブルです。
require、permitは何をしているのでしょう?

requireとpermitで、paramsから不要なパラメータを取り除き、必要なパラメータに絞り込むためのメソッドです。

requireメソッドで受け取るパラメータ群を、permitメソッドで利用可能なパラメータ名を指定します。
form_forでは@userを指定しました。
そのあと、labelは:nameと:emailと:passwordと:password_confirmationを入力しました。

paramsから、:userの:name,:email,:password,:password_confirmationを抽出するということですね。

ちなみにrequireは必要、permitは許可という意味です。

参考サイト
https://techblog.kyamanak.com/entry/2017/08/29/012909#paramsrequirepermit-%E3%81%AE%E4%B8%AD%E8%BA%AB%E3%82%92%E7%A2%BA%E8%AA%8D

railsチュートリアル7.3.2章で習ったStrong Parameterというやつです。
user_paramsメソッドを使うことで、User.new(name: "~",email: "~",password: "~", password_confirmation:"~")と同じになります。

そして、以下の行がある。
@user.send_activation_email
send_activation_emailはどこか?
app/models/user.rbに、メソッドの定義がある。
UserMailer.account_activation(self).deliver_now
によって、メールが送信されていく。

deliver_nowは、今すぐ送信する時に使用する

app/mailers/user_mailer.rb
に、account_activationの定義がある。
このメソッドによって、送り先と件名が決定する。

ちなみにrailsチュートリアル11章で、アカウントの有効化を実装しました。

以下でプレビューが表示されたかと思います。
URLごにょごにょ/rails/mailers/user_mailer/account_activation
URLごにょごにょ/rails/mailers/user_mailer/account_activation.txt

それらの内容は、下記で変更できる
app/views/user_mailer/account_activation.html.erb
app/views/user_mailer/account_activation.text.erb

それぞれを確認しながら、メールの内容を魔改造します。
注意として、textのほうはURLがクリックできないので、それを考慮して文章を考える必要があるかも。

edit_account_activation_urlは、以下の箇所で作成される
/sample_app/config/routes.rb
resources :account_activations, only: [:edit]

そして、Userのインスタンスであるactivation_tokenは、
/sample_app/app/models/user.rb
に定義がある。
それは、インスタンスメソッドであるcreate_activation_digestを呼び出すことで、ダイジェストと一緒に作成される
activation_tokenは、クラスメソッドであるnew_tokenで作成する
このメソッドはSecureRandom.urlsafe_base64でランダムに作成したトークンを返すだけのシンプルなメソッドである。
クラスメソッドのdigestは、引数にトークンを渡すことで、ハッシュ値を返す

じゃあ、create_activation_digestは、どこで呼び出しているか?
実は、/sample_app/app/models/user.rbのbefore_createを使って、データベースへの登録前でのみ呼び出されるようになっている(before_saveはデータベースへの登録と更新で呼び出される)

https://www.techscore.com/blog/2012/12/25/rails%E3%81%AE%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E3%81%BE%E3%81%A8%E3%82%81/

ここでrails testをすると、REDになるはず

ログ
----------------------------
ec2-user:~/environment/sample_app (rails-makaizou-17) $ rails test
Running via Spring preloader in process 9822
Started with run options --seed 52825
FAIL["test_account_activation", UserMailerTest, 1.8401885599996604]
test_account_activation#UserMailerTest (1.84s)
       Expected /Michael\ Example/ to match # encoding: US-ASCII
       "\r\n----==_mimepart_5e1b3047f06ec_265e12d19a4780d\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n\r\nTWljaGFlbCBFeGFtcGxl44GV44KT44CB44GT44KT44Gr44Gh44Gv77yBDQoN\r\nCuS7peS4i+OBrlVSTOOCkumWi+OBj+OBqOOAgeOCouOCq+OCpuODs+ODiOOB\r\njOacieWKueOBq+OBquOCiuOBvuOBmeOAgg0KDQpodHRwOi8vZXhhbXBsZS5j\r\nb20vYWNjb3VudF9hY3RpdmF0aW9ucy9Pam8zUHloVkYyOWQ5SXg4VUgyekZ3\r\nL2VkaXQ/ZW1haWw9bWljaGFlbCU0MGV4YW1wbGUuY29tDQo=\r\n\r\n----==_mimepart_5e1b3047f06ec_265e12d19a4780d\r\nContent-Type: text/html;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n\r\nPCFET0NUWVBFIGh0bWw+DQo8aHRtbD4NCiAgPGhlYWQ+DQogICAgPG1ldGEg\r\naHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7\r\nIGNoYXJzZXQ9dXRmLTgiIC8+DQogICAgPHN0eWxlPg0KICAgICAgLyogRW1h\r\naWwgc3R5bGVzIG5lZWQgdG8gYmUgaW5saW5lICovDQogICAgPC9zdHlsZT4N\r\nCiAgPC9oZWFkPg0KDQogIDxib2R5Pg0KICAgIDxwPk1pY2hhZWwgRXhhbXBs\r\nZeOBleOCk+OAgeOBk+OCk+OBq+OBoeOBr++8gTwvcD4NCg0KPHA+DQrku6Xk\r\nuIvjga5VUkzjgpLjgq/jg6rjg4Pjgq/jgZnjgovjgajjgIHjgqLjgqvjgqbj\r\ng7Pjg4jjgYzmnInlirnjgavjgarjgorjgb7jgZnjgIINCjwvcD4NCg0KPGEg\r\naHJlZj0iaHR0cDovL2V4YW1wbGUuY29tL2FjY291bnRfYWN0aXZhdGlvbnMv\r\nT2pvM1B5aFZGMjlkOUl4OFVIMnpGdy9lZGl0P2VtYWlsPW1pY2hhZWwlNDBl\r\neGFtcGxlLmNvbSI+44Ki44Kr44Km44Oz44OI44KS5pyJ5Yq544Gr44GZ44KL\r\nPC9hPg0KICA8L2JvZHk+DQo8L2h0bWw+DQo=\r\n\r\n----==_mimepart_5e1b3047f06ec_265e12d19a4780d--\r\n".
       test/mailers/user_mailer_test.rb:12:in `block in <class:UserMailerTest>'
 74/74: [=====================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.76447s
74 tests, 425 assertions, 1 failures, 0 errors, 0 skips
----------------------------

test/mailers/user_mailer_test.rb
を見ても、特に問題になるようには見えない
https://teratail.com/questions/188866
にて、日本語が混じるとエンコード周りで駄目になるとのこと。
gem 'mail-iso-2022-jp' を入れると解決するようだが、今回は見送り。
ちなみに以下の3行全てで引っかかる。
#assert_match user.name, mail.body.encoded
#assert_match user.activation_token, mail.body.encoded
#assert_match CGI.escape(user.email), mail.body.encoded
ので、コメントアウトにした。

17.2 メール送信後メッセージ魔改造

/sample_app/app/controllers/users_controller.rbのcreateメソッドにそのメッセージがあるので、書き換える
ここでrails testをすると、REDになるはずである。

ログ
----------------------------
ec2-user:~/environment/sample_app (rails-makaizou-17) $ rails test
Running via Spring preloader in process 9282
Started with run options --seed 9619
FAIL["test_account_activation", UserMailerTest, 2.290641062998475]
test_account_activation#UserMailerTest (2.29s)
       Expected: "Account activation"
         Actual: "railsチュートリアル魔改造へようこそ!"
       test/mailers/user_mailer_test.rb:9:in `block in <class:UserMailerTest>'
 74/74: [====================================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.61194s
74 tests, 422 assertions, 1 failures, 0 errors, 0 skips
----------------------------


原因は下記

/sample_app/test/mailers/user_mailer_test.rb

assert_equal "Account activation", mail.subject

これはコメントアウトしておく。件名は変わる可能性があるため。

17.3 アカウント有効後メッセージ魔改造

アカウント有効後に表示されるメッセージを魔改造します。

URLを辿ると、
/sample_app/app/controllers/account_activations_controller.rb
のeditが呼び出される
emailは、urlに乗っけられてる

find_byは、1件のみ取得する

user.activatedは、
db/migrate/〇〇_add_activation_to_users.rb
に定義がある

authenticated?は、
/sample_app/app/models/user.rb
に定義がある

user.activateは、
/sample_app/app/models/user.rb
に定義がある

そこにあるupdate_columnsはrailsで普通に使える関数
テーブルの値を複数更新する時とかに使う

ちなみにupdate_columnsとupdate_columnの違いは下記
https://qiita.com/lemtosh469/items/371544fa4fd3c333adf1
1つだけの更新をするか、複数更新するかの違い

log_inは、
/sample_app/app/helpers/sessions_helper.rb
に定義がある

ここで、ようやく有効化に成功したときと失敗したときのメッセージが表示される処理にたどり着く。長かった・・・

ここを魔改造する。


17.4 最後に

保存する

rails test
git add -A
git status


ログ
----------------------------
ec2-user:~/environment/sample_app (rails-makaizou-17) $ git status
On branch rails-makaizou-17
Changes to be committed:
 (use "git reset HEAD <file>..." to unstage)
       modified:   app/controllers/account_activations_controller.rb
       modified:   app/controllers/users_controller.rb
       modified:   app/helpers/sessions_helper.rb
       modified:   app/mailers/user_mailer.rb
       modified:   app/models/user.rb
       modified:   app/views/static_pages/home.html.erb
       modified:   app/views/user_mailer/account_activation.html.erb
       modified:   app/views/user_mailer/account_activation.text.erb
       modified:   config/routes.rb
       modified:   test/mailers/user_mailer_test.rb
----------------------------


10ファイル修正したことを確認

git commit -m "add 17"
git checkout master
修正前ファイルをブログ用に保存しておく
git merge rails-makaizou-17


ログ
----------------------------
ec2-user:~/environment/sample_app (master) $ git merge rails-makaizou-17
Updating 6ea4cdb..f8c21a9
Fast-forward
.../account_activations_controller.rb          | 25 +++++++++++-
app/controllers/users_controller.rb            | 23 ++++++++++-
app/helpers/sessions_helper.rb                 |  2 +-
app/mailers/user_mailer.rb                     |  9 ++++-
app/models/user.rb                             | 39 ++++++++++++++++++-
app/views/static_pages/home.html.erb           |  7 ++++
.../user_mailer/account_activation.html.erb    | 10 ++---
.../user_mailer/account_activation.text.erb    |  4 +-
config/routes.rb                               |  6 +++
test/mailers/user_mailer_test.rb               |  8 ++--
10 files changed, 114 insertions(+), 19 deletions(-)
----------------------------


修正後ファイルをブログ用に保存しておく

git push origin master

そしてherokuの操作
source <(curl -sL https://cdn.learnenough.com/heroku_install)

差分の画像を保存したいので、
railsチュートリアル14章のデータベースをリセットするためにいったん切り替える。絶対にpushしないようにする。
git remote set-url heroku https://git.heroku.com/jun-killer-rails14.git


heroku maintenance:on
heroku pg:reset DATABASE
jun-killer-rails14
heroku run rails db:migrate
heroku run rails db:seed
heroku restart
heroku maintenance:off

そして、railsチュートリアル魔改造17章用にまた切り替える
こちらは必ずpushする

git remote set-url heroku https://git.heroku.com/jun-killer-makaizou.git

heroku maintenance:on
git push heroku master
heroku pg:reset DATABASE
jun-killer-makaizou
heroku run rails db:migrate
heroku run rails db:seed
heroku restart
heroku maintenance:off

https://jun-killer-rails14.herokuapp.com/
https://jun-killer-makaizou.herokuapp.com/

スクショを取るのは
・有効化前のメッセージ
・メールの内容
・有効化に失敗したとき
・有効化に成功したとき


17.4.1 修正後


リスト17.02:
app/controllers/account_activations_controller.rb
----------------------------
class AccountActivationsController < ApplicationController
 #メールアドレスからURLを辿ったあとに呼び出される
 def edit
   #メールアドレスを元に、テーブルから1件だけユーザー情報を取得する
   #params[:email]とparams[:id]は、本文のerb(viewのなんとかmailer)で作成される
   user = User.find_by(email: params[:email])
   
   #ユーザーが存在しており、有効化はまだ済んでおらず、ダイジェストとトークンが一致するとき
   #activatedはdb/migrate/〇〇_add_activation_to_users.rbに定義がある
   #authenticated?は/sample_app/app/models/user.rbに定義がある
   if user && !user.activated? && user.authenticated?(:activation, params[:id])
     #ユーザーを有効化する
     #app/models/user.rbに定義がある
     user.activate
     #ユーザーをログイン状態にする
     #app/helpers/sessions_helper.rbに定義がある
     log_in user
     
     #有効化に成功したときのメッセージを出力する
     flash[:success] = "railsチュートリアル魔改造へようこそ!"
     
     #ユーザーページへ
     redirect_to user
   #認証になんらかの理由で失敗したとき
   else
     #有効化に失敗したときのメッセージを出力する
     flash[:danger] = "申し訳ありません。認証に失敗しました。パスワードの変更をお願いします。"
     #トップページへ
     redirect_to root_url
   end
 end
end

リスト17.03:
app/controllers/users_controller.rb
----------------------------
class UsersController < ApplicationController
 before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                       :following, :followers]
 before_action :correct_user,   only: [:edit, :update]
 before_action :admin_user,     only: :destroy
 
 def index
   @users = User.where(activated: true).paginate(page: params[:page])
 end
 def show
   @user = User.find(params[:id])
   @microposts = @user.microposts.paginate(page: params[:page])
 end
 
 # /signup で呼ばれるアクション
 # app/views/users/new.html.erbへ
 def new
   #ユーザーを新規に作成する
   @user = User.new
 end
 
 # app/views/users/new.html.erbでform_forによって送信を押した後に呼ばれる
 def create
   #入力フォームに入力された値でユーザーデータを作成する
   #user_paramsは同ファイルのprivateに定義がある
   #user_paramsはStrongParameterが返ってくる
   @user = User.new(user_params)
   
   #セーブに成功した?
   if @user.save
     #メールを送信する
     #send_activation_emailの定義はapp/models/user.rbにある
     #メールの内容は以下で編集できる
     #app/views/user_mailer/account_activation.html.erb
     #app/views/user_mailer/account_activation.text.erb
     @user.send_activation_email
     #メールを送信したことを通知する
     flash[:info] = @user.email+"にメールを送信しました。お手数ですが、承認をお願いします。"
     #トップページに移行する
     redirect_to root_url
   #セーブに失敗した?
   else
     #再度入力をお願いする
     #エラー文は自動的に出るようになってる
     render 'new'
   end
 end
 def edit
 end
 def update
   if @user.update_attributes(user_params)
     flash[:success] = "Profile updated"
     redirect_to @user
   else
     render 'edit'
   end
 end
 
 def destroy
   User.find(params[:id]).destroy
   flash[:success] = "User deleted"
   redirect_to users_url
 end
 
 def following
   @title = "Following"
   @user  = User.find(params[:id])
   @users = @user.following.paginate(page: params[:page])
   render 'show_follow'
 end
 def followers
   @title = "Followers"
   @user  = User.find(params[:id])
   @users = @user.followers.paginate(page: params[:page])
   render 'show_follow'
 end
 private
   #paramsの中身を:userの:name,:email,:password,:password_confirmationのみにして返す
   #StrongParameterというやつ
   def user_params
     params.require(:user).permit(:name, :email, :password,
                                  :password_confirmation)
   end
   # beforeアクション
   # 正しいユーザーかどうか確認
   def correct_user
     @user = User.find(params[:id])
     redirect_to(root_url) unless current_user?(@user)
   end
   
       # 管理者かどうか確認
   def admin_user
     redirect_to(root_url) unless current_user.admin?
   end
end
リスト17.04:
app/helpers/sessions_helper.rb
----------------------------
module SessionsHelper
 # 渡されたユーザーをログイン状態にする
 def log_in(user)
   session[:user_id] = user.id
 end
 # ユーザーのセッションを永続的にする
 def remember(user)
   user.remember
   cookies.permanent.signed[:user_id] = user.id
   cookies.permanent[:remember_token] = user.remember_token
 end
 # 渡されたユーザーがログイン済みユーザーであればtrueを返す
 def current_user?(user)
   user == current_user
 end
 # 現在ログイン中のユーザーを返す (いる場合)
 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
 
 # ユーザーがログインしていればtrue、その他ならfalseを返す
 def logged_in?
   !current_user.nil?
 end
 
 # 永続的セッションを破棄する
 def forget(user)
   user.forget
   cookies.delete(:user_id)
   cookies.delete(:remember_token)
 end
 # 現在のユーザーをログアウトする
 def log_out
   forget(current_user)
   session.delete(:user_id)
   @current_user = nil
 end
 
   # 記憶したURL (もしくはデフォルト値) にリダイレクト
 def redirect_back_or(default)
   redirect_to(session[:forwarding_url] || default)
   session.delete(:forwarding_url)
 end
 # アクセスしようとしたURLを覚えておく
 def store_location
   session[:forwarding_url] = request.original_url if request.get?
 end
end
リスト17.05:
app/mailers/user_mailer.rb
----------------------------
class UserMailer < ApplicationMailer
 # アカウント有効化用初期設定
 #app/models/user.rbから呼ばれる
 def account_activation(user)
   #ユーザー情報を取得する(意味あるのか?)
   @user = user
   #ユーザーのメールアドレスに対して、件名を設定する
   #メールの内容は以下で編集できる
   #app/views/user_mailer/account_activation.html.erb
   #app/views/user_mailer/account_activation.text.erb
   mail to: user.email, subject: "railsチュートリアル魔改造へようこそ!"
 end
 def password_reset(user)
   @user = user
   mail to: user.email, subject: "Password reset"
 end
end

リスト17.06:
app/models/user.rb
----------------------------
class User < ApplicationRecord
 has_many :microposts, dependent: :destroy
 has_many :active_relationships,  class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
 has_many :passive_relationships, class_name:  "Relationship",
                                  foreign_key: "followed_id",
                                  dependent:   :destroy
 has_many :following, through: :active_relationships,  source: :followed
 has_many :followers, through: :passive_relationships, source: :follower
 
 #トークンを作成
 #リメンバーミー用トークン
 attr_accessor :remember_token
 #承認用トークン
 attr_accessor :activation_token
 #パスワードリセット用トークン
 attr_accessor :reset_token
 #データベースへ登録前と更新前のどちらかでメールアドレスを小文字にする
 before_save   :downcase_email
 
 #データベースへ登録前にトークンとダイジェストを作成する
 before_create :create_activation_digest
 
 #名前を表す
 #空白NG
 #最大50文字
 validates :name,  presence: true, length: { maximum: 50 }
                     
 #emailを表す
 #正規表現用の文字列をVALID_EMAIL_REGEXへ
 #空白NG
 #最大255文字
 #フォーマットはVALID_EMAIL_REGEXに従う
 #一意であること
 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
 validates :email, presence: true,
                   length: { maximum: 255 },
                   format: { with: VALID_EMAIL_REGEX },
                   uniqueness: { case_sensitive: false }
 has_secure_password
 validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
 # 渡された文字列のハッシュ値を返す
 def User.digest(string)
   cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                 BCrypt::Engine.cost
   BCrypt::Password.create(string, cost: cost)
 end
 
 # ランダムなトークンを返す
 def User.new_token
   SecureRandom.urlsafe_base64
 end
 
 # 永続セッションのためにユーザーをデータベースに記憶する
 def remember
   self.remember_token = User.new_token
   update_attribute(:remember_digest, User.digest(remember_token))
 end
 
 # トークンがダイジェストと一致したらtrueを返す
 #app/controllers/account_activations_controller.rbのeditアクションで呼ばれる
 def authenticated?(attribute, token)
   #メタプログラミングでactivation_digestインスタンスを取得する
   digest = send("#{attribute}_digest")
   #たぶんないが、念のためnilでないことを確認する
   return false if digest.nil?
   #インスタンスのダイジェストとトークンから生成したダイジェストが
   #一致すればtrue
   #一致しなければfalse
   BCrypt::Password.new(digest).is_password?(token)
 end
 
 # ユーザーのログイン情報を破棄する
 def forget
   update_attribute(:remember_digest, nil)
 end
 
 # アカウントを有効にする
 def activate
   #有効化状態と、有効化日時を更新
   update_columns(activated: true, activated_at: Time.zone.now)
 end
 # 有効化用のメールを送信する
 #app/controllers/users_controller.rbから呼ばれる
 def send_activation_email
   #メイラーを使ってメールを今すぐに送信する
   #account_activationの定義はapp/mailers/user_mailer.rbにある
   UserMailer.account_activation(self).deliver_now
 end
 # パスワード再設定の属性を設定する
 def create_reset_digest
   self.reset_token = User.new_token
   update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
 end
 # パスワード再設定のメールを送信する
 def send_password_reset_email
   UserMailer.password_reset(self).deliver_now
 end
 
 # パスワード再設定の期限が切れている場合はtrueを返す
 def password_reset_expired?
   reset_sent_at < 2.hours.ago
 end
 
 # ユーザーのステータスフィードを返す
 def feed
   following_ids = "SELECT followed_id FROM relationships
                    WHERE follower_id = :user_id"
   Micropost.where("user_id IN (#{following_ids})
                    OR user_id = :user_id", user_id: id)
 end
 # ユーザーをフォローする
 def follow(other_user)
   following << other_user
 end
 # ユーザーをフォロー解除する
 def unfollow(other_user)
   active_relationships.find_by(followed_id: other_user.id).destroy
 end
 # 現在のユーザーがフォローしてたらtrueを返す
 def following?(other_user)
   following.include?(other_user)
 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
リスト17.07:
app/views/static_pages/home.html.erb
----------------------------
<%# ログイン中? %>
<% if logged_in? %>
 <%# Bootstrapでくくる準備 %>
 <div class="row">
   <%# 12分割して左側幅4つ分を使用する %>
   <aside class="col-md-4">
     <%# ユーザーの情報を表示する %>
     <section class="user_info">
       <%= render 'shared/user_info' %>
     </section>
     <%# ステータスを表示する %>
     <section class="stats">
       <%= render 'shared/stats' %>
     </section>
     <%# マイクロポストフォームを表示する %>
     <section class="micropost_form">
       <%= render 'shared/micropost_form' %>
     </section>
   </aside>
   <%# 12分割して右側幅8つ分を使用する %>
   <div class="col-md-8">
     <%# 他の人のマイクロポストを表示する %>
     <h3>Micropost Feed</h3>
     <%= render 'shared/feed' %>
   </div>
 </div>
<%# ログインしていない? %>
<% else %>
 <%# タイトル %>
 <div class="center">
   <%= image_tag("title.jpg", alt: "title") %>
   <h1>railsチュートリアル<br>魔改造</h1>
   <%# サブタイトル %>
   <h2>
     全ページ……魔改造してやる!<br>このサイトから……一ページ残らず!
   </h2>
   <%# 新規登録ボタンを赤色(danger)にする %>
   <%= link_to "新規登録", signup_path, class: "btn btn-lg btn-danger signup " %>
 </div>
<% end %>

リスト17.08:
app/views/user_mailer/account_activation.html.erb
----------------------------
<p><%= @user.name %> さん、こんにちは!</p>
<p>
以下のURLをクリックすると、アカウントが有効になります。
</p>
<%# edit_account_activation_urlは、config/routes.rbで作成され、そのURLに色々とくっつける %>
<%= link_to "アカウントを有効にする", edit_account_activation_url(@user.activation_token,
                                                   email: @user.email) %>
                                                   
リスト17.09:
app/views/user_mailer/account_activation.text.erb
----------------------------
<%= @user.name %> さん、こんにちは!
以下のURLを開くと、アカウントが有効になります。
<%= edit_account_activation_url(@user.activation_token, email: @user.email) %>
リスト17.10:
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'
 
 # 新規登録ページは、/signup
 # app/controllers/users_controller.rbのnewアクションへ
 get    '/signup',  to: 'users#new'
 
 
 get    '/login',   to: 'sessions#new'
 post   '/login',   to: 'sessions#create'
 delete '/logout',  to: 'sessions#destroy'
 resources :users do
   member do
     get :following, :followers
   end
 end
 #アカウント有効化用ルーティングをeditのみ作成する
 #edit_account_activation_urlで、以下が実行される
 #app/controllers/users_controller.rbのeditアクション
 resources :account_activations, only: [:edit]
 
 
 resources :password_resets,     only: [:new, :create, :edit, :update]
 resources :microposts,          only: [:create, :destroy]
 resources :relationships,       only: [:create, :destroy]
end

リスト17.11:
test/mailers/user_mailer_test.rb
----------------------------
class UserMailer < ApplicationMailer
 # アカウント有効化用初期設定
 #app/models/user.rbから呼ばれる
 def account_activation(user)
   #ユーザー情報を取得する(意味あるのか?)
   @user = user
   #ユーザーのメールアドレスに対して、件名を設定する
   #メールの内容は以下で編集できる
   #app/views/user_mailer/account_activation.html.erb
   #app/views/user_mailer/account_activation.text.erb
   mail to: user.email, subject: "railsチュートリアル魔改造へようこそ!"
 end
 def password_reset(user)
   @user = user
   mail to: user.email, subject: "Password reset"
 end
end


17.4.2 本章のまとめ


新規登録後に表示されるメッセージを変えることができた
メールの内容を変更することができた。
有効化成功時と有効化失敗時のメッセージを変えることができた。
フォームからどのアクションが呼ばれるのか分かった。
paramsのrequireメソッドとpermitメソッドは、不要なパラメータを取り除き、必要なパラメータに絞り込むためのメソッドであることが分かった。
StrongParameterがなんのためにあるのか分かった。
deliver_nowについて意味を理解した
エンコードの関係で、メールの本文に日本語があると、railsチュートリアルのminitestが失敗することが分かった。対策方法までは踏み込まなかったが、gemを追加すると直るらしい。
ダイジェストとトークンの違いが分かった。ダイジェストは生成したトークンを元に作成されたあとにデータベースに保存されてて、トークンはURLとかに埋め込まれ、それを変換してデータベース上のダイジェストと比較していることが分かった。
before_createとbefore_saveがいつ呼ばれるのか分かった。
user.activatedとuser.authenticatedとuser.activateが紛らわしいなと思った。コメントで補足するのが吉と判断。

修正前

001 有効化前のメッセージ

002 メールの内容

003 有効化に失敗したとき

004 有効化に成功したとき



修正後

005有効化前のメッセージ

006 メールの内容

007 有効化に失敗したとき

008 有効化に成功したとき


この記事が気に入ったらサポートをしてみませんか?