RubyOnRails - PrivatePub Gem - Multiple Publishes to view with single Subscription

590 views Asked by At

I'm using private_pub as a gem for implementing a notification system much like the one Facebook provides. For that, I am subscribing to a channel in a View and publishing from various places (different Controllers).

What's happening is that sometimes I get a lot of publishes for a single subscription. Do you have any idea about why this could happen?

My first suspicion was that I might be calling my notify method too many times (in some kind of loop or something, but I can see that it is only being called once, so I guess the problem must be somewhere in between the pub/sub layer, and most likely because of something I am doing wrong when notifying the View.

Next I present some snippets of my implementation.

In the _header.html.erb partial of my website, I subscribe to the user's notification channel, like so:

For instance in my friendships_controller, when adding a friend I generate some html to present the notification and publish the jquery with the prepended notification, like so:

html_text = render_to_string(:partial => 'notifications/notification', :locals => { notification: notification }, :formats => [:html]).squish jquery = "$('#notifications_" + user.id.to_s + "').prepend('#{html_text}');" PrivatePub.publish_to("/notifications/" + user.id.to_s, jquery)

Sorry for the long post, I hope some of you can help me. Thanks in advance.

Btw, I am running Rails 4.0.0 with Ruby 2.0.0p247.

3

There are 3 answers

0
stephan.com On

I've been getting this problem, too, I think... I suspect I'm multiply subscribing somehow, and I suspect it has something to do with Turbolinks. The extra subscriptions appear to pile up when you visit other pages without a full reload. Maybe some sort of cacheing?

I've come up with a sleazy workaround that does the job. Not a great solution, though, I'd rather know why the multiple subscribe is happening. Still, this pattern works to kill the problem.

Basically, just delete anything you're about to insert into the DOM before you add it. Usually this will be an object you've marked with a dom_id, but if you're inserting multiple things, it might be more convenient to add some class that lets you get them all.

Let's say you're creating some @object which has a partial that you're inserting into the dom, appending it to #some_destination. In your create.js.slim (convert mentally to erb/haml/etc if you must)

- publish_to some_channel do
  | $('##{dom_id @object').remove();
  | $('#some_destination').append('#{j render(@object)');

and then in the partial _object.html.slim you make sure it's marked properly

div id=dom_id(object)
  = object.to_text

Sleazy but it works. I would prefer to know why I'm getting these multiple subscriptions to the same channel and how to stop them, that would be much better than this workaround.

0
Estu On

I know it's an old question, but some people may still be struggling with this issue.

In my case, I was subscribing to the same channel multiple times because the subcribe_to method was inside a tab that was loaded via AJAX. So everytime I selected that tab without reloading the page, it would subscribe me again to the same channel resulting in multiple publish_to executions equal to the number of times I selected that tab.

While this might not be the exact same scenario that you are facing, this solution may very well suit your needs.

What I've done to deal with this issue is to override the class private_pub/lib/private_pub/view_helpers.rb and the method subscribe_to with this one:

module PrivatePub
  module ViewHelpers

    # Subscribe the client to the given channel. This generates
    # some JavaScript calling PrivatePub.sign with the subscription
    # options.
    def subscribe_to(channel)
      subscription = PrivatePub.subscription(:channel => channel)
      content_tag "script", :type => "text/javascript" do
        raw(
          "if(typeof PrivatePub.subscriptions['#{channel}'] === 'undefined') {
            PrivatePub.sign(#{subscription.to_json});
          }")
      end
    end
  end
end

What I've changed is the JavaScript code that is embedded in the html when the subscribe_to method is called by adding an if statement that checks whether the channel already exists in the subscriptions object or not. If it doesn't exists, it lets you subscribe to it, otherwise it will not.

raw(
    "if(typeof PrivatePub.subscriptions['#{channel}'] === 'undefined') {
       PrivatePub.sign(#{subscription.to_json})
     }")

Hope this helps!

0
Luca Faggianelli On

I think we have the same problem: sometimes I get multiple notification of the same message...for example in the chat I receive multiple identical messages, but if I reload the page and the chat, the message is only one!

Well in my case the problem is that I use AJAX for loading pages (and also to load the chat) so basically I always have the same page and loading twice the chat, that is load the chat, close the chat and load it again, it results in having two subscriptions to the same channel, thus two identical messages!

At present time there isn't any 'unsubscribe' method, so the only way two avoid that is to be sure to make the subscription only once per page load...

hope to be useful