title: Implementing a facebook-style messaging system, where a user can be in either of two different columns of another model
body: <p>I am trying to set up a messaging system similar to facebook where you have a list of messages sorted by conversations between two users (it is not important for multiple recipients at the moment, but maybe if I'd used a smarter design then it can be easily implemented in the future. I don't think it would be easy to add to what I currently have.) I have something kind of working, but I am unable to implement a few features. I am pretty new to rails / web programming, so any and all tips/hints/solutions is much appreciated.</p>
  
  <p>So I have three relevant models: User, Conversation, and Messages. User has many conversations, Conversation has many messages and belongs to Users, and Message belongs to Conversation. Okay, so this is what the models look like:</p>
  
  <p>User: has relevant fields ID:int, username:string</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
  
    has_many :conversations, :class_name =&gt; "Conversation", :finder_sql =&gt;
              proc { 
              "SELECT * FROM conversations " +
              "WHERE conversations.user1_id = #{id} OR conversations.user2_id = #{id} " +
              "ORDER BY conversations.updated_at DESC" }
  </code></pre>
  
  <p>Conversation: has relevant fields: ID:int, user1_id:int, user2_id:int, user1_deleted:boolean, user2_deleted:boolean, created_at:datetime, updated_at:datetime</p>
  
  <pre><code>class Conversation &lt; ActiveRecord::Base
    has_many :messages  
  
    belongs_to :participant_one, :class_name =&gt; "User", :foreign_key =&gt; :user1_id
    belongs_to :participant_two, :class_name =&gt; "User", :foreign_key =&gt; :user2_id
  
    private
  
      def self.between(user1, user2)
        c = Conversation.arel_table
        Conversation.where(c[:user1_id].eq(user1).and(c[:user2_id].eq(user2)).or(c[:user1_id].eq(user2).and(c[:user2_id].eq(user1))))
  </code></pre>
  
  <p>Message: has relevant fields: id:int, conversation_id:int, author_id:int, content:text, created_at:datetime, updated_at:datetime</p>
  
  <pre><code>class Message &lt; ActiveRecord::Base
    belongs_to :conversation, :touch =&gt; true  
  </code></pre>
  
  <p>I'm not really sure if I need participant_one and participant_two, but I use </p>
  
  <pre><code>  def conversation_partner(conversation)
      conversation.participant_one == current_user ? conversation.participant_two : conversation.participant_one
    end
  </code></pre>
  
  <p>in a ConversationHelper so that in views, I can show the other participant. </p>
  
  <p>So this basically works. But one of the complications I have is that I do not really distinguish the users very well in the Conversation, a user can be in either the user1 field or the user2 field. So I need to constantly look for the user in one or the other field, e.g. in the finder_sql of the User has_many declaration. Also, when I create a new message, I first search to see if there's a Conversation parameter, or if there isn't one, see if there's a conversation between the two users, and if not, then create a new conversation. (You can either send a message from the conversation index (like a reply), or the current_user can be viewing the another user and click on the "send this user a message" link. The messagecontroller looks like this, and uses that self.between method in the Conversation model:</p>
  
  <pre><code>class MessagesController &lt; ApplicationController
    before_filter :get_user
    before_filter :find_or_create_conversation, :only =&gt; [:new, :create]
  
    def new
       @message = Message.new
    end
  
    def create
      @message = @conversation.messages.build(params[:message])
      @message.author_id = current_user.id
      if @message.save
        redirect_to user_conversation_path(current_user, @conversation), :notice =&gt; "Message sent!"
      else
        redirect_to @conversation
      end
    end
  
    private
       def get_user
        @user = User.find(params[:user_id])
      end
  
      def find_or_create_conversation
        if params[:conversation_id]
          @conversation = Conversation.find(params[:conversation_id]) 
        else
          @conversation = Conversation.between(@user.id, current_user.id).first or @conversation = Conversation.create!(:user1_id =&gt; current_user.id, :user2_id =&gt; @user.id)
        end
      end
  </code></pre>
  
  <p>(my routes look like this:)</p>
  
  <pre><code>  resources :users do
      resources :conversations, :only =&gt; [:index, :create, :show, :destroy] do
        resources :messages, :only =&gt; [:new, :create]
      end
      resources :messages, :only =&gt; [:new]
    end
  </code></pre>
  
  <p>So now, I am having problems trying to set the user1_deleted or user2_deleted flags. (and similarly if/when i implement a read/up-to-date flag). The problem is that because the same User can have many conversations, but he can either be the user1 or the user2, it becomes difficult to find him. I was thinking I can do something like this in the Conversation model:</p>
  
  <pre><code>def self.active(user)
    Conversation.where(which_user?(user) + "_deleted = ?", false)
  end
  
  def self.which_user?(user)
    :user1_id == user ? 'user1' : 'user2'
  end
  </code></pre>
  
  <p>But then you can't run it an entire conversation unless you iterate through each of the User's conversation one by one, because sometimes he is user1, and sometimes he is user2. Should I ditch this whole approach and try a new design? If so, does anyone a possible approach that would be more elegant/perform better/actually work and still meet the same needs?</p>
  
  <p>This is a pretty long question, so I appreciate anyone willing to wade through all this with me. Thanks.</p>