Recently, I needed to figure out if a user was active(online) on my site. Because I hadn't done this before, I Googled for inspiration π but found little success π. The majority of posts on Stack Overflow and blogs were variations of the same solution:
1) Add a last_online
timestamp column to your User model, then define a scope for user called online_now
.
Meaning, add new column to store on every timestamp the user is active/online. Then make a query(scope) from database which user that last active 2-10 minutes ago and display to the view.
This may take up some of your app's resources if you have many users who are logged in, because this will execute(update column & database query) before every controller action requested by someone who is logged in.
I think this process is slow.
2) devise_lastseenable Rubygems
Pretty old gems.
3) When someone has logged in
, you can push him into an array, if someone has logged out
, just remove him from the array.
I think last time I use this trick is on my Computer Science studies π at universities long time ago.
4) WebSocket & Redis
If you're bothered by making a trip to database on every. single. http. request. only to have some small window where you can sort of assume that a user is online; There have an alternate solution.
Using websockets and redis it's possible to reliably get a user's online status up to the millisecond without making a billion costly writes to disk.
Unfortunately, it requires a good bit more work and has two additional dependencies.
There is one article that explains well how to use this solution if you are interested.
But probably not my choice for a simple project.
So, What else ?
Well, this solution is related to Devise.
When we create a User Model based on Devise;
rails generate devise User
Devise will create updated_at
column with a datetime
data type.
This is good tho. We can utilize already column from Devise instead of creating a new column like previous solution.
Whenever the current_user
does any action, his updated_at
will be set as Time.now
.
#application_controller.rb
after_action :update_user_online, if: :user_signed_in?
private
def update_user_online
current_user.try :touch
end
And we will just say that the user is online if he was updated_at
during the last 2.minutes
or 1.minutes
#user.rb
def online?
updated_at > 2.minutes.ago
end
Now we can get true
or false
if we make a call like @user.online?
#users/show.html.erb
<%= @user.online? %>
This is one of the best and simple solution I get when doing research and a little bit of experimentation.
I am very happy π if you can share to me other tricks that is better than this.
The end
resources:
Top comments (2)
haha! I opened your article with hope to find a better approach than mine, only to discover that my approach #5 is the one you found best!
This approach is very simple, although is very similar to #1 and is not super-scalable if there are very many users/requests
π Haha. I found your article first which is simple and great, but I want to find if there is another solution. It ends with the same kind of solution.
I write this article hoping someone else gives another great approach. Your solution is still on the top :P. Thanks