Preventing instances of this Post model from appearing twice (Rails)? - ruby-on-rails

I have a Post model:
class Post < ActiveRecord::Base
belongs_to :user
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
before_create :init_sort_column
def tag_names
#tag_names || tags.map(&:name).join(" ")
end
private
def assign_tags
self.tags = []
return if #tag_names.blank?
#tag_names.split(" ").each do |name|
tag = Tag.find_or_create_by_name(name)
self.tags << tag unless tags.include?(tag)
end
end
end
a Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
#has_many :subscribed_users, :source => :user, :through => :subscriptions
end
and a User model:
class User < ActiveRecord::Base
(Code related to Devise)
has_many :posts, :dependent => :destroy
has_many :subscriptions
has_many :subscribed_tags, :source => :tag, :through => :subscriptions
has_many :subscribed_posts, :source => :posts, :through => :subscribed_tags
attr_writer :subscribed_tag_names
after_save :assign_subscribed_tags
def subscribed_tag_names
#subscribed_tag_names || subscribed_tags.map(&:name).join(' ')
end
private
def assign_subscribed_tags
#self.subscribed_tags = []
return if #subscribed_tag_names.blank?
#subscribed_tag_names.split(" ").each do |name|
subscribed_tag = Tag.find_or_create_by_name(name)
self.subscribed_tags << subscribed_tag unless subscribed_tags.include?(subscribed_tag)
end
end
end
In the index page users only see posts with tags they have subscribed to:
posts_controller.rb:
#posts = current_user.subscribed_posts.paginate(:page => params[:page],
:per_page => 5,
:order => params[:order_by])
Now say there is a post with the tags food and drinks, and the user has subscribed to these two tags. He will see the post twice; it seems like it is appearing once as a post tagged as food and then as a post tagged as drinks.
Is there a way of preventing posts like this from appearing twice?

Add :uniq => true as a parameter to the has_many in the User model:
has_many :subscribed_posts, :source => :posts, :through => :subscribed_tags, :uniq => true
The docs at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many-label-Options says:
:uniq
If true, duplicates will be omitted from the collection. Useful
in conjunction with :through.

Related

How not get current_user

i'm breaking my head to get the user of this situation:
a conversation model:
has_many :conversation_participants, :dependent => :destroy
has_many :users,
:through => :conversation_participants
has_many :messages, :dependent => :destroy
has_one :display_message,
:class_name => 'Message',
:order => 'created_at DESC'
def participants(options={})
if options[:not].is_a? User
users - [options[:not]]
else
users
end
end
and conversation_participants:
belongs_to :user
belongs_to :conversation
attr_accessible :user_id
on a conversation helper:
def self_or_other
#conversation.conversation_participants.find_by_user_id(:not => current_user)
end
please, someone could clear me how to get the other user inside conversation_participants model?
I believe like follows:
#conversation.conversation_participants.where.not(user_id: current_user.id).first

How to print my posts and posts of my friends?

I have 2 models, user and friendship, here's how they looks like:
class User < ActiveRecord::Base
has_many :posts
has_many :friendships, :conditions => { :confirmed => true }
has_many :friend_requests, :class_name => 'Friendship', :conditions => { :confirmed => false }
has_many :friends, :through => :friendships
has_many :friends_friendships, :through => :friends, :source => :friendships
has_many :friends_of_friends, :through => :friends_friendships, :source => :friend
has_many :friends_listings, :through => :friends, :source => :listings
has_many :friends_posts, :through => :friends, :source => :posts
...
end
and
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
attr_accessible :friend_id
...
end
and here's how I fetch posts of currently logged in user and posts of his/her friends:
#my_posts = current_user.posts.includes(:user)
#friends_posts = current_user.friends_posts.includes(:user)
This is working well, but how can I load my posts + posts of my friends into one variable and display them as on Facebook? In other words, how can I merge those 2 queries into only one?
Thanks
something like this:
ids = current_user.friends.pluck(:id) << current_user.id
posts = Post.where(user_id: ids)
return
SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1, 2, 3, 6)
then in view:
posts.each do |post|
....
end

Rails has_many through with condition, build new

I've got users and organisations with a join model UsersOrganisation. Users may be admins of Organisations - if so the is_admin boolean is true.
If I set the is_admin boolean by hand in the database, Organisations.admins works as I'd expect.
In the console, I can do Organisation.first.users << User.first and it creates an organisations_users entry as I'd expect.
However if I do Organisation.first.admins << User.last it creates a normal user, not an admin, ie the is_admin boolean on the join table is not set correctly.
Is there a good way of doing this other than creating entries in the join table directly?
class User < ActiveRecord::Base
has_many :organisations_users
has_many :organisations, :through => :organisations_users
end
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_users, :class_name => "User",
:source => :user,
:conditions => {:organisations_users => {:is_admin => true}}
end
class OrganisationsUser < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
end
You can always override the << method of the association:
has_many :admins do
def <<(user)
user.is_admin = true
self << user
end
end
(Code has not been checked)
there are some twists with the has_many :through and the << operator. But you could overload it like in #Erez answer.
My approach to this is using scopes (I renamed OrganisationsUsers to Memberships):
class User < ActiveRecord::Base
has_many :memberships
has_many :organisations, :through => :memberships
end
class Organisation < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :class_name => 'User', :source => :user
# response to comment:
def admins
memberships.admin
end
end
class Memberships < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
scope :admin, where(:is_admin => true)
end
Now I create new admins like this:
Organisation.first.memberships.admin.create(:user => User.first)
What I like about the scopes is that you define the "kind of memberships" in the membership class, and the organisation itself doesn't have to care about the kinds of memberships at all.
Update:
Now you can do
Organisation.first.admins.create(:user => User.first)
You can try below code for organization model.
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :organisations_admins, :class_name => "OrganisationsUser", :conditions => { :is_admin => true }
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_admins, :source => :user
end

How to turn this attr_writer attribute into an actual database column?

I have this Post model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :tag_names
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
attr_writer :tag_names
after_save :assign_tags
before_create :init_sort_column
def tag_names
#tag_names || tags.map(&:name).join(" ")
end
private
def assign_tags
self.tags = []
return if #tag_names.blank?
#tag_names.split(" ").each do |name|
tag = Tag.find_or_create_by_name(name)
self.tags << tag unless tags.include?(tag)
end
end
def init_sort_column
self.content_changed_at = self.created_at || Time.now
end
end
and Tag model:
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :posts, :through => :taggings
has_many :subscriptions
has_many :subscribed_users, :source => :user, :through => :subscriptions
def tag_posts_count
"#{self.name} (#{self.posts.count})"
end
end
I would like to turn attr_writer :tag_names into an actual column in the database so I can do this: Post.find_by_tag_names("drinks").
How can I achieve this?
What do you think of turning around the associations?
Tag.find_by_name('drinks').posts
You should archive the same.

Passing two variables to separate table...associations problem

I have developed an application and I seem to be having some problems with my associations. I have the following:
class User < ActiveRecord::Base
acts_as_authentic
has_many :questions, :dependent => :destroy
has_many :sites , :dependent => :destroy
end
Questions
class Question < ActiveRecord::Base
has_many :sites, :dependent => :destroy
has_many :notes, :through => :sites
belongs_to :user
end
Sites (think of this as answers to questions)
class Site < ActiveRecord::Base
acts_as_voteable :vote_counter => true
belongs_to :question
belongs_to :user
has_many :notes, :dependent => :destroy
has_many :likes, :dependent => :destroy
has_attached_file :photo, :styles => { :small => "250x250>" }
validates_presence_of :name, :description
end
When a Site (answer) is created I am successfully passing the question_id to the Sites table but I can't figure out how to also pass the user_id. Here is my SitesController#create
def create
#question = Question.find(params[:question_id])
#site = #question.sites.create!(params[:site])
respond_to do |format|
format.html { redirect_to(#question) }
format.js
end
end
I'd think this would do the job
#question = current_user.questions.find params[:question_id]
if not, then just assign mannualy.
#site = #question.sites.build(params[:site])
#site.user = current_user
#site.save

Resources