mazing
mazing

Reputation: 743

Rails: How to generate a random order of "next post" in rails unique to each user?

I have a model where a user can enter a multiple choice question. I have a view where a user can click "previous" or "next", and it takes them up 1 or down 1 in the database.

However, I want to make it so that..

A. User clicks on "next", is taken to a new random question that hasn't been viewed before.

B. User clicks on "prev", is taken to the last question that was done, and the last question done before that.

C. User should have different order of "next" as compared to other users.

How can I go about doing this?

post.rb

def next
    Post.where("id > ?", id).order(id: :asc).limit(1).first
end

def prev
     Post.where("id < ?", id).order(id: :desc).limit(1).first
end

posts_controller.rb

def show 
@post = Post.friendly.find(params[:id])


# this is used in the show.html.erb view for posts
@randomize_posts = [
  @post.answer_choice,
  @post.answer_choice_2,
  @post.answer_choice_3,
  @post.answer_choice_4,
  @post.answer_choice_5
].shuffle

end

posts/show.html.erb

<%= link_to "← Previous Question", @post.prev, :class => 'button previous-question' %>

<%= link_to "Next Question →", @post.next, :class => 'button next-question' %>

Upvotes: 0

Views: 141

Answers (1)

Alex Santos
Alex Santos

Reputation: 2950

There are a few ways of doing this. I'd recommend doing a deterministic approach where different users get different results, but the same user will always have the same result. This allows for you to have randomness and do forward/backward navigation without storing extra data.

To randomize on a deterministic way, you can apply an md5 method on the post's id and the current user id:

posts = Post.order("md5('#{current_user.id}' || posts.id::text)")

Now the simplest way of finding the next and the previous is to see which one is right after and before the current one. You know the current one's position by calculating its md5 hash, so you just need to find which one comes right after or right before:

Post
  .where("md5('?' || '?') > md5('?' || posts.id::text)", current_user.id, id, current_user.id)
  .order("md5('#{current_user.id}' || posts.id::text)")
  .limit(1)

Applying to your model, it could look something like:

def next(user)
   ordered_posts(user)
     .where("md5('?' || '?') > md5('?' || posts.id::text)", user.id, id, user.id)
end

def prev(user)
  ordered_posts(user)
     .where("md5('?' || '?') < md5('?' || posts.id::text)", user.id, id, user.id)
end

private

def ordered_posts(user)
  Post.order("md5('#{current_user.id}' || posts.id::text)")
end

And the view would look like:

<%= link_to "← Previous Question", @post.prev(current_user), :class => 'button previous-question' %>

<%= link_to "Next Question →", @post.next(current_user), :class => 'button next-question' %>

Upvotes: 2

Related Questions