Skip to content

Instantly share code, notes, and snippets.

@niquepa
Forked from jibiel/friendship.rb
Created March 9, 2021 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niquepa/f0413c8c41293d4069ffde7942d60edb to your computer and use it in GitHub Desktop.
Save niquepa/f0413c8c41293d4069ffde7942d60edb to your computer and use it in GitHub Desktop.
DRY Mutual Friendships / Friends in Ruby on Rails
# app/models/friendship.rb
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: 'User'
end
# app/queries/friendships_query.rb
module FriendshipsQuery
extend self
def both_ways(user_id:)
relation.unscope(where: :user_id)
.where(user_id: user_id)
.or(relation.where(friend_id: user_id))
end
private
def relation
@relation ||= Friendship.all
end
end
# app/models/user.rb
class User < ApplicationRecord
has_many :friendships,
->(user) { FriendshipsQuery.both_ways(user_id: user.id) },
inverse_of: :user,
dependent: :destroy
has_many :friends,
->(user) { UsersQuery.friends(user_id: user.id, scope: true) },
through: :friendships
end
# app/queries/users_query.rb
module UsersQuery
extend self
def friends(user_id:, scope: false)
query = relation.joins(sql(scope: scope)).where.not(id: user_id)
query.where(friendships: { user_id: user_id })
.or(query.where(friendships: { friend_id: user_id }))
end
private
def relation
@relation ||= User.all
end
def sql(scope: false)
if scope
<<~SQL
OR users.id = friendships.user_id
SQL
else
<<~SQL
INNER JOIN friendships
ON users.id = friendships.friend_id
OR users.id = friendships.user_id
SQL
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment