Maurice Maurice - 1 month ago 9
Ruby Question

If users have messages between them and a user deletes his account, the other user can no longer access the messages index in Rails

I followed a tutorial to allow users to send each other messages on my app. I'm using devise for authentication. Basically if two or more users have conversations between them and one or more cancels their account, the remaining can no longer access the index page for conversations. I get

ActiveRecord::RecordNotFound
in
ConversationsController#index
.

The code in bold is where the error was identified. What would be the best way to solve this?

Thanks in advance

Index view:

<% @conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
**<% recipient = User.find(conversation.recipient_id) %>**
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<tr>
<td><%= link_to (image_tag recipient.avatar.url, size: "50x50"), user_path(recipient) %></td>
<td><%= recipient.full_name %></td>
<td><%= link_to "View Message", conversation_messages_path(conversation) %></td>
</tr>
<% end %>
<% end%>


The Controller:

class ConversationsController < ApplicationController
def index
@users = User.all
@conversations = Conversation.all
end

def create
if Conversation.between(params[:sender_id],params[:recipient_id])
.present?
@conversation = Conversation.between(params[:sender_id],
params[:recipient_id]).first
else
@conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(@conversation)
end

private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end


The Model:

class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, class_name: 'User'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'User'

has_many :messages, dependent: :destroy

validates_uniqueness_of :sender_id, :scope => :recipient_id

scope :between, -> (sender_id,recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id =?) OR (conversations.sender_id = ? AND conversations.recipient_id =?)", sender_id,recipient_id, recipient_id, sender_id)
end
end

Answer

Yeah, it's a problem, and you have a lot of calls that are made on the non-existent user records.

I'd suggest you replace...

recipient = User.find(conversation.recipient_id)

and

recipient = User.find(conversation.sender_id) 

with...

recipient = get_the_user(conversation.recipient_id)
recipient = get_the_user(conversation.sender_id)

You'll need a helper method... you can put it in your ApplicationController

class ApplicationController

  helper_method :get_the_user

  DummyUser= Struct.new(:full_name)

  def get_the_user(id)
    user = User.find_by(id: id)
    return user if user
    user = DummyUser.new('deleted user')
  end

And as you can't link to the nonexistent user, change

 <%= link_to (image_tag recipient.avatar.url, size: "50x50"), user_path(recipient) %>

into

 <%= recipient.class == User ? link_to (image_tag recipient.avatar.url, size: "50x50"), user_path(recipient) : 'no link available' %>