User Presence with Rails using PrivatePub and Redis

Rails is a really nice framework for developing websites, however if you want real-time communication Rails will not be the best option. Socket.IO and Node.js seem to be a better alternative.

Despite that sometimes you have to add real time communications to your Rails app.In this article we use private pub which is a pub/sub gem along with redis database to persist user and preserve user’s online status in a rails application.

Add these gems to your Gemfile.

gem 'private_pub'
gem 'thin'
gem 'redis

Now use bundler to install new gems.

bundle install

Run the generator to create the private pub configuration files.

rails g private_pub:install

Add the JavaScript file to your application.js file manifest.

//= require private_pub

We will use one global channel ‘/presence’ and one private channel for each user ‘/push/:user_id’, the user should subscribe to them both on login.

‘/presence’ will be a channel for all online users to subscribe, while subscribing to a private channel will publish that this particular user is now logged_in/out to all online users after saving that user online status to redis database.

Now to bind the subscription and unsubscription events on a channel we need to change the file to look like this

# Run with: rackup -s thin -E production
require "bundler/setup"
require "yaml"
require "faye"
require "private_pub"
require "redis"

# you must pass any environment variables manually
redis =

# delete online records
online_users = redis.keys "user_*_online"
redis.del *online_users unless online_users.empty?
puts 'All users are now offline!'


PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV['environment'] || 'default')

faye_application = PrivatePub.faye_app(timeout: 30, ping: 20)
# subscribe - online
faye_application.bind(:subscribe) do |client_id, channel|
  puts "Client subscribe: #{client_id}:#{channel}"
  unless channel == "/presence"
    user_id = channel.split('/').last
    redis.set("user_#{user_id}_online", true) do
      PrivatePub.publish_to "/presence", {:user_id => user_id, :status => "online"}

# unsubscribe - offline
faye_application.bind(:unsubscribe) do |client_id, channel|
  puts "Client unsubscribe: #{client_id}:#{channel}"

  unless channel == "/presence"
    user_id = channel.split('/').last
    redis.set("user_#{user_id}_online", false) do
      PrivatePub.publish_to "/presence", {:user_id => user_id, :status => "offline"}

run faye_application

User records are deleted on Faye startup in case of Faye crash.

Now in the user’s web page, we can toggle the online/offline icon accordingly.

<% if (user_signed_in? && !( %>
    <%= subscribe_to "/push/#{}" %>
    <%= subscribe_to "/presence" %>
        PrivatePub.subscribe("/presence", function(data, channel) {
                if($("#users").length > 0){
                    if(data.status == "online"){
<% end %>

Latly, start up Faye server using the rackup file that was generated.

rackup -s thin -E production

Now you have an application that keeps the user’s online status in real time.

Authored by Amr Kamel