2

In my Ruby on Rails project I have a Tickets MVC. A typical page for editing a ticket (e.g. ticket with id 5) would be /tickets/5/edit.

On another page I have a simple table that I want to show, in real time, how many users are viewing for each ticket(i.e. show how many users are on /tickets/1/edit, tickets/2/edit ....

Whenever a user is logged in and goes to view a ticket at /tickets/:id/edit, I have a coffeescript ticket_notifications.coffeethat looks like:

$ ->
  if $('body').attr('data-controller') == 'tickets' && $('body').attr('data-action') == 'edit'
    ticketId = document.getElementById('ticket_id').value
    App.ticket_notifications = App.cable.subscriptions.create {channel: "TicketNotificationsChannel", ticket_id: ticketId},
      connected: ->
# Called when the subscription is ready for use on the server
      disconnected: ->
# Called when the subscription has been terminated by the server
      received: (data) ->

My app/channels/ticket_notifications_channel.rb looks like:

class TicketNotificationsChannel < ApplicationCable::Channel
  def subscribed
    # stream_from "some_channel"
    if current_user&.account
      stream_from "ticket_notifications_channel_#{current_user.account_id}"
      if params[:ticket_id]
        if Ticket.find(params[:ticket_id]).account == current_user.account
          ActionCable.server.broadcast "ticket_notifications_channel_#{current_user.account_id}",
                                       {stuff: "Agent #{current_user.email} is viewing ticket #{params[:ticket_id]}"}
        end
      end
    end
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end
end

The front end table looks like (I am using Slim, but similar to erb):

table.table.table-hover
            thead
              tr
                th
                  | Ticket #
                th
                  | Short Code
                th
                  | Mobile Number
                th
                  | Number of Tickets
                th
                  | Updated At
                th
                  | Viewers
            tbody
              - unless @tickets.empty?
                - current_time = Time.now
                - @tickets.each do |tkt|
                  - tkt_updated_at = tkt.updated_at
                  tr.m-unread.m-tr-clickable data-href=edit_ticket_path(id: tkt.id)
                    td
                      b #{tkt.id}
                    td
                      b #{tkt.short_code}
                    td
                      b #{tkt.mobile_number}
                    td
                      - if last_message = tkt.messages&.last&.body
                        b #{last_message[0..60]}
                    td
                      - if (current_time-tkt_updated_at) <= time_period
                        b #{time_ago_in_words(tkt_updated_at)} ago
                      - else
                        b #{tkt_updated_at.in_time_zone.strftime('%b %e %H:%M:%S %Y')}
                    td
                      b Count how many subscriptions to tickets_notifications channel with params tkt.id here. 

Thanks.

Johnson Liu
  • 21
  • 1
  • 3
  • And your question is...? Are you trying to count subscriptions or users (not always the same thing and shouldn't be the same thing)? What code did you try so far? Where are you stuck? – Myst Oct 15 '18 at 14:21

1 Answers1

1

You can check the answer to the following quetsion:

How do I find out who is connected to ActionCable?

Basically you've to do.

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

This will give you all the active pubsub channels.

Also you can provide userId as well along with the ticketId when you're creating a new subscription like:

App.cable.subscriptions.create {channel: "TicketNotificationsChannel", ticket_id: ticketId, user_id: userId}

So now Redis.new.pubsub("channels", "action_cable/*") will give you all active subscriptions in following manner

["action_cable/Z2lkOi8vYXBpL1VzZXIvMTUwMjIz", "action_cable/Z2lkOi8vYXBpL1VzZXIvMTUwNTc0"]

Doing a Base64.decode of the strings mentioned above will give you output in following manner "gid://api/User/150223/Ticket/1". Then you can build some logic over this output that gives you count of all the users for a particular ticket id.

eurodo061
  • 116
  • 1
  • 5
  • 1
    **Please note**: by adding channels that include the user ID, you might be adding overhead to the Redis server - especially if Redis' pattern matching is used at the same time. However, without these channels you couldn't get a reliable user count, since a single process/machine should use (theoretically) a single Redis connection, making the whole process/machine a single subscriber. IMHO, I would consider a simple counter in the database (which would count a single user with two connections as two users) or a reference counted list of user names in the database (which is more complex). – Myst Oct 15 '18 at 14:45