I followed how to build a feed in Michael Hartl's tutorial. I have this method in my user model:
def feed
Video.from_users_followed_by(self)
end
and this in my video model:
scope :from_users_followed_by, lambda { |user| followed_by(user) }
def self.followed_by(user)
followed_ids = %(SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where("user_id IN (#{followed_ids})", { :user_id => user })
end
in order to retrieve all the videos created by the users followed by the current_user.
Then I have this method in my videos_controller:
def feed_display
#videos = current_user.feed.page(params[:page]).per(15)
end
and then I render the partial in my videos/feed_display.html.erb view:
<div id ='video_div'>
<%= render #videos, :collection => #videos %>
</div>
in order to display all the videos that are created by the users the user follows.
Now I want to add another resource to the feed so that it contains both videos and video_votes from the users that the user follows.
I'm thinking of replicating the model scope code that is my video model into my video_vote model and then adding this line:
VideoVote.from_users_followed_by(self)
to the feed method so that it looks like:
def feed
Video.from_users_followed_by(self)
VideoVote.from_users_followed_by(self)
end
My question is how should I modify my feed_display method and feed_display view so that it not only displays the videos but also the votes?
You could modify your SQL query to include the second model with the UNION statement.
Related
I have three models
User has_many :articles
Article has_many :daily_view_metrics & belongs_to :user
DailyViewMetric belongs_to :article
I want each user to have an overview of the metrics for their articles. At the moment I pass #user = User.find(params[:id]) in the show action of the users controller to open a specific users's show page. I also pass in #articles = Article.where(user_id: params[:id]).load in order to limit the available articles to only the user's articles.
Since the DailyViewMetric model has the same article multiple times (at different days) I need to aggregate them into a new array of arrays. I.e. I need
article_id date views
1 feb-01 20
1 feb-02 50
2 feb-01 30
to be returned as [[1,70],[2,30]] so I can sort it according to user wishes. How do I do this? Do I make a named scope in the DailyViewMetric model? or in the user_helper.rb?
It would be nice if I could have something that I can run newArray.each do |a| on in my view to make a table with stuff like <%= a.article_id %> and <%= sumviews %>, etc.. with something I can pass in the user_id so the aggregate is limited to his/her articles
You should be able to do it in the following way
Long form:
views_array = []
#articles.each do |a|
a.views do |v|
views_array << [a.id, v.date, v.count]
end
end
Short form:
#articles.collect {|a| a.daily_view_metrics.collect {|dv| [a.id, dv.date, dv.count] } }
I have a rails app where users post reviews of albums. The reviews that are posted are called "pins" and are uploaded with a release date, album title, artist name, album cover image, and rank for the year. (as in my #1 favorite album of the year) The pins belong users and the users have many pins. What I want to do is create a new page for 2013 that displays each user, then lists a descending ordered list of the album image, title, artist as their top ten list for the year. Something like:
<% #users.each do |user| %>
<%= link_to (image_tag user.image(:small)), user %> <%= user.name %>
<ol>
<li><%= #pin.album %> - <% #pin.artist%></li>
</ol>
<% end %>
I need to limit the pins to only :date => "2013" and I need to list them in descending order.
I'm having trouble figuring out the controller and view. Here is my page controller so far:
def tens2013
#users = User.all
#pin = Pin.where(:date => "2013")
end
How should I set my controller to be able to call <%= #pin.user.album %> ?
The way I see it, your models should be set up like this:
class User < ActiveRecord::Base
has_many :pins
end
class Pin < ActiveRecord::Base
belongs_to :user
belongs_to :album
end
class Album < ActiveRecord::Base
has_many :pins
end
Pin should have user_id and album_id to achieve this.
Then in the controller, you can eager load all users and their pins, with each pin's respective album, like this:
#users = User.includes(:pins => :album)
Or to limit to a certain year, do:
#users = User.includes(:pins => :album).where('pins.date>=?','2013-01-01').references(:pins)
Now you can iterate through the users in your view, and through each user's pins, and each pin's album.
You don't need to use #pin for each user's pin. Make the necessary changes in your view, and iterate through them using this style:
#users.each do |user|
# do something
user.pins.each do |pin|
# Now you have "pin" and you can use it:
# pin.album...
# pin.artist...
end
end
To call #pin.user.album, you need to define the dependency first at the model level. So in the Pin model, you should have belongs_to :users and in the User model, you should include has_many :pins. Now this will assume that in the Pin model, there is a field called user_id, which will be the foreign key. Also use :include in the queries when you are going to access dependent classes like this. It avoids the N+1 query problem. eg:
#pin = Pin.includes(:users).where("date >= ?","2013-01-01")
To limit responses to only the year 2013, you may want to search your query likewise:
#pin = Pin.where("date >= ?","2013-01-01")
I am using the amistad gem to handle friend relationships. Users have events associated with them. I would like to provide a feed of events for the a given user based on who they are friends with.
I have used the following code from http://ruby.railstutorial.org for follow relationships. However with amistad i don't have a user.friend_ids method only a user.friends method.
How can I get a similar feed type of result (that can be paged and all that) with the user.friends call that gives me a list of user objects and not just the ids?
class Micropost < ActiveRecord::Base
default_scope :order => 'microposts.created_at DESC'
# Return microposts from the users being followed by the given user.
scope :from_users_followed_by, lambda { |user| followed_by(user) }
private
# Return an SQL condition for users followed by the given user.
# We include the user's own id as well.
def self.followed_by(user)
following_ids = %(SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where("user_id IN (#{following_ids}) OR user_id = :user_id",
{ :user_id => user })
end
end
This is mostly pseudocode as it doesn't work, but here's what I think I'm trying to accomplish in code:
class Event< ActiveRecord::Base
default_scope :order => 'event.created_at DESC'
# Return events from friends of a user.
scope :from_friends, lambda { |user| friends_of(user) }
private
# Return an SQL condition for users followed by the given user.
# We include the user's own id as well.
def self.friends_of(user)
friend_ids = %(SELECT friendIDs FROM friendships)
where("user_id IN (#{friend_ids})")
end
end
You can manually add friend_ids method to the User model.
def friend_ids
self.friends.map(&:id)
//Here, I'm iterating over user.friends array and getting an array of ids
end
EDIT: As per your comment, I'm assuming you have the associations between user and events built up properly.
e.g A user has many events and an event belongs to a user/multiple users (depending on your requirements.)
Once you have the associations setup correctly, you can simply lookout for events with the user_ids which you got from above friend_ids method.
I'm trying to build a facebook style feed of items for a user. The feed will contain recent notes (on books) made by a user or people the user follows combined with other notifications such as "user x that you follow started reading a new book". You get the idea.
So far I have a scope in my Note class which returns the notes I want:
class Note < ActiveRecord::Base
scope :from_users_followed_by, lambda { |user| followed_by user }
def self.followed_by(user)
followed_ids = %(SELECT followed_id FROM relationships WHERE follower_id = :user_id)
where("user_id IN (#{followed_ids}) OR user_id = :user_id", { :user_id => user })
end
end
and a similar scope in my Readings class which returns records built when user starts reading a book:
class Reading < ActiveRecord::Base
scope :from_users_followed_by, lambda { |user| followed_by(user) }
def self.followed_by(user)
# is this not at risk of sql injection??
followed_ids = %(SELECT followed_id FROM relationships WHERE follower_id = :user_id)
# return readings where user_id IN (an array of user_ids that the user follows)
where("reader_id IN (#{followed_ids}) OR reader_id = :user_id", { :user_id => user })
end
end
Now this works fine and I can get arrays of objects from these no problem. I'm struggling to combine the two queries into a feed which is correctly ordered by creation time. The best I can do at the moment is my user class with a combined feed method:
class User < ActiveRecord::Base
def combined_feed
feed = Note.from_users_followed_by(self) | Reading.from_users_followed_by(self)
feed.sort! do |a, b|
a.created_at <=> b.created_at
end
feed.reverse
end
end
Which gets me a combined feed but strikes me as being horrendously inefficient. How can I do the equivalent at the database level in rails?
I think I would probably create an entirely separate model called FeedItem. Then, when certain events occur (such as the creation of a new note), you just create a new FeedItem record. Then you only have one table to query from and it will already be in the correct order.
I have a web application with users and their documents. Each user can have many documents:
user.rb:
has_many :documents
document.rb:
belongs_to :user
document_controller.rb:
def index
#documents = Document.find(:all)
end
I am using the restful_authentication plugin. Here is my question: How do I get the controller to only show documents that belongs to each user? Right now it shows all the documents for all the users.
I am using the latest version of Rails.
You set a relationship in your User class to your Document class. This will automatically add a method to your User objects that returns a list of all documents related to a particular user:
def index
#documents = #current_user.documents
end
See the documentation for other automatically added methods.
Try this:
def index
#documents = Document.where(:user_id => current_user.id)
end
def index
#documents = Document.find(:all, :conditions => {:user_id => session[:user_id]})
end
Take a look here in the rails API in the Association Join Models section.
However be aware Restful authentication won't control access in order to limit the users to only their own records particularly with restful routes. They can still view other users' records by entering values in the urls once they are logged in.
For that you might want to look into Restful ACL