# Terminal
rails g controller welcome index
rails g controller dashboard index
rails g model friendship user:references friend:references status:integer
rails g devise:install
rails g devise User
# Gemfile
gem 'devise'
# application.html.erb
...
<div class='container'>
<%= render 'layouts/navigation' %>
<%= yield %>
</div>
...
# layouts/_navigation.html.erb
<ul class='navigation'>
<% if user_signed_in? %>
<li><%= link_to current_user.email, edit_user_registration_path %></li>
<li><%= link_to 'Logout', destroy_user_session_path, method: :delete %></li>
<% else %>
<li><%= link_to 'Login', new_user_session_path %></li>
<% end %>
</ul>
# config/routes.rb
Rails.application.routes.draw do
devise_for :users
authenticated :user do
root 'dashboard#index', as: :authenticated_root
end
root 'welcome#index'
end
# 20181015003610_create_friendships.rb
class CreateFriendships < ActiveRecord::Migration[5.2]
def change
create_table :friendships do |t|
t.references :user
t.references :friend
t.integer :status, default: 0, limit: 1
t.timestamps
end
end
end
# models/friendship.rb
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: 'User'
enum status: { pending: 0, requested: 1, accepted: 2, blocked: 3 }
# id: 1
# friend: 2
# user_id: 1, friend_id: 2, status: :requested
# user_id: 2, friend_id: 1, status: :pending
end
# models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :friendships
has_many :friends, -> { where friendships: { status: :accepted }}, through: :friendships
has_many :requested_friends, -> { where friendships: { status: :requested }}, through: :friendships, source: :friend
has_many :pending_friends, -> { where friendships: { status: :pending }}, through: :friendships, source: :friend
has_many :blocked_friends, -> { where friendships: { status: :blocked }}, through: :friendships, source: :friend
# has_many :friendships_inverse, class_name: 'Friendship', foreign_key: :friend_id
# has_many :friends_inverse, through: :friendships_inverse, source: :user
# def all_friends
# friends + friends_inverse
# end
def friend_request(friend)
unless self == friend || Friendship.where(user: self, friend: friend).exists?
transaction do
Friendship.create(user: self, friend: friend, status: :pending)
Friendship.create(user: friend, friend: self, status: :requested)
end
end
end
# jane.accept_request(john)
# john.accept_request(jane) ! bad
def accept_request(friend)
transaction do
Friendship.find_by(user: self, friend: friend, status: [:requested])&.accepted!
Friendship.find_by(user: friend, friend: self, status: [:pending])&.accepted!
end
end
end
# Rails Console
user1 = User.first
user2 = User.second
user1.friend_request(user2)
user2.accept_request(user1)