Rails Tutorial 拡張機能のメッセージ機能を作ってみた(その5):削除機能


Rails Tutorialの第14章にある、メッセージ機能を作る件の続きです。

前回までで受信したDMが表示される機能ができました。
今回はDMを削除する機能を作ります。

DMを削除する機能の設計

DMを削除する機能を作ります。13.3.4を読みます。
micropostは自分が投稿したもののみ削除できました。DMでは、自分が受信したDMのみ削除できるようにします。削除はモデルを作った際に決めたとおり、deletedフラグをTRUEにします。

destroyアクション作成

destroyアクションを作ります。
「リスト 13.52: Micropostsコントローラのdestroyアクション 」を読みます。
削除対象のDMは、現在のユーザーが受信者になっているかをチェックします。
consoleで試してみます。

>> user1 = User.third
 => #<User id: 3, name: "Vivianne Sporer", 

>> user1.received_dms
=> #<ActiveRecord::Associations::CollectionProxy [#<Dm id: 305, content: "content5", sender_id: 1, receiver_id: 3, deleted: false, created_at: "2020-11-23 23:18:33", updated_at: "2020-11-23 23:18:33">, #<Dm id: 304, content: "content4", sender_id: 1, receiver_id: 3, deleted: false, created_at: "2020-11-23 00:45:49", updated_at: "2020-11-23 00:45:49">, 

>> dm1 = user1.received_dms.find_by(id: 305)                                                                              
=> #<Dm id: 305, content: "content5", sender_id: 1, receiver_id: 3, deleted: false, created_at: "2020-11-23 23:18:33", updated_at: "2020-11-23 23:18:33">

>> dm1.nil?
=> false

削除はdeletedフラグをTRUEにする更新なので、参考にユーザーの更新を読みます。
「リスト 11.31: アカウントを有効化するeditアクション 」を参考にします。

      user.update_attribute(:activated,    true)
      user.update_attribute(:activated_at, Time.zone.now)

試しに削除してみます。

コンソールで調べると、deletedフラグが正しく更新されています。

>> Dm.find(305)

=> #<Dm id: 305, content: "content5", sender_id: 1, receiver_id: 3, deleted: true, created_at: "2020-11-23 23:18:33", updated_at: "2020-11-29 02:02:01">

一覧画面では削除したのに表示されたままです。
chatを変更する必要があるからと分かりました。
コンソールでSQL文を確かめます。

>>     Dm.where("sender_id = :user_id OR (receiver_id = :user_id AND deleted = :flag) ", user_id: 3, flag: false)       

chatを変更します。

app/models/user.rb
 def chat
    #Dm.where("sender_id = ?", id)
    Dm.where("sender_id = :user_id OR (receiver_id = :user_id AND deleted = :flag) ", user_id: id, flag: false)
  end

chatを変更したことで、直す必要があるところがあるかもしれないので確認していきます。
コントローラーdms_controller.rbを読みます。indexメソッドでchatを使っていますが直す必要はなさそうです。

テストを実行してみます。REDになりました。

ubuntu:~/environment/sample_app (create-dm) $ rails test
 test_DM_interface#DmsTest (2.72s)
        <Michael Example> expected but was
        <from:Michael Example>..
        Expected 0 to be >= 1.
        test/integration/dms_test.rb:54:in `block in <class:DmsTest>'

前に行った「From」を加える変更をしたときに、テストも直す必要があったようです。どうやら変更後にテストをしていなかったようです。テストを修正しGREENになりました。

test/integration/dms_test.rb
    assert_select "span.user", "from:#{@user.name}"

削除のテストを書きます。「13.3.5 フィード画面のマイクロポストをテストする」を読みます。
テストする項目の一つ目は、receiverが自分以外のユーザーのマイクロポストは削除をしようとすると、適切にリダイレクトされることです。画面にはdeleteの選択肢が出ないので、このテストの対象は、人が画面から入力するケースではなく、クローラーなどのツールからPostするケースです。

「リスト 13.54: 間違ったユーザーによるマイクロポスト削除に対してテストする 」を読んで気が付いたのですが、
ログインしていないとDMの送信も削除もできないテストも必要そうなので、合わせて作ります。

「リスト 13.31: Micropostsコントローラの認可テスト」を参考にします。

test/controllers/dms_controller_test.rb
  def setup
    @dm = dms(:morning)
  end
.. 
  test "should redirect create when not logged in" do
    assert_no_difference 'Dm.count' do
      post dms_path, params: { dm1: { content: "hogehoge",
                                      receiver_name: "hoge_receiver"  } }
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when not loggged in" do
    assert_no_difference 'Dm.count' do
      delete dm_path(@dm)
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy from wrong user" do
    log_in_as(users(:michael))
    assert_no_difference 'Dm.count' do
      delete dm_path(@dm)
    end
    assert_redirected_to dms_path
  end

テストがGREENになりました。

削除のテスト作成

統合テストに削除を追加します。テスト項目は、
・DMの削除
・送信者のDMには [delete] リンクが表示されないことを確認
です。

test/integration/dms_test.rb

  test "DM interface" do
    # 受信者が削除
    assert_select 'a', text: 'delete'
    first_dm = @receiver.received_dms.first
    assert_difference ' Dm.count', -1 do
      delete dm_path(first_dm)
    end

実行するとREDになりました。deleteしても数が減っていません。

test_DM_interface#DmsTest (1.37s)
        " Dm.count" didn't change by -1.
        Expected: 34
          Actual: 35

Dmの数は変わっていなくて、削除フラグを更新していたことを思い出しました。

test/integration/dms_test.rb
    # 受信者が削除
    assert_select 'a', text: 'delete'
    first_dm = @receiver.received_dms.first
    assert_difference '@receiver.chat.count', -1  do
      delete dm_path(first_dm)
    end

GREENになりました。

送信者のDMには [delete] リンクが表示されないことを確認するテストを考えます。

送信者=受信者のケースを考えると、このテストは逆にして、
受信者が自分でないDMには[delete]リンクが表示されないことを確認
にしたほうがよいと考えました。

ユーザーの削除リンクのテストを読みます。
「[リスト 10.62: 削除リンクとユーザー削除に対する統合テスト 」にありました。

   first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end

参考にしてテストを作り、GREENになりました。

test/integration/dms_test.rb
   # 受信者ば自分でないDMは削除リンクが表示されない
    delete logout_path
    log_in_as(@user)
    get dms_path

    first_page_of_chat_items = @user.chat.paginate(page: 1)
    first_page_of_chat_items.each do |chat|
      if chat.receiver_id == @user.id 
        assert_select 'a[href=?]', dm_path(chat), text: 'delete' 
      else
        assert_select 'a[href=?]', dm_path(chat), text: 'delete', count: 0
      end
    end

すべてのテストがGREENになり、完成です。

所要時間

11/29から12/5までの5.0時間です。

メッセージ機能全体では、
10/31から12/5までの33.0時間です。