Show posts for:

Disclaimer: I am a complete Rails n00b.

I need to present forum users with a list of posts where some may be "unread". By unread I mean that the post has a newer: update_at timestamp than the last timestamp of the user of that post. I cannot find the best approach for this - unread messages will obviously be unique to each user.

I tried to use the "Unread" gem, but the documentation is beyond my comprehension, I cannot get it to work (and I'm not even sure if it does what I want it to do).

What's the best Rails-y way to do this?

I'm on Rails 4.1.6

I currently have a user model and a post model:

class User
    has_many :posts
end

class Post
    belongs_to :user
end

      

See below for editing.


EDIT 1:

I tried to follow the example below for adding a Model Looks and I think I am much closer to a solution (although not really). Here's what I did:

1) rails g model Look post:references user:references

+ rake db:migrate

. This obviously spawned the required model:

class Look  
    belongs_to :post  
    belongs_to :user  
end

      

2) Edited my user and post models:

class User  
  has_many :posts, through: :looks  
  has_many :looks  
end

class Post  
  belongs_to :user  
  has_many :looks  
  has_many :users, through: :looks  
end

      

3) Went to rails console

:

user1 = User.first

post1 = Post.first
post2 = Post.last

look = Look.create(user: user1, post: post1)
look = Look.create(user: user1, post: post2)

      

4) Now I tried to spit out the results:

seen = user1.posts
seen.map(&:title)

      

This works great, it gives me the result user1

after seeing these two messages.

5) Then I tried to just spit out the noticed post IDs:

ids = Look.where(user: user1).pluck(:post_id)

      

This also works great, I get a map of visible messages ids => [2, 30]

6) Then I managed to get around the duplicate IDs by putting .uniq

at the endUser.first.posts.map(&:id).uniq

7) This is where I got stuck! :

Applying has_many :posts, through: :looks

destroys the current relationship between the user and the message (user_id is not included when the message is created). Here is my PostsController:

def create
  @post = current_user.posts.build(post_params)

  if @post.save
    redirect_to @post
  else
    render 'new'
  end
end

private

def post_params
  params.require(:post).permit(:title, :content, :sticky, :ama_post, :post_url)
end

      

This is the last hurdle. I just need to make sure the user_id is included when posting.

+3


source to share


2 answers


Assuming you have a typical setup with many users and many posts, and you want to track if any user has viewed any post, then you need to link users and posts using a third model that attaches to a specific user for a specific position. ...

Start with the Rails tutorial: http://guides.rubyonrails.org/association_basics.html

Examples of models:

class User < ActiveRecord::Base
  has_many :looks
  has_many :posts, through: looks
end

class Post < ActiveRecord::Base
  has_many :looks
  has_many :users, through: looks
end

class Look < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

      

Let's set up some sample data using the rails console:

alice = User.create(name: "Alice")

post1 = Post.create(name: "Post 1")
post2 = Post.create(name: "Post 2")
post3 = Post.create(name: "Post 3")
post4 = Post.create(name: "Post 4")

look = Look.create(user: alice, post: post1)
look = Look.create(user: alice, post: post2)

      

To find posts the user has seen:

seen = alice.posts
seen.map(&:name)
=> ["Post 1", "Post 2"]

      



To find messages that the user has not seen:

ids = seen.map(&:id)
unseen = Post.where("id not in (?)", ids)
unseen.map(&:name)
=> ["Post 3", "Post 4"]

      

This is an easy way to get the records you want.

Once you get this working, there are better ways to get records through query optimization.

For example, you can get much less data by getting only the ID numbers. Here are some ways.

# Basic: traverses two joins, then loads all columns
ids = Post.joins(:looks => :user).where("users.id = ?", alice.id).map(&:id)

# Faster: traverses two joins, then loads one column
ids = Post.joins(:looks => :user).where("users.id = ?", alice.id).pluck(:id)

# Fastest: no joins, and only loads one column
ids = Look.where(user: alice).pluck(:post_id)

      

Take a look at the unread pearl for tutorial and example code.

+4


source


You will probably need some kind of history journal. He can either register individual copies, or simply register the last visit to the forum.

Here's a hypothetical scenario:

  • There is only one forum group in your forum.
  • You have a user model for each user.
  • You have a Topic / Topic containing a separate topic, with a has_many relationship to:
  • Post model that contains a specific post created by a specific user (not relevant to the answer, but part of the script)
  • Your forum has a theme controller with standard RESTful resource routes.

What you want to do is split / highlight / mark unread topics from read topics, right? Breaking this down, you need:

  • Find out when the user last visited the forum index page.
  • Get a list of topics.
  • When rendering each theme in the list, determine if the theme is more "recent" than the user's last visit. Whether it was created after the last visit or after the post was posted.
  • Give it away in a different way (or whatever) as per your requirement.

Turning this into a very simple implementation, you should:

  • Add the attribute "last hit index" to the user.
  • Load this value when visiting the index.
  • Provide relevant topics.
  • Update the last visit attribute of the index for the current user (best implemented as after_action)

This implementation now assumes that you only want to track it in one place and in order with the constraints associated with this selection (reading a single topic will not mark it as read).



If you so desire, you can overcome this limitation by creating a table that belongs to both the user and the topic, and then update that table whenever the user "reads" the topic (depending on whether you want them to view topic or just see it in the index).

EDIT

To ultimately answer this question, you need to break it down into more details. "Identify unread topics" means that you need to answer:

  • When do I believe the topic is being read? This is when I look at individual posts, so how is it "new"? Is it when I see a topic on the list after it's "new"?
  • What do I consider to be a new / updated theme? This is when it was sent first? what happens if someone posts a response?

EDIT 2

Then I would do the following:

  • Add a statement touch: true

    to the Post model association to a topic so that the updated_at attribute on that topic will be changed whenever another post is made / edited.
  • Create a helper view method to determine if the last current read (if any) is the last one than the Topic updated_at timestamp.
  • Create after_action

    only on Posts#show

    that creates / updates the Read for this user and topic.
+1


source







All Articles