WhoIsOnline 💚
Track "who is online right now?" in Rails using Redis TTL. Zero database writes, production-ready, and auto-hooks into controllers.
Installation • Quick Start • Configuration • API • Examples
✨ Features
- 🚀 Zero Setup - Rails Engine auto-includes controller concern
- 🔌 Works with Any Auth - Devise, custom, or any
current_usermethod - ⚡ Redis TTL-Based - No database tables required
- 👁️ Smart Tracking - Only tracks when tab is visible/active
- 🔄 Automatic Offline Detection - Marks users offline when browser closes
- ⏱️ Throttled Writes - Configurable to reduce Redis load
- 🔍 SCAN-Based - Safe counting without blocking Redis (
KEYSnot used) - 🎛️ Highly Configurable - Redis client, TTL, throttle, heartbeat interval, and more
📦 Installation
From RubyGems (Recommended)
Add to your Gemfile:
gem "whoisonline"Then run:
bundle installFrom GitHub (Development)
gem "whoisonline", github: "KapilDevPal/WhoIsOnline"🚀 Quick Start
1. Create Initializer
Create config/initializers/whoisonline.rb:
WhoIsOnline.configure do |config|
config.redis = -> { Redis.new(url: ENV.fetch("REDIS_URL")) }
config.ttl = 5.minutes
config.throttle = 60.seconds
config.heartbeat_interval = 60.seconds # Heartbeat when tab is visible
end2. Add to Layout (Optional but Recommended)
Add to your main layout (app/views/layouts/application.html.erb):
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
<%= whoisonline_offline_script %>
</body>
</html>That's it! The engine automatically tracks users online after each controller action.
📖 Public API
# Mark user online (auto-called by controller concern)
WhoIsOnline.track(user)
# Mark user offline immediately
WhoIsOnline.offline(user)
# Check if user is online
WhoIsOnline.online?(user) # => true/false
# Get count of online users
WhoIsOnline.count # => 42
# Get array of online user IDs
WhoIsOnline.user_ids # => ["1", "2", "3"]
# Get ActiveRecord relation of online users
WhoIsOnline.users(User) # => User.where(id: [...])⚙️ Configuration
WhoIsOnline.configure do |config|
# Redis connection (required)
config.redis = -> { Redis.new(url: ENV.fetch("REDIS_URL", "redis://127.0.0.1:6379/0")) }
# TTL settings
config.ttl = 5.minutes # How long user stays online without activity
config.throttle = 60.seconds # Minimum time between Redis writes per user
# User identification
config.user_id_method = :id # Method to get user ID
config.current_user_method = :current_user # Method on controllers to get user
# Heartbeat (for visible tab tracking)
config.heartbeat_interval = 60.seconds # Heartbeat frequency when tab is visible
# Advanced
config.namespace = "whoisonline:user" # Redis key namespace
config.auto_hook = true # Auto-include controller concern
config.logger = Rails.logger # Logger instance
endConfiguration Options
| Option | Default | Description |
|---|---|---|
ttl |
5.minutes |
How long a user stays online without activity |
throttle |
60.seconds |
Minimum time between Redis writes per user |
heartbeat_interval |
60.seconds |
Heartbeat frequency when tab is visible |
user_id_method |
:id |
Method to extract ID from user object |
current_user_method |
:current_user |
Method name on controllers |
namespace |
"whoisonline:user" |
Redis key namespace prefix |
auto_hook |
true |
Auto-include controller concern |
logger |
Rails.logger |
Logger for errors/warnings |
💡 Examples
Basic Usage
# In a controller (automatic via engine)
# Users are tracked after each action
# Check if user is online
if WhoIsOnline.online?(current_user)
# User is currently online
end
# Get online users count
@online_count = WhoIsOnline.count
# Get online users
@online_users = WhoIsOnline.users(User)Manual Tracking
# Manually mark user online
WhoIsOnline.track(current_user)
# Manually mark user offline (e.g., on logout)
WhoIsOnline.offline(current_user)In Background Jobs
class NotificationJob < ApplicationJob
def perform(user_id)
user = User.find(user_id)
if WhoIsOnline.online?(user)
# Only notify if user is online
NotificationService.deliver(user)
end
end
endDashboard View
# In your controller
class DashboardController < ApplicationController
def index
@online_count = WhoIsOnline.count
@online_users = WhoIsOnline.users(User)
.order(last_sign_in_at: :desc)
.limit(50)
end
end<!-- In your view -->
<div class="online-users">
<h2>Online Now (<%= @online_count %>)</h2>
<ul>
<% @online_users.each do |user| %>
<li><%= user.name %> <span class="badge">●</span></li>
<% end %>
</ul>
</div>🎯 How It Works
-
Controller Action - After each action, the engine automatically calls
WhoIsOnline.track(current_user) -
Redis Write - User presence is stored in Redis with TTL:
SET whoisonline:user:123 <timestamp> EX 300 - Throttling - Writes are throttled per user to prevent Redis spam
- Heartbeat - When tab is visible, JavaScript sends periodic heartbeats to keep user online
- Offline Detection - When browser closes, JavaScript sends offline request
- TTL Expiry - If no activity, Redis key expires automatically
⚡ Performance
-
O(1) Writes - Uses
SET key value EX ttlfor constant-time operations - Throttled - Prevents hot users from spamming Redis
-
SCAN-Based - Counting uses
SCANinstead ofKEYS(non-blocking) - Namespace Isolation - Keys are namespaced for easy management
- No Database - Zero database writes, all in Redis
Recommended Settings
For high-traffic applications:
config.ttl = 2.minutes # Shorter TTL = more accurate
config.throttle = 30.seconds # More frequent updates
config.heartbeat_interval = 30.seconds # More frequent heartbeatsFor low-traffic applications:
config.ttl = 10.minutes # Longer TTL = less Redis activity
config.throttle = 120.seconds # Less frequent updates
config.heartbeat_interval = 120.seconds # Less frequent heartbeats🔧 Troubleshooting
Users Not Showing as Online
- Check Redis connection:
redis-cli ping - Verify
current_usermethod exists in your controllers - Check logs for errors:
tail -f log/development.log - Verify TTL hasn't expired:
redis-cli TTL whoisonline:user:1
Helper Not Available
If whoisonline_offline_script is not available:
- Restart Rails server
- Check that gem is loaded:
bundle list | grep whoisonline - Verify engine is loaded: Check
config/application.rbfor engine loading
Heartbeat Not Working
- Check browser console for JavaScript errors
- Verify CSRF token is present:
<%= csrf_meta_tags %> - Check network tab for heartbeat requests
- Verify
heartbeat_intervalis configured
🛠️ Extensibility
The gem is designed to be extensible:
- ActionCable Integration - Broadcast presence changes
- Custom Events - Hook into track/offline events
- Multiple Redis Instances - Use different Redis for presence
- Custom User Models - Works with any user model structure
Example: ActionCable Integration
# In an initializer
WhoIsOnline.tracker.define_singleton_method(:track) do |user|
super(user)
ActionCable.server.broadcast("presence", { user_id: user.id, status: "online" })
end📝 License
MIT License - see LICENSE file for details.
👤 Author
Kapil Dev Pal
- 📧 Email: dev.kapildevpal@gmail.com
- 🐦 Twitter: @rails_to_rescue
- 🚀 Project: rails_to_rescue
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
⭐ Show Your Support
If you find this gem useful, please give it a ⭐ on GitHub!
Made with ❤️ for the Rails community