0

I'd like to modify the contents of a controller's index.html.erb page based on whether or not a user has previously viewed records associated with that controller.

In my particular case, I have an Item model, Items controller, and an Items view. The Item's view index.html.erb displays a link to each Item. I need to setup this view such that if an item has been viewed previously, then its link on index.html.erb would be in italics. If it had not been viewed, the link would be in boldface.

Visually, this approach is similar to an e-mail inbox, where items that have not been viewed have subject headings listed in bold face and viewed items have a regular font weight.

My question is similar to this previous stack overflow post; however, that post provides a general answer related to database structure. I'm curious to know if there is a ''Rails way'' to achieve this behavior that I am missing. If the linked post is appropriate, can anyone offer suggestions as how to achieve the posts' recommended solution using Rails?

I could rely on the browser and use the appropriate CSS to style visited and unvisited URLs, but for other reasons relevant to my project (but not this question, really) I'd rather have a solution that relies on Rails, hooks, and/or the database. I'm also up for other solutions (e.g., jQuery-based) if there's no straightforward Rails way of doing this.

Community
  • 1
  • 1
Eli Hooten
  • 994
  • 2
  • 9
  • 23

3 Answers3

2

Honestly, it seems like option 1 of the link you mentioned is the most "Rails way" of doing it. You make a model called a View, which has an item_id and a user_id. I'd say the most important thing is making a View resource.

Update: So let's say you want to mark an item as viewed if the user has accessed Items#show for that item. It's easy enough -- in Items#show, before you render that page's view, do something like this:

View.create(item_id: params[:id], user_id: current_user.id)

If you're worried about current_user not being logged in or something like that, you can either make a before_filter on Items#show to log the user in (if you require that he be logged before he views an item) OR just create the view conditionally:

if current_user
  View.create(item_id: params[:id], user_id: current_user.id)
end
Community
  • 1
  • 1
varatis
  • 14,494
  • 23
  • 71
  • 114
  • I think you're right, which is why I linked the post. I'm tripping over the implementation details, though. Where is the best place to write to the View resource? The show method in the Item controller, perhaps? If so, I should have easy access to the Item's record (i.e., `item_id`), but how do I ensure I have access to the User resource so that I can write the correct `user_id` to the View resource? – Eli Hooten Jul 02 '12 at 20:19
  • @EliHooten see updated answer. It assumes some sort of `current_user` helper method such as Devise's, but it's fairly straightforward. – varatis Jul 02 '12 at 20:27
  • I assumed I'd need a current_user helper. No big deal. Thanks for your answer. Based on my digging around it seems like the best approach. I'll implement this today and accept soon unless I hit some severe issue. – Eli Hooten Jul 02 '12 at 20:40
  • This solution is correct. However, @varatis 's approach will create new records every time a page is viewed. So, a ''view'' can be logged multiple times for a single page if a user views the page, leaves, then navigates back to it later. This may be desired behavior, but in my case it wasn't. So, my `Items#show` method contains a check against the `View` database to determine if the page has been previously visited. If it has, I increment a `visited_count` for the record. If not, I perform a `View.create` similar to the one proposed by varatis. Good solution. Thanks varatis. – Eli Hooten Jul 03 '12 at 04:22
0

Add a new column to the table used in index and call it viewed

After you render the page, set the viewed column for the rows to true or 1

Next time you load the page, you will check if it has been viewed with that column then show as italics, then render the page then set the new rows -- ie all rows to true

-- The trick is to update your rows after rendering the page --

When you insert new records you would insert them with column view as false -- not viewed

Sully
  • 14,672
  • 5
  • 54
  • 79
  • 1
    How would this work per user, though? Wouldn't two different users modify the same table? E.g., User A clicks on link 1, User B subsequently loads `index.html.erb` for the first time and link 1 is displayed in italics (since User A set its viewed attribute to true) rather than boldface. Perhaps there is something I'm missing. – Eli Hooten Jul 02 '12 at 20:10
0

If you only want the links to change on a per session basis, you can do this...

If you're really set on doing this with rails you could store a variable in the session to check whether a page has been visited or not. Something like

session["visited_hash"]["name_of_page_or_whatever"] = true

then on the index page

if session["visited_hash"]["name_of_page_or_whatever"] == true
  #render link with special formatting
else
  #render normally

To be honest I would probably implement this in CSS, but this is one way to do it in rails. No guarantee it's the most efficient though.

Kenny Bania
  • 637
  • 6
  • 18
  • I wouldn't say session variables are the "Rails way" of doing things, in any circumstance.. – varatis Jul 02 '12 at 20:06
  • I agree completely, just pointing out it is one way to do it if he wanted to implement this feature on a per-session basis, where storing a flag in the database doesn't work as well. The question didn't specify whether he wanted it per session or not. – Kenny Bania Jul 02 '12 at 20:21