How to restrict a view by - ruby-on-rails

I have successfully set up a friendship self referencial association for users in my Ruby on Rails app after following Ryan Bates' railscast. I can successfully follow, unfollow, and view who is following. Now I want to pull in another element and I am not sure how to do it.
When visiting a user/show page of a user that the current_user is already friends with...what is the syntax for checking the Friendships table for an existing friendship.
Here is how the associations are set up.
Friendship.rb
belongs_to :user
belongs_to :friend, :class_name => "User"
User.rb
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
def friends_workouts
#friends_workouts ||= Workout.find_all_by_user_id(self.friends.map(&:id), :order => "created_at DESC", :limit => 3)
end

I'd go with something like this:
def friends_with?(other_user)
friends.include?(other_user) || inverse_friends.include?(other_user)
end

Related

Rails has_many through, issues with foreign key and source

A User has many subscribers and many publishers, both of which are users:
class User < ActiveRecord::Base
has_many :relationships, :foreign_key => "subscriber_id"
has_many :subscribers, :through => :relationships, :source => :subscriber
has_many :inverse_relationships, :class_name => "Relationship", :foreign_key => "publisher_id"
has_many :publishers, :through => :inverse_relationships, :source => :publisher
def subscribe_to(publisher)
self.relationships.create!(publisher_id: publisher.id, subscriber_id: id)
end
end
class Relationship < ActiveRecord::Base
belongs_to :subscriber, :class_name => "User"
belongs_to :publisher, :class_name => "User"
end
A user's publishers is who the user is subscribed to.
However, if I do john.subscribe_to(a_publisher), and then attempt to puts john.publishers, I get back an empty array.
I'm going back and forth randomly changing the foreign key and the source, hoping that it will eventually work, but something is off. What should the sources and foreign keys be here?
Update
Here's what I did to make it work:
class User < ActiveRecord::Base
has_many :relationships, :foreign_key => "publisher_id"
has_many :subscribers, :through => :relationships, :source => :subscriber
has_many :inverse_relationships, :foreign_key => "subscriber_id", :class_name => "Relationship"
has_many :publishers, :through => :inverse_relationships, :source => :publisher
def subscribe_to(publisher)
publisher.relationships.create!(subscriber_id: id)
end
end
First, I switched the foreign keys for both. Second, and this is the part I don't understand, I changed
self.relationships.create!(publisher_id: publisher.id)
to
publisher.relationships.create!(subscriber_id: id)
and it worked. For some reason, it doesn't work the other way around. Can anyone explain why?
Your association seems strange. You should try like this:
has_many :subscribers, :through => :relationships, :class_name => 'User'
And call :
john.subscribers.create(a_publisher)
Nowhere you're saying that subscriber is a User. I think your problem comes from here.
Maybe you can try with: :source => :user

Correct association for Twitter-style posts by followed users in rails

I'm trying to list all the posts made by a user's followed users, and not sure how to correctly set up the associations. This has been discussed in various forms but I can't find a definitive answer that works in this case. My current associations are as follows:
user.rb:
has_many :followers, :class_name => 'UserFollower', :foreign_key => 'user_id'
has_many :following, :class_name => 'UserFollower', :foreign_key => 'follower_id'
has_many :posts
user_follower.rb:
belongs_to :user
belongs_to :follower, :class_name => 'User'
post.rb
belongs_to :user
I want to be able to do something like current_user.following_user_posts. So I'm trying to define an association in my user.rb something like:
has_many :following_user_posts, :through => :following
Obviously this isn't quite right, but where am I going wrong?
Michael hartle answers this exactly in his tutorial using his Micropost.from_users_followed_by method http://ruby.railstutorial.org/chapters/following-users#sec-a_first_feed_implementation
Figured this out, seems like a simple problem but the terminology makes it quite confusing. I have the following in by user.rb:
has_many :user_followers
has_many :followers, :through => :user_followers, :source => :follower #the users that are following this user
has_many :user_followings, :class_name => 'UserFollower', :foreign_key => "follower_id"
has_many :following, :through => :user_followings, :source => :user #the users that this user is following
has_many :following_user_posts, :through => :user_followings, :source => :posts
And in my user_follower.rb:
belongs_to :user
belongs_to :follower, :class_name => 'User'
has_many :posts, :through => :user, :source => :posts
The resulting query uses 2 inner joins - I have no idea how well this will scale but its working fine for me on development. So now, I can simply use current_user.following_user_posts

Relationship Type in Rails

Hi I am very new rails , would need some help , there is nothing similar I could find , i watch all the rail casts on the similar lines.
So I have a article model and user model ( devise ) .
I would to user to add article either in Follow Mode or Just Read Later Mode.
so UserArticleAssociation has article_id , user_id and association type . I am not understanding how to implement this feature correctly. I could make some hack to do these , but I don't want to.
Any tutorial on similar will be great help.
Try this:
class User < ActiveRecord::Base
has_many :user_articles
has_many :read_user_articles, :class_name => "UserArticle",
:conditions => {:mode => "read"}
has_many :follow_user_articles, :class_name => "UserArticle",
:conditions => {:mode => "follow"}
has_many :articles, :through => :user_articles
has_many :read_articles, :through => :read_user_articles, :source => :article
has_many :follow_articles,:through => :follow_user_articles,:source => :article
end
# Add a column called mode of type string (follow, read)
class UserArticle < ActiveRecord::Base
belongs_to :user
belongs_to :article
end
class Article < ActiveRecord::Base
has_many :user_articles
has_many :read_user_articles, :class_name => "UserArticle",
:conditions => {:mode => "read"}
has_many :follow_user_articles, :class_name => "UserArticle",
:conditions => {:mode => "follow"}
has_many :readers, :through => :read_user_articles, :source => :user
has_many :followers,:through => :follow_user_articles,:source => :user
end
Now you can do the following:
To add an article to read/follow category:
user.read_articles << article
user.follow_articles << article
OR
article.reader << user
article.follower << user
To access the articles
user.read_articles
user.follow_articles
To access the users
article.readers
article.followers
You should use has_many :through association. There is a Railscast about it here:
http://railscasts.com/episodes/47-two-many-to-many

has_many through self referential association

I want to (as an example) create a has_many association to all posts by friends of a person, something like has_many :remote_posts to give me something like person > friends > person > posts.
..here is how I would go about it
script/generate model post title:string person_id:integer
script/generate model friendship person_id:integer friend_id:integer
script/generate model person name:string
class Person < ActiveRecord::Base
has_many :posts
has_many :friendships, :foreign_key => 'friend_id'
has_many :people, :through => :friendships
has_many :remote_posts, :class_name => 'Post', :through => :people, :source => :posts
end
class Friendship < ActiveRecord::Base
belongs_to :person
#also has a 'friend_id' to see who the friendship is aimed at
end
class Post < ActiveRecord::Base
belongs_to :person
end
# generate some people and friends
{'frank' => ['bob','phil'], 'bob' => ['phil']}.each {|k,v|
v.each {|f|
Friendship.create(
:person_id => Person.find_or_create_by_name(f).id,
:friend_id => Person.find_or_create_by_name(k).id
)
}
}
# generate some posts
Person.all.each {|p|
p.posts.create({:title => "Post by #{p.name}"})
}
Now,
Person.first.friendships # ..works
Person.first.people # (friends) ..works
Person.first.posts # ..works
Person.first.remote_posts #....
...and I get this error..
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: people.person_id: SELECT "posts".* FROM "posts" INNER JOIN "people" ON "posts".person_id = "people".id WHERE (("people".person_id = 1))
Aside from the foreign key error - seems like the friendships association isn't coming into play at all. I was thinking that this might be because of the :source => :posts, since the posts association would come into it twice.
I could write some finder sql (and that is what I have working at the moment), though I'd sooner do it this way.
Any ideas of how to get this to work?
How about this:
In the FriendShip class, add:
has_many :posts, :through => :person
and in the Person class, change the remote_posts to:
has_many :remote_posts, :class_name => 'Post',
:through => :friendships, :source => :person
How about a nested has_many :through relationship. This seems to work for me:
class Friendship < ActiveRecord::Base
belongs_to :person
belongs_to :friend, :class_name => 'Person'
has_many :posts, :through => :friend, :source => :posts
end
class Person < ActiveRecord::Base
has_many :posts
has_many :friendships, :foreign_key => 'friend_id'
has_many :people, :through => :friendships
has_many :remote_posts, :through => :friendships, :source => :posts
end
Note: this requires this nested_has_many_through plugin. (Note: direct linking to github repos seems to be broken... but that repo is there despite the error message.)

Rails - Two-way "friendship" model (cont'd)

This is a continuation of this question:
Original Question (SO)
The answer to this question involved the following set of models:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships #...
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User', :foreign_key => 'friend_id'
end
<% for friendship in #user.friendships %>
<%= friendship.status %>
<%= friendship.friend.firstname %>
<% end %>
This works fine if say, I have a user and I want to get all the "friendships" for which his or her id is the :user_id FK on the Friendship model. BUT, when I run something like
#user.friendships.friends
I would like it to return all User records for which that User is either the :user or the :friend in the friendship - so, in other words, return all friendships in which that user is involved.
Hopefully the above makes sense. I'm still quite new to rails and hope there is a way to do this elegantly without making just a standard link table or providing custom SQL.
Thank you!
Tom
railscasts episode on this topic
You cannot just use #user.friendships here because it will only give you those friendships where #friendship.user_id == #user.id.
The only thing I can think of right now is just to do
Friendship.find_by_user_id(#user.id).concat Friendship.find_by_friend_id(#user.id)
my solution is a a scope:
# model
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => "User"
belongs_to :recipient, :class_name => "User"
scope :of_user, lambda { |user_id| where("sender_id = ? or recipient_id = ?",
user_id, user_id) }
end
# in controller
#messages = Message.of_user(current_user)
From the railscast link:
# models/user.rb
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user

Resources