How can I implement the same feature as Linkedin (count users who has viewed my profile page) for my rails app, so users will get notified for users who has viewed their profile?
You should add a new model, e.g.:
class PageView
belongs_to :page
belongs_to :user
end
Use it like this:
PageView.create(page: #page, user: current_user) # to register page view
PageView.where(page: #page).count # to get views count for particular page
PageView.where(page: #page, created_at: 1.week.ago..Time.now) # to get views count for particular page for a week
First of all you need to define your Visit model. It will have at least the following attributes:
id (visit id)
user_id (id of the visitor)
page_url (the page visited)
created_at (date and time of the visit)
Then you can decide to store it in a Relationa Database like MySQL or in a NO-SQL one like MongoDB. For this case any of them it could be a good option.
If you decide the SQL solution, just add a has_many :visits in your User model and :belongs_to :user in your Visit model.
And that's a good point to start from.
Related
Can't find same question. How can I specify select.where for model?
I need to select from different tables by one model and want to get something like this in controller:
params[:id] = 1248 // here is example of request params
id=params[:id] // this id goes to message SQL like table name with prefix:
Message(id).all => select * from messages_1248
How can I get something like this?
Thanks for answers!
UPD:
I have one table with users and many tables with messages (each table is for one pair of users). In users table there is 'messages' column with messages tables id's. And inside user_controller I need to run a query like in my question. Maybe anybody can share an example?
how about little bit change the design, with just 2 tables (user and message) just idea with details below
user table (id,name)
messages table(user_id,message_text)
you setup the relation user has_many messages (please see this link for more guide http://guides.rubyonrails.org/association_basics.html#the-has-many-association
user.rb
has_many :memberships
message.rb
belongs_to :user
for example you need to access user with specific id and the messages for this user
inside users_controller.rb
def show
#user = User.find(params[:id])
# this find user
#messages = #user.messages
# get all the messages for specific users
end
Disclaimer: I'm a complete Rails n00b.
I need to present my forum users with a list of posts, where some might be 'unread'. By unread I mean that the post has a newer :updated_at timestamp than the user's last view timestamp of that post. I can't figure out the best approach for this - unread posts would obviously be unique to every user.
I tried using the 'Unread' gem but the documentation is above my comprehension, I can't get it to work (and I'm not even sure it does what I want it to do).
What's the leanest most 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 edits.
EDIT 1:
I tried following the example below of adding a Looks model, and I think I'm much closer to a solution (although not all the way there). Here's what I did:
1) rails g model Look post:references user:references + rake db:migrate. This obviously generated the model needed:
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 into 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) I now tried to spit out the results:
seen = user1.posts
seen.map(&:title)
This works fine, it gives me the result of user1 having seen those two posts.
5) Then I tried just spitting out the IDs of the seen posts:
ids = Look.where(user: user1).pluck(:post_id)
This also works fine, I get a map of seen post ids => [2, 30]
6) I then managed to get around duplicate IDs by putting a .uniqat the end of User.first.posts.map(&:id).uniq
7) This is where I get stuck!:
Applying has_many :posts, through: :looks ruins the current relationship between a user and a post (the user_id is not included when creating a post). 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 creating a post.
Presuming you have a typical setup with many users and many posts, and you want to keep track whether any user has looked at any post, then you need to associate the users and posts by using a third model that joins a specific user to a specific post.
Start with the Rails guide: http://guides.rubyonrails.org/association_basics.html
Example 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 the posts that a user has seen:
seen = alice.posts
seen.map(&:name)
=> ["Post 1", "Post 2"]
To find the posts that a user has not seen:
ids = seen.map(&:id)
unseen = Post.where("id not in (?)", ids)
unseen.map(&:name)
=> ["Post 3", "Post 4"]
This is a simple way to get the records you want.
After you get this working, there are better ways to get the records by using query optimizations.
For example, you can retrieve much less data by getting just the id numbers. Here are a few 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 gem for guidance and example code.
You'll probably need some sort of historical log. It can either log individual instances, or merely log the latest visit to the forum.
Here's a hypothetical scenario:
Your forum has only one forum group.
You have a User model for each user.
You have a Topic/Thread model that contains the individual topic, with a has_many relationship to:
The Post model which contains a specific post authored by a specific user (not relevant to answer but part of scenario)
Your forum has a Topic controller with the standard RESTful resource routes assigned.
What you want to do is separate/highlight/designate the unread topics from the read topics, right? Breaking that down, you need to:
Figure out when the user last visited the forum's index page.
Get a list of topics.
When rendering each topic in the list, determine whether the topic is more "recent" than the user's last visit. Regardless of whether it was created after the last visit, or had a post after the last visit.
Render it differently (or whatever) as per your requirements.
Turning that into a very simple implementation, you would:
Add a "last index visit" attribute to the User.
Load that value when visiting the index.
Render the topics accordingly.
Update the last index visit attribute on the current user (best implemented as an after_action)
Now this implementation assumes that you only want to keep track of it in one place and are OK with the limitations associated with that choice (reading an individual topic won't mark it as read).
If you wanted to, you could eliminate that limitation by creating a table that belongs to both the user and the topic, and then updating that table whenever the user 'reads' the topic (depending on whether you want them to view the 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 you need to answer:
When do I consider a topic read? Is it when I view the individual posts since it is 'new'? Is it when I see the topic in a list after it is 'new'?
What do I consider a new/updated topic? Is it when it's first posted? what happens if someone posts a reply?
EDIT 2
Then I'd do the following:
Add a touch: true statement to the Post model's association to the Topic to ensure the updated_at attribute on the topic is modified whenever another post is made/edited.
Create a view helper method to determine whether the current user's last reading (if any) is later than the Topic's updated_at timestamp.
Create an after_action only on Posts#show that creates/updates the Reading for that user & topic.
so I've created a web app that has user profiles, where users can search for fellow users based on interests etc.. How can I provide users with the ability to see who viewed their profile, which isa bonus feature that they can unlock if they retweet about the website.
The which users viewed your profile can be done with something like this I guess:
class User < ActiveRecord::Base
has_many :user_visits
has_many :visitors, :through => :user_visits, :source => :visitor
end
class UserVisit < ActiveRecord::Base
# user_id :integer
# visitor_id :integer
# visited_at :datetime
belongs_to :user
belongs_to :visitor, :foreign_key => 'visitor_id', :class_name => 'User'
end
User.first.visitors = [user_1, user2]
Although, I'm not quite sure how they'd be added to the visitors array? I.e. If they visit the page what will cause them to be stored as a visitor?
And the second feature how can I create the ability to un-lock a feature, and how can I make it so the feature will be un-locked if the user tweets a certain message, in this case the website URL.
User Visits
Although, I'm not quite sure how they'd be added to the visitors
array? I.e. If they visit the page what will cause them to be stored
as a visitor?
To do this, you'd need 3 things:
A way to track the user
A way to track the visitor
A way to keep the record
Track User
I don't know how your application is set up, but if you're visiting someone's profile, you'd typically have this route:
`/users/13`
This will give you the params[:id] as the user_id
Track Visitor
If the visitor has to be signed in, you should have some sort of session variable defined for them. A typical case would be Devise's current_user object, which will allow you to use current_user.id as the visitor_id
I would then use a before_filter function on your users_controller to create a record of the visit, like this:
#app/controllers/users_controller.rb
before_filter :add_visit
private
def add_visit
UserVisit.create(:user_id => params[:id], :visitor_id => current_user.id) if current_user.id
end
Store The Record
To store this information, I'd make a model called UserVisitor, which will have these columns:
user_visitors
id | user_id | visitor_id | created_at | updated_at
This will allow you to store as many visitors as you wish without encumbering the application structure as a whole
Retweets
And the second feature how can I create the ability to un-lock a
feature, and how can I make it so the feature will be un-locked if the
user tweets a certain message, in this case the website URL
Further to Roberto's comments, you'd use a standard boolean column in your db. I'd recommend putting in your users table, and call it retweet, like this:
add_column :users, :retweet, :boolean
What you'd do is keep the UserVisit functionality in the backend (for everyone), but on the front-end, you could use the cancan gem (Railscast) to determine whether the users will have the chance to see who's viewed their profile
As Roberto mentioned, you could handle the retweet boolean inside the "retweet" function. Instead of using Twitter's HTTP-based functionality, you could use their API & create a method to fire after they use the retweet functionality
As soon as the retweet happens, you'll be able to set the boolean to true, unlocking the feature
I'm currently developing an application whereby a user clicks a button and they're offered up a new page of content, and was wondering how I would go about hiding or skipping past those that the user has already interacted with (a separate table stores the post_id and user_id for each view).
I currently use this code in the model for displaying a random page:
def self.random
if (c = count) != 0
find(:first, :offset =>rand(c))
end
end
The user authentication system is built off of Authlogic, and I have User, Post and View models.
So if a user has already seen a post "foo", how would I not display that in the future and instead serve up a random "bar".
Thanks
Steve,
I would set a boolean field for each post called "read" (default => false).
Upon firing the "show" action of your controller (and any other action you consider the person seeing the post), you can automatically set that to true and perform a save without validation. When you then show your list of records, you can add the condition .where("read = ?", false).
Of course, you can decide whether you want to give users the flexibility of setting individual posts to 'unseen' or 'unread' - if you want to do that it's the subject of another question :).
You could store an array of viewed post ids on the session in the show action of the posts_controller. EDIT -- find random post not already viewed. Not tested, but the idea is here:
def show_random_post
while (id == nil || (session[:viewed_posts] ||= []).include?(id)) # initialize array if it hasn't been initialized
id = rand(Post.count) + 1
end
session[:viewed_posts] << id
#post = Post.find(id)
# etc.
end
Do you want to keep a record of viewed posts between sessions?
EDIT: If you want to keep a user-level record of viewed posts between sessions, you'll probably want to do it at the db level. Since this means a many-to-many relationship between users and posts, you'll likely want to manage that with a relational table, and the best way to do that in Rails is with has_many :through. Something like (again, not tested):
class ViewedPostRecord < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
class User < ActiveRecord::Base
has_many :viewed_post_records
has_many :viewed_posts, :class => 'Post', :through => :viewed_post_records
end
class PostsController < ApplicationController
def show_random_post
while (id == nil || current_user.viewed_posts.map(&:id).include?(id))
id = rand(Post.count) + 1
end
#post = Post.find(id)
current_user.viewed_posts << #post
# etc.
end
end
I am new to Rails so go easy. I have created a blog and also created the ability for users to indicate they "like" a particular post. The way I am implementing this is by using the post table and a separate table 'vote'. When the user clicks the "like" button it sends the record to the 'vote' table with a value of '1' and the particular post id.
I would like to display the "most liked" posts in the sidebar. How do I go about calling such a thing. I would like to display the post_title and the number of 'votes', that is, I would like to somehow query the 'vote' table for the post_id's that have the most records and display them in descending order.
I hope this is an easy question.
There are several ways to accomplish this, but probably the most versatile and Rails-ish would be to create a module with a method to do the ranking, and then have any classes or associations that can be "liked" extend that module.
# lib/likable.rb
#
module Likable
def most_liked (limit = 10)
# This may be possible without a find_by_sql... see the API docs.
find_by_sql("SELECT Posts.*, SUM(votes.count) AS num_votes FROM Posts, Votes WHERE Posts.id = post_id GROUP BY post_id ORDER BY num_votes DESC LIMIT #{limit}")
end
end
# app/models/post.rb
#
require 'likable'
class Post < ActiveRecord::Base
extend Likable
# ...whatever else you've got in here
end
# app/models/user.rb (or any other model with things that can be "liked")
#
require 'likable'
class User < ActiveRecord::Base
has_many :posts, :extend => Likable
# ...the rest of the User class
end
This lets you do things like...
Post.most_liked # => an array of the 10 most liked posts
#some_user.posts.most_liked(5) # => that user's 5 most liked posts
If you needed to later, you could add methods to the module to see, eg, how many votes a particular Post has. You could also change the post_id to a target_id in Vote and make it a polymorphic association, and then you could use your Likable module to vote for anything, not just posts (you would need to generalize the call to find in most_liked if you did that).
This is actually best done by adding a counter cache to the post model, avoiding the database count on every load.
This railscast episode explains how to setup the counter cache.
Assuming you named your counter cache votes_count, you can do this to get the 10 most popular posts from your controller.
#popular_posts = Post.find(:all, :limit => 10, :order => "votes_count DESC")