ActionCable - how to display number of connected users?
Asked Answered
E

5

22

I'm trying to create a simple chat-like application (planning poker app) with Action Cable. I'm a little bit confused by the terminology, files hierarchy and how the callbacks work.

This is the action that creates user session:

class SessionsController < ApplicationController
  def create
    cookies.signed[:username] = params[:session][:username]
    redirect_to votes_path
  end
end

A user can then post a vote that should be broadcasted to everyone:

class VotesController < ApplicationController
  def create
    ActionCable.server.broadcast 'poker',
                                 vote: params[:vote][:body],
                                 username: cookies.signed[:username]
    head :ok
  end
end

Up to this point everything is clear for me and works fine. The problem is - how do I display the number of connected users? Is there a callback that fires in JS when a user (consumer?) connects? What I want is when I open 3 tabs in 3 different browsers in incognito mode I would like to display "3". When a new user connects, I would like the number to increment. If any user disconnects, the number should decrement.

My PokerChannel:

class PokerChannel < ApplicationCable::Channel
  def subscribed
    stream_from 'poker'
  end
end

app/assets/javascripts/poker.coffee:

App.poker = App.cable.subscriptions.create 'PokerChannel',

  received: (data) ->
    $('#votes').append @renderMessage(data)

  renderMessage: (data) ->
    "<p><b>[#{data.username}]:</b> #{data.vote}</p>"
Ellery answered 2/10, 2015 at 10:29 Comment(0)
E
19

Seems that one way is to use

ActionCable.server.connections.length

(See caveats in the comments)

Ellery answered 5/10, 2015 at 10:6 Comment(2)
Do note that this is the current number of connection for the specific thread this is called in, not the number of connections over various processes and threadsGraniah
It's also the number of connections to the server, not to a specific channel.Desperate
R
5

In a related question on who is connected, there was an answer for those who uses redis:

Redis.new.pubsub("channels", "action_cable/*")

and if you just want number of connections:

Redis.new.pubsub("NUMPAT", "action_cable/*")

This will summarize connections from all your servers.

All the magic covered inside RemoteConnections class and InternalChannel module.

TL;DR all connections subscibed on special channels with a prefix action_cable/* with only purpose of disconnecting sockets from main rails app.

Renn answered 1/12, 2017 at 14:23 Comment(0)
K
3

For a quick (and probably not ideal) solution you can write a module that tracks subscription counts (using Redis to store data):

#app/lib/subscriber_tracker.rb
module SubscriberTracker
  #add a subscriber to a Chat rooms channel 
  def self.add_sub(room)
    count = sub_count(room)
    $redis.set(room, count + 1)
  end

  def self.remove_sub(room)
    count = sub_count(room)
    if count == 1
      $redis.del(room)
    else
      $redis.set(room, count - 1)
    end
  end

  def self.sub_count(room)
    $redis.get(room).to_i
  end
end

And update your subscribed and unsubscribed methods in the channel class:

class ChatRoomsChannel < ApplicationCable::Channel  
  def subscribed
     SubscriberTracker.add_sub params['room_id']
  end

  def unsubscribed
     SubscriberTracker.remove_sub params['chat_room_id'] 
  end
end
Kitkitchen answered 17/2, 2017 at 8:45 Comment(0)
B
0

I think i found a answer for you. Try this:

ActionCable.server.connections.select { |con| con.current_room == room }.length?

I can use it everywhere in my code and check amount of connected users to selected stream :)

in my connection.rb I have something like this:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_room

    def connect
      self.current_room = find_room
    end

    private

    def find_room
      .....
    end
  end
end

Hope that helps anyone.

Batavia answered 7/5, 2021 at 14:18 Comment(0)
C
-1

With

ActionCable.server.pubsub.send(:listener).instance_variable_get("@subscribers")

you can get map with subscription identifier in the key and array of procedures which will be executed on the broadcast. All procedures accept message as argument and have memoized connection.

Chapter answered 16/10, 2018 at 14:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.