Skip to content

Instantly share code, notes, and snippets.

@MatheusRich
Last active May 28, 2025 01:32
Show Gist options
  • Save MatheusRich/71b34fb8b5d1fc236394873b2910fb33 to your computer and use it in GitHub Desktop.
Save MatheusRich/71b34fb8b5d1fc236394873b2910fb33 to your computer and use it in GitHub Desktop.
Favoritable concern
class CreateFavoriteRecords < ActiveRecord::Migration[7.2]
def change
create_table :favorite_records, id: :uuid do |t|
t.references :owner, foreign_key: { to_table: :users }, null: false, type: :uuid
t.references :favoritable, polymorphic: true, null: false, type: :uuid
t.timestamps
end
add_index :favorite_records, [:owner_id, :favoritable_type, :favoritable_id], unique: true, name: 'index_favorite_records_uniqueness'
end
end
module Favoritable
extend ActiveSupport::Concern
included do
has_many :favorite_records, class_name: 'FavoriteRecord', as: :favoritable, dependent: :destroy, inverse_of: :favoritable
scope :favorite, -> { favorited_by(Current.user) }
scope :favorited_by, ->(owner) { joins(:favorite_records).where(favorite_records: { owner: }) }
scope :not_favorite, -> { where.missing(:favorite_records) }
end
def favorite! = favorite_records.create!(owner: Current.user)
def unfavorite! = favorite_record_for(Current.user)&.destroy
def favorited_by?(user) = favorite_record_for(user).present?
def favorite? = favorited_by?(Current.user)
def favoritable_sgid = to_sgid(for: :favoritable, expires_in: nil)
private
def favorite_record_for(user) = favorite_records.find { _1.owner_id == user.id }
end
class FavoriteRecord < ApplicationRecord
belongs_to :owner, class_name: 'User'
belongs_to :favoritable, polymorphic: true
validates :owner, uniqueness: { scope: [:favoritable_type, :favoritable_id] }
end
class FavoriteRecordsController < ApplicationController
before_action :set_favoritable
def create
@favoritable.favorite!
redirect_back_or_to @favoritable
end
def destroy
@favoritable.unfavorite!
redirect_back_or_to @favoritable
end
private
def set_favoritable
@favoritable = GlobalID::Locator.locate_signed(params[:favoritable_sgid], for: :favoritable)
end
end
class Note < ApplicationRecord
include Favoritable
end
Rails.application.routes.draw do
resource :favorite_records, param: :favoritable_sgid, only: %i[create destroy]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment