Peter Gerdes
Peter Gerdes

Reputation: 2988

Rails: Broadcast message based on current_user (connection identifier)

Is there any way in action cable to modify the message being sent based on the user who established the connection (the conneciton is identified_by the current user).

Specifically, I have a subscription based on a particular model so I can write code like:

ProjectChannel.broadcast_to(project, { action: action, data: project.to_json })

However, I'd like to send different messages to subscribers based on whether or not they are owners of the project or merely are authorized to view the project. Is this possible? Or does the underlying pubsub model make this impossible?

Upvotes: 1

Views: 881

Answers (1)

Peter Gerdes
Peter Gerdes

Reputation: 2988

So one can setup multiple streams inside a single channel. Thus, if one had two types of users (say admin and non-admin) you could subscribe only the admin users to an admin stream like so:


def subscribed
   stream_from "all_user_stream"
   stream_from "admin_stream" if current_user.is_admin?
   stream_from "non_admin_stream" unless current_user.is_admin?
end

Now you can send a message to just the admin users via

ActionCable.server.broadcast "admin_stream", msg

And to all users via

ActionCable.server.broadcast "all_user_stream", msg

So if you want a different message to go to the admin users and the non-admin users you just create a method that sends one message to the admin_stream and a different message to the non_admin_stream. For instance, somewhere in your app you could write:

def send_user_msg(msg, secret)  #assume msg is a hash
    ActionCable.server.broadcast "non_admin_stream", msg
    msg['secret'] = secret
    ActionCable.server.broadcast "admin_stream", msg
end

Now only admin users will get the secret in their message and the rest of the users will get the same message without the secret.

Note that, if you are using stream_for with a model you'll probably want to retrieve the actual name of the stream (stream_for is just a wrapper to stream_from that encodes the active record object into a unique string) and then modify that.

For instance, if you are working in a channel named DocumentsChannel then stream_for doc essentially does:

stream_from DocumentsChannel.broadcasting_for(doc)

So if you want multiple streams you could stream_from "#{DocumentsChannel.broadcasting_for(doc)}:admin" and "#{DocumentsChannel.broadcasting_for(doc)}:non_admin"


One note of caution here. This doesn't update if the attribute changes so if you have a user whose admin status is removed they will keep receiving the admin messages until either they disconnect or you explicitly use stop_stream_from to end that streaming. Since that later code has to be called from the Channel instance opened for that user's connection this isn't necessarily a good option for properties that might be changed by outside factors (i.e. in response to anything other than a message on that channel from the connection you want to modify).

Upvotes: 1

Related Questions