ryanb / private_pub

Handle pub/sub messaging through private channels in Rails using Faye.
MIT License
864 stars 228 forks source link

Support for wildcard subscriptions #12

Open BinaryMuse opened 13 years ago

BinaryMuse commented 13 years ago

When subscribing to channels, Faye allows the channel passed to end in either one or two asterisks to match a wildcard segment or multiple segments recursively. The problem is, subscribe_to generates a signature based on the channel being subscribed to. Thus, the JS client cannot subscribe to a wildcard channel unless the exact same string is passed to the helper (and vice versa). For example, harkening back to the classic chat example, the following is not possible:

<%= subscribe_to "/updates/#{room.id}/*" %>

<script>
  var id = the_room_id();
  PrivatePub.subscribe("/updates/" + id + "/chats", function(data, channel) {
    $("#chat").append(data.chat_message);
  });

  PrivatePub.subscribe("/updates/" + id + "/events", function(data, channel) {
    process_event(data);
  });
</script>

Do you think PrivatePub should be modified to support this, or should the client be responsible for using the channel parameter in the callback to figure out what channel the event came from? If the former, how should it be handled?

BinaryMuse commented 13 years ago

One (possibly crappy? :) idea for the "change the gem" direction is to check any possible wildcard channels if the signature passed to PrivatePub doesn't match the one for the channel the client is subscribing to in authenticate_subscribe. For example, if the client is subscribing to /updates/6/chats, first check to see if the signature is correct for that specific channel (as is done now). If it doesn't, then check the following channels until a match is found (or we run out of channels):

[Update]

In retrospect, yeah, I think I was right, crappy idea. :) Hopefully the one below isn't quite so crappy.

BinaryMuse commented 13 years ago

Another potential solution (that Ryan alluded to in a pull request) would necessitate changes on PrivatePub's JavaScript side, but could be more flexible, namely, making the task of figuring out wildcard channel callbacks the responsibility the PrivatePub object.

<%= subscribe_to "/updates/#{room.id}/*" %>

<script>
  // The following call *actually* subscribes us to /updates/id/* with no server-side changes,
  // but PrivatePub calls the correct callback(s) when we receive a message based on the
  // channel passed to the "subscribe" method.
  PrivatePub.subscribe("/updates/" + id + "/chats", function(data, channel) {
    $("#chat").append(data.chat_message);
  });

  // The following call doesn't subscribe us again (because we're already subscribed
  // to /updates/id/*, but adds a new "mapping" in the PrivatePub callback set.
  PrivatePub.subscribe("/updates/" + id + "/events", function(data, channel) {
    process_event(data);
  });
</script>

As mentioned in the afore-linked pull request, this allows us the opportunity to do other neat things, like support wildcards in other parts of the channel (e.g. /updates/*/events).

[Update]

I assume the best way to handle something like this would be to change

if (callback = PrivatePub.subscription_callbacks[options.channel]) {
  callback(message.data, message.channel);
}

to something like

if (callbacks = PrivatePub.find_callbacks_for(options.channel) {
  for(var callback in callbacks) {
    callback(message.data, message.channel);
  }
}
ryanb commented 13 years ago

Yep, exactly. This can all be done through the JavaScript without much hassle. However you may want to hold off on any pull requests for now. I will be doing some refactoring of the javascript and adding some tests with Jasmine before adding any additional features.

BinaryMuse commented 13 years ago

Excellent. Thanks for your work thus far; this has proven to be very useful!

ryanb commented 13 years ago

I'm done refactoring the JavaScript and it now includes Jasmine tests. Feel free to send pull requests around the JavaScript now, but be sure to include tests.

Uko commented 11 years ago

:+1: