How to find users that are on the same page - ruby-on-rails

I am new to rails, I have one model named "Topic", I want to get users those are currently viewing topic index page.
How do I get those users, can anybody help me?
Thanks in advance

Here is article which describe how to track active/online users use Redis. You can use this approach to track users who are currently viewing your page.

I would do this like so:
Similar to Jack Hardcastle's suggestion, you could have a column in the users table in your db which stores the path part of a user's url, and another column called "last_seen_at"
:string last_viewed_page
:datetime last_seen_at
Then, in your ApplicationController you could have a before_filter like this:
#in ApplicationController
before_action :set_last_seen_at_and_last_viewed_page
def set_last_seen_at_and_last_viewed_page
if current_user && request.method == :get
current_user.update_attributes(:last_seen_at => Time.now, :last_viewed_page => request.path)
end
end
The reason i test that request.method == :get is that this will only include pages that they actually load in the browser, and see the address in their address bar, and not form submission urls which are usually hidden from the user.
So, now you have the data, it's easy to query. We can never know who is on a page at this exact moment, since we don't know what they are actually doing - they might have gone to the toilet or closed their computer. We can only say that "this page is the last page these people looked at, and they did so within the last 15 minutes" or whatever.
So, if we wanted to use that criteria, of the last 15 minutes, we could say
#current_path = request.path
#users_on_current_path = User.where(["last_seen_at > ? and last_viewed_page = ?", 15.minutes.ago, #current_path])

Related

How can I protect my site from the multiple post requests?

Any user can 'like' photos on my site. But if he presses the button many times (10 for example), the 10 post requests will be sent to the server.
I tried to solve with the help of sessions.
I thought this code would take likes only each 5 seconds. But it doesnt! There are my action and before_filter:
def like
photo.create_like if session[:voted].nil?
session[:voted] = Time.now.to_a.first(3).reverse.join
redirect_to root_path
end
def check_session
a = Time.now.to_a.first(3).reverse.join
b = a.to_i - session[:voted].to_i
session[:voted] = nil if b >= 5
end
You can't do this with sessions (at least not with the default session store): by default rails stores the session in a cookie.
Cookies are sent by the browser as part of the request, and the response from your server can optionally update them. If you click on your like button several times in quick succession then you'll fire off several requests, each containing cookie data representing the current state of the session. Your response updates the session, but it's too late for the requests that have already been sent - their session data has been already been sent to the server and won't include any changes made by the responses.
As others have said, a bandaid is to use Javascript to restrict multiple submission but the only robust way to deal with this is at the database level ( with a unique index on the likes table).
You forgot reverse in your like method, so the comparison doesn't work. It should be:
session[:voted] = Time.now.to_a.first(3).reverse.join
Also in like you're using session[:voted] and in check_session you're using session[:time].
Although this won't work perfectly either. It would be better to use a Unix timestamp for this and let check_session return a boolean. Something like this:
def like
photo.create_like if check_session
session[:voted] = Time.now.to_i
redirect_to root_path
end
def check_session
session[:voted].blank? || (Time.now.to_i - session[:voted]) > 5
end
disable the button with
<%= submit_tag "Login", 'data-disable-with' => "Please wait.." %>
You could add a conditional in your views to show voted or want to vote? if the user voted already

Getting referrer and tracking conversions

I'm trying to create a mini analytics page like the one shown on the image below. It's a marketplace and each user has their analytics page with a graph. I've been able to get the views and referrer but my problem is how to go about doing the conversion part.
In my item show action i have something like this:
def show
#item = Item.find_by_rand_no params[:number]
if current_user && current_user.id != #item.owner_id
#item.views.create(item_id: #item.id, ip_address: request.remote_ip, owner_id: #item.owner_id, referrer: request.env["HTTP_REFERER"])
end
end
How do i track the ones that actually bought something so i can get a conversion rate?
Ps: A user may land on the page then click on another item and buy that instead. I also want to be able to account for that scenario.
Thanks
In your application controller create a before filter e.g. before_filter :save_referrer
def save_referrer
unless session['referrer']
session['referrer'] = request.env["HTTP_REFERER"] || 'none'
end
end
When a user buys an item you can save the referrer in a field either in the order table or create a separate table for referrals which i think is the better option, you can store views for each referrer by counter caching that column. You can also use first_or_create if you don't want to have to group by the referrer column and so on, it's up to you. Anyway you get conversion like this;
Conversion = (Sales/Number of unique views for a referrer) * 100

Disallowing users to view others posts

I have a page that shows posts that another user makes, and I was able to make it so that if you go to the index, it only shows posts who's user_id is = to the current users user_id. But if i go to the url and change the post number, I can see other users posts and even edit them. I have tried changing the controllers show #time_sheets = TimeSheet.where(:user_id => current_user.id)find(params[:id]) and have also tried using <% if #time_sheet.user_id == #currentuser.id %> in the view, but neither seem to work. What am I doing wrong?
You can use before_filter and redirect user to home page with error message if they are trying to access a page of a different user.
before_filter :validate_user
def validate_user
timesheet_user_id = Timesheet.find(params[:id]).user_id
redirect_to :root, :alert => "Unauthorized" unless timesheet_user_id == current_user.id
end
You can read more about filters here
In controller, you should have:
#time_sheet = TimeSheet.where(user_id: current_user.id).find(params[:id])
or, if you have association user has_many timesheets:
#time_sheet = current_user.time_sheets.find(params[:id])
It should work.
BTW you should of course check if anyone is logged in before calling this piece of code.

Showing if someone is reading a post

I want to show the list of people who are reading a post.
Before attempting to implement the function, I wanted to ask for some advice. I would start by checking users who invoke the show action in the PostsController; every time a person views a post, he will be recorded onto the list of people who are reading the post. However, I am not sure how to remove the person from the list when he navigates away from reading the post. Is there a way to use cookies or sessions to notice if the user navigates away from a certain page? A little bit of help would help me get started right away!
I appreciate any advice. Thank you!
To be more accurate
in routes.rb
post "/posts/:id/being_read" => "posts#being_read", :defaults => { :format => "json"}
in controller
PostsController < ApplicationController
def being_read
current_user && #post.being_read_by current_user
head :ok
end
end
on show page
function beingRead(){
$.post("/posts/<%=#post.id%>/being_read", function(data) {
setTimeout(beingRead,10000);
});
}
in post.rb
def being_read_by user=nil
# making use of rails low level cache
readers_ids = Rails.cache.fetch("Post #{id} being read", :expires_in => 5.minutes) || []
readers_ids = Rails.cache.fetch("Post #{id} being read", :expires_in => 5.minutes) (readers_ids << user.id) if user
readers_ids
end
Usually, a system of timeout is used.
Rather than store a boolean "is reading post", we can store the date on which the user has read the post.
No, you can easily set a timeout (for example 5 minutes) to know who are reading the post. This is an approximation, but this is almost realistic.
Advantages :
you can delete the finished dates when you want (with a cron, each day, or something else), with a single query rather than one a each view.
you resolve the problem of the user who close its browser (when no next page is open)

Case insensitive user names in Rails 3 routes

I am trying to allow users to access their accounts by their user name, regardless of how they capitalize it in the URL. So http://example.com/Username would go to the same page as http://example.com/username.
I am fine with the any other part of the URL being case sensitive (I know that's the standard), but I don't see a need to force a user to get the case of their user name correct in the URL.
Can this be done by adding certain settings in the routes.rb file alone?
I thought this would be straightforward but apparently it isn't.
I found a seemingly simple question here but there's only one answer that I think will help and it's too hacky (by the author's own admission)
I believe this isn't a routing issue and you can address it in your controller code:
User.where("lower(username) = lower(?)", params[:username]).first!
or
User.where("lower(username) = ?", params[:username].downcase).first!
or
User.find(:first, :conditions => [ "lower(username) = ?", params[:username].downcase ])
It should work fine to handle this behaviour in the controller, not in the routes.
# config/routes.rb
match ':username' => 'users#show'
# app/controllers/users_controller.rb
def show
username = params[:username]
user = User.find_by_username(username) || User.find_by_username(username.downcase)
# or something along these lines...
end
An even nicer solution might be to store some kind of slug identification for the user that is always downcased and ready to be used in URLs. You could have a look at the awesome friendly_id gem for that purpose.

Resources