0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rails Tutorial の拡張機能:フォロワーの通知機能を作ってみた

Posted at

Rails Tutorialの第14章にある、拡張機能を作る件の続きです。

今回はフォロワーの通知機能を作ります。
##要件を定義
チュートリアルに要件が書いてあります。

ユーザーに新しくフォロワーが増えたときにメールで通知する機能を実装してみましょう。
続いて、メールでの通知機能をオプションとして選択可能にし、不要な場合は通知をオフにできるようにしてみましょう

Twitterに同じような機能があるか調べます。
・「通知」の画面がありました。「xxxさんにフォローされました」と表示されます。
・「メール通知」をオンやオフに設定できる画面があります。

要件はこの2点とします。
##仕様を設計
要件から具体的な機能に落とし込みます。

1.フォロワーが増えたときに、メールで通知する

フォローボタンを押したときに、メールを送信することをコントローラーに追加します。
コントローラーに作ることができるか、確認のために見てみます。

app/controllers/relationships_controller.rb
  def create
      @user = User.find(params[:followed_id])
      current_user.follow(@user)
      respond_to do |format|
        format.html { redirect_to @user }
        format.js
      end
  end

followメソッドでフォローの細かい中身を実行しているようなので、その後の行にメール送信を追加すればよさそうです。

メール送信はチュートリアルの「第11章アカウントの有効化」を参考にします。

2.「メール通知」をオンやオフに設定できる
大きく2つあり、一つは設定を入力できること、もう一つはその設定でメール送信するかしないか動作を変更することです。

2.1 動作の変更機能
後者の動作の変更は、メール送信の際にif文で判定することでできそうです。

2.2 設定できる機能
前者の設定を入力できることを分解すると、一つは設定を入力する画面と、もう1つは入力した値を保持するためのモデルです。

2.2.1 設定画面
設定を入力する画面は、入力後に正しく入力できたか確認したいと思うので、表示する画面も必要です。
Twiiterの例と同様に、ユーザーごとの設定です。
設定は自分だけができるように制限をします。
既存のユーザー変更画面に項目を追加すればできそうです。

2.2.2 設定を保持するモデル
既存のユーザーモデルに項目を追加すればできそうです。

3.その他
Twitterでは通知はメールだけでなく通知の画面でも確認することができます。

作り手の視点では、なくても機能として問題なく作れます。

利用者の視点では、なくても自分のメールボックスで過去のメールを見れば分かるので、困ることはないです。

以上から今回は作らないことにします。

##モデルの追加

現在のユーザーモデルをコンソールで確認します。

>> User.first
  User Load (1.0ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-11-11 23:26:45", updated_at: "2020-11-11 23:26:45", password_digest: "$2a$10$7XL.tADLyZ5OdhbHWb6Eh.MVIbc1QpHR.cuu9yqe5or...", remember_digest: nil, admin: true, activation_digest: "$2a$10$ZXWhU/7mCD5JjJXuGHDnPeHIEXVzlmNVAvSj2h3fIXx...", activated: true, activated_at: "2020-11-11 23:26:45", reset_digest: nil, reset_sent_at: nil, unique_name: "Example">

最後の列がunique_nameなので、返信機能で変更したのが最新版だと分かります。https://qiita.com/sadao2005/items/cc94d1f8673c14d198f1
を読み、以下のように列を追加します。

列名 属性
id integer
name string
email string
.. ..
unique_name string
follow_notify boolean
図.フォロー通知の列を追加したUserモデル

トピックブランチを作ります。

ubuntu:~/environment/sample_app (master) $ git checkout -b follow_notify

モデルに列を追加するmigrateを生成します。

buntu:~/environment/sample_app (reply-micropost) $ rails generate migration add_unique_name_to_users unique_name:string

列を追加するmigrateを書きます。

db/migrate/20201215115547_add_follow_notify_to_users.rb
class AddFollowNotifyToUsers < ActiveRecord::Migration[5.1]
  def change
    add_column :users, :follow_notify, :boolean, default: false, null: false
  end
end
ubuntu:~/environment/sample_app (follow_notify) $ rails db:migrate

consoleで確認します。

> user = User.first
>> user.follow_notify
=> false

モデルが出来ました。
##メイラーを作成
次に、メールを送信する方法について、チュートリアルの「11.2 アカウント有効化のメール送信」を読みます。
Notifyメイラーを生成します。

ubuntu:~/environment/sample_app (follow_notify) $ rails generate mailer NotifyMailer follow_notify
Running via Spring preloader in process 2828
      create  app/mailers/notify_mailer.rb
      create    app/views/notify_mailer
      create    app/views/notify_mailer/follow_notify.text.erb
      create    app/views/notify_mailer/follow_notify.html.erb
      create    test/mailers/notify_mailer_test.rb
      create    test/mailers/previews/notify_mailer_preview.rb

viewとテストも合わせてファイルが生成されました。

メールのテンプレートを変更します。
送信先と、件名を設定します。コントローラーで使っている変数の@userを使います。

app/mailers/notify_mailer.rb
  def follow_notify
    mail to: current_user.email, subject: "follow notification"
  end

メール本文を作成します。

app/views/notify_mailer/follow_notify.html.erb
<h1>You are followed</h1>

<p>Hi <%= current_user.name %>,</p>

<p>You are followed by <%= @user.name %>.</p>

テストのため、メイラーのプレビューファイルを更新します。
設定の必要があるのか分かりませんでしたので変更しません。

プレビューにブラウザーからアクセスします。

エラーになりました。

NameError in Rails::MailersController#preview
undefined local variable or method `current_user' for #<NotifyMailer:0x00007f64b45086e0>

やはりプレビューファイルで値を設定する必要があると分かりました。

current_userとフォローされたユーザーをメソッドの変数で渡すように変更します。

test/mailers/previews/notify_mailer_preview.rb
  def follow_notify
    user = User.first
    followed_user = User.second
    NotifyMailer.follow_notify(user, followed_user)
  end
app/mailers/notify_mailer.rb
  def follow_notify(user, followed_user)
    @user = user
    @followed_user = followed_user
    mail to: user.email, subject: "follow notification"
  end
app/views/notify_mailer/follow_notify.html.erb
You are followed

Hi <%= @user.name %>,

You are followed by <%= @followed_user.name %>.

プレビューで表示できました。

notify1.png

notify2.png

###テストを作成
テストを作ります。
「リスト 11.19: Userメイラーのテスト (Railsによる自動生成)」を読みます。

 test/mailers/notify_mailer_test.rb
  test "follow_notify" do
    user = users(:michael)
    followed_user = users(:archer)
    mail = NotifyMailer.follow_notify(user, followed_user)
    assert_equal "follow notification", mail.subject
    assert_equal [user.email], mail.to
    assert_equal ["app173424233@heroku.com"], mail.from
    assert_match user.name,          mail.body.encoded
    assert_match followed_user.name, mail.body.encoded
  end

GREENになりました。

followするときにメールを送信するようアクションを変更

followするときにメールを送信するようアクションを変更します。
どこに変更をするかは、上記のとおりrelationships_controller.rbです。

app/controllers/relationships_controller.rb
  def create
      @user = User.find(params[:followed_id])
      current_user.follow(@user)
      if @user.follow_notify 
        NotifyMailer.follow_notify(current_user, @user).deliver.now
      end
      respond_to do |format|
        format.html { redirect_to @user }
        format.js
      end
  end

rails serverでフォローしてみて、メールが送られたかログを見ます。
テストのためにユーザーのfollow_notifyをtrueに変更します 。
コンソールで行います。

>> user = User.first
>> user.follow_notify = true
>> user.follow_notify
=> true
>> user.save

@userとcurrent_userが逆なことに気が付いたので修正します。

app/controllers/relationships_controller.rb
      @user = User.find(params[:followed_id])
      current_user.follow(@user)
      if @user.follow_notify 
        NotifyMailer.follow_notify(@user, current_user).deliver_now
      end

メールが作成されました。

Sent mail to example@railstutorial.org (9.1ms)
Date: Sat, 19 Dec 2020 02:26:38 +0000
From: appXXXX3@heroku.com
To: example@railstutorial.org
Subject: follow notification

You are followed

Hi Example User,

You are followed by Vivianne Sporer.

integration testを作ります。フォローのテストに追加します。

test/integration/following_test.rb
  def setup
    @user  = users(:michael)
    @other = users(:archer)
    log_in_as(@user)
    ActionMailer::Base.deliveries.clear

  test "should follow a user the standard way" do
    assert_difference '@user.following.count', 1 do
      post relationships_path, params: { followed_id: @other.id }
    end
    assert_equal 1, ActionMailer::Base.deliveries.size
  end
 test_should_follow_a_user_the_standard_way#FollowingTest (0.82s)
        Expected: 1
          Actual: 0
        test/integration/following_test.rb:34:in `block in <class:FollowingTest>'

@otherであるarcherのfollow_notifyがfalseのため、REDになります。
fixtureのfollow_notifyをtrueに変更します。

test/fixtures/users.yml
archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>  
  unique_name: Archer1
  follow_notify: true

GREENになりました。

###「メール通知」の設定機能を作成
「メール通知」をオンやオフに設定する機能を作ります。
前に調べた通り、既存のユーザー変更画面に項目を追加します。
ユーザー編集のビューを見ます。

app/views/users/edit.html.erb
    <%= render 'form' %>  

formを呼び出しているので見ます。

チェックボックスが、ユーザーを記憶する画面にあったことを思い出し読みます。
「9.2 [Remember me] チェックボックス」

      <%= f.label :remember_me, class: "checkbox inline" do %>
        <%= f.check_box :remember_me %>
        <span>Remember me on this computer</span>
      <% end %>

form_forでbooleanの列はどう指定するのかネットで調べます。
https://qiita.com/tanutanu/items/b86c4adc26ae464c71fd

関連するモデルがあるときは、check_boxメソッドを使うと分かりました。

ちなみに、check_boxではなく、f.check_boxの形にすると(つまり、form_withやform_forの中で使うと、第1引数(オブジェクト)が省略できるようです。

とありました。

app/views/users/_form.html.erb
   <%= form_for(@user, url: yield(:url_path) ) do |f|%>
    
...
    <%= f.label :password_confirmation %>
    <%= f.password_field :password_confirmation, class: 'form-control' %>

    <%= f.label :follow_notify %>
    <%= f.check_box :follow_notify, class: 'form-control' %>


    <%= f.submit yield(:button_text), class: "btn btn-primary" %>
    <% end %>    

画面に表示してみます。
notify3.png

更新したところ画面ではエラーが出ませんでしたが、ログにはエラーらしきものが出ていました。

Started PATCH "/users/1" for 133.106.33.48 at 2020-12-20 01:34:53 +0000
Processing by UsersController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"m2dZ6rvG6WmcX4kasgUW6qZzSngJNHZpszjdEOyaIfUXp5xpihm7OhBWm9USAUmA1t2VfXn9JSixF5MO26lHYg==", "user"=>{"name"=>"Example User", "email"=>"example@railstutorial.org", "unique_name"=>"Example", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "follow_notify"=>"1"}, "commit"=>"Save changes", "id"=>"1"}
Unpermitted parameter: :follow_notify

permitをどこかで設定したことを思い出したので、調べます。ユーザーのupdateのところではないかとあたりをつけたところ、ありましたので、追加します。

app/controllers/users_controller.rb
    def user_params
        params.require(:user).permit(:name, :email, :password, 
                                     :password_confirmation, :unique_name, :follow_notify)
    end

更新のため、チェックボックスを外します。無事成功しました。

更新できたか、データをコンソールで確認します。

>> User.first
  User Load (0.2ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2020-11-11 23:26:45", updated_at: "2020-12-20 02:04:03", password_digest: "$2a$10$7XL.tADLyZ5OdhbHWb6Eh.MVIbc1QpHR.cuu9yqe5or...", remember_digest: nil, admin: true, activation_digest: "$2a$10$ZXWhU/7mCD5JjJXuGHDnPeHIEXVzlmNVAvSj2h3fIXx...", activated: true, activated_at: "2020-11-11 23:26:45", reset_digest: nil, reset_sent_at: nil, unique_name: "Example", follow_notify: false>

falseに更新されていました。

チェックボックスのレイアウトが中央寄せなのを、分かりやすいように、変更します。

app/assets/stylesheets/custom.scss
   <%= f.label :follow_notify %>
    <%= f.check_box :follow_notify, class: 'checkbox inline' %>


  #user_follow_notify {
    width: auto;
    margin-left: 0;
  }
  
  
  #session_remember_me {
    width: auto;
    margin-left: 0;
  }
app/views/users/_form.html.erb
    <%= f.label :follow_notify %>
    <%= f.check_box :follow_notify, class: 'checkbox inline' %>

notify4.png

ラベルとチェックボックスを同じ行にはできませんでしたが、よしとします。

###テストを作成
テストを作ります。
ユーザーの編集のテストに項目を追加します。GREENになりました。

test/integration/users_edit_test.rb
 test "successful edit" do
... 
   follow_notify = false
    patch user_path(@user), params: { user: { name: name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "",
                                              unique_name: unique_name,
                                              follow_notify: follow_notify } }

これで「フォロワーの通知」機能は完成です。

#所要時間

12/13から12/20までの8.0時間です

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?