Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

Rails javascript polling

I'm trying to implement javascript polling in my app but I'm running into a few problems. I'm pretty much following along with this railscasts. My problem is in trying to prepending any new data found. It prepends all of the data old and new and if there isn't any new data found it just prepends all of the old data. My other problem is that my setTimeout is only being called once, even after I try to keep it polling like they show in railscast. Below is my code. What am I doing wrong here?

polling.js

var InboxPoller;

InboxPoller = {
  poll: function() {
    return setTimeout(this.request, 5000);
  },
  request: function() {
    return $.getScript($('.inbox_wrap').data('url'), {
      after: function() {
        $('.conversation').last().data('id')
      }
    });
  }
};

$(function() {
  if ($('.inbox_wrap').length > 0) {
    return InboxPoller.poll();
  }
});

polling.js.erb

$(".inbox_wrap").prepend("<%= escape_javascript(render @conversations, :locals => {:conversation => @conversation}) %>");
InboxPoller.poll();

conversations_controller.rb

class ConversationsController < ApplicationController
    before_filter :authenticate_member!
    helper_method :mailbox, :conversation

    def index
        @messages_count = current_member.mailbox.inbox({:read => false}).count
        @conversations = current_member.mailbox.inbox.order('created_at desc').page(params[:page]).per_page(15)
    end

    def polling 
        @conversations = current_member.mailbox.inbox.where('conversation_id > ?', params[:after].to_i)
    end 

    def show
        @receipts = conversation.receipts_for(current_member).order('created_at desc').page(params[:page]).per_page(20)

        render :action => :show
        @receipts.mark_as_read 
    end

    def create
        recipient_emails = conversation_params(:recipients).split(',').take(14)
        recipients = Member.where(user_name: recipient_emails).all

        @conversation = current_member.send_message(recipients, *conversation_params(:body, :subject)).conversation

        respond_to do |format|
          format.html { redirect_to conversation_path(conversation) }
          format.js
        end  
    end

    def reply
        @receipts = conversation.receipts_for(current_member).order('created_at desc').page(params[:page]).per_page(20)
        @receipt = current_member.reply_to_conversation(conversation, *message_params(:body, :subject))

        respond_to do |format|
          format.html { conversation_path(conversation) }
          format.js 
        end
    end

    private

      def mailbox
          @mailbox ||= current_member.mailbox
      end

      def conversation
          @conversation ||= mailbox.conversations.find(params[:id])
      end

      def conversation_params(*keys)
          fetch_params(:conversation, *keys)
      end

      def message_params(*keys)
          fetch_params(:message, *keys)
      end

      def fetch_params(key, *subkeys)
          params[key].instance_eval do
            case subkeys.size
              when 0 then self
              when 1 then self[subkeys.first]
              else subkeys.map{|k| self[k] }
            end
          end
      end

      def check_current_subject_in_conversation
          if !conversation.is_participant?(current_member)
            redirect_to conversations_path
          end
      end

end

index.html.erb

<%= content_tag :div, class: "inbox_wrap", data: {url: polling_conversations_url} do %>
    <%= render partial: "conversations/conversation", :collection => @conversations, :as => :conversation %>
<% end %>

_conversation.html.erb

<div id="conv_<%= conversation.id %>_<%= current_member.id %>" class="conversation" data-id="<%= conversation.id %>">

    <div class="conv_body">
        <%= conversation.last_message.body %>
    </div>

    <div class="conv_time">
        <%= conversation.updated_at.localtime.strftime("%a, %m/%e %I:%M%P") %>
    </div>

</div>

1 Answer

Brandon Barrette
Brandon Barrette
20,485 Points

I'm going to take a stab at it. I've implemented polling as well for notifications. My guess is here:

def polling 
    @conversations = current_member.mailbox.inbox.where('conversation_id > ?', params[:after].to_i)
end 

You are sending in some params "after". Are you sure that's making it through correctly? In your development log, do you see the call to rails execute properly?

My guess is the after params is not working properly. If your after function in javascript calls nil, then in your controller, you call params[:after].to_i, which turns to 0 (because nil.to_i = 0). Thus when you call conversation_id > 0, it returns everything. So I would investigate by sending just an integer into your JS after function first. Then seeing if it works (then you know it's not rails). If that's the case, then you can work on fixing the javascript to send the proper ID and set a condition to not ping the database if nil.

Hope that helps, if I've missed something, please add more to your question or explain what you've tried and I can try to help.