0

I am implementing a chat in Rails 5 API Action Cable using Devise and gem 'devise_token_auth'.

I have a ChatRequestChannel with parameterized subscriptions:

stream_from "chat_request_#{chat_request_chanel_token}_channel"

I need to somehow reliably retrieve the chat_request_chanel_token value inside the unsubscribed hook - to send a message to other subscribers. Here's my code:

class ChatRequestChannel < ApplicationCable::Channel
  def subscribed 

    answerer = params["answerer"]

    chat_request_chanel_token = answerer

    answerer_user = User.find_by email: answerer

    if answerer_user

      stream_from "chat_request_#{chat_request_chanel_token}_channel"

    else
# http://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#class-ActionCable::Channel::Base-label-Rejecting+subscription+requests
      reject

    end

  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed

      chat_request_chanel_token = "What to put here????"

      message = "Client disconnected"  

      ActionCable.server.broadcast "chat_request_#{chat_request_chanel_token}_channel",
                                   disconnected: message

  end
end

All the clients subscribe to ChatRequestChannel and with the command like this (answerer param varies from client to client):

{"command":"subscribe","identifier":"{\"channel\":\"ChatRequestChannel\",\"answerer\":\"client1@example.com\"}"}

The unsubscribed hook actually gets called when a client disconnects from Action Cable. So I can't provide any params to unsubscribed.

I suggest that every client should have only one (per parameter) parameterized subscription to the ChatRequestChannel. Is it stored somewhere inside Action Cable and can be retrieved?

prograils
  • 2,248
  • 1
  • 28
  • 45

1 Answers1

0

I probably found. There is an Array streams inside

/var/lib/gems/2.3.0/gems/actioncable-5.0.1/lib/action_cable/channel/streams.rb

def stream_from(broadcasting, callback = nil, coder: nil, &block)
        broadcasting = String(broadcasting)

        # Don't send the confirmation until pubsub#subscribe is successful
        defer_subscription_confirmation!

        # Build a stream handler by wrapping the user-provided callback with
        # a decoder or defaulting to a JSON-decoding retransmitter.
        handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder)
        streams << [ broadcasting, handler ]

        connection.server.event_loop.post do
          pubsub.subscribe(broadcasting, handler, lambda do
            ensure_confirmation_sent
            logger.info "#{self.class.name} is streaming from #{broadcasting}"
          end)
        end
      end

See the line streams << [ broadcasting, handler ].

Here's my code based on it:

module ApplicationCable
  class Channel < ActionCable::Channel::Base
    include ApplicationHelper

      def stream_name
        streams.try(:[],0).try(:[],0)
      end

      def token_from_stream_name

        stream_name ? stream_name.gsub(channel_name,"").slice(1..-"_channel".size-1) : ""

      end
  end
end

The stream_name method is available now inside unsubscribe hook. When a client disconnects from the server socket, the unsubscribed hook is automatically called for every client's subscription (to that channel). Calling stream_name inside unsubscribe returns the name of the currently subscription being stopped allowing you to notify all the other subscribers to this channel:

class ChatChannel < ApplicationCable::Channel
 .... 
  def unsubscribed
    # Any cleanup needed when channel is unsubscribed

    message = { user_id: current_user.id, online: :off }

    ActionCable.server.broadcast stream_name, chat_cancel: message      
  end
....
end
prograils
  • 2,248
  • 1
  • 28
  • 45