feat: add votable concern

This commit is contained in:
Mohammad Kanaan 2026-05-30 23:23:13 +03:00
parent 82f48c9ad3
commit da586ae3d3
2 changed files with 46 additions and 0 deletions

View file

@ -0,0 +1,4 @@
ts=2026-05-30T17:25:04.225Z type=context scope=developer-profile content="Developer Profile - Abdo Achkar: TS dev 2yr, mostly React frontend. Backend: Next.js server actions, basic Laravel ecommerce (admin middleware, no policies). Light Java/C++, solid OOP but functional instincts. Ruby beginner-intermediate (built mini-spec DSL with blocks/Procs/modules). Rails early (understands AR, polymorphic, nested routes, no auth yet). Project: price-point-api Rails 8 API-only crowdsourced price reporting. Learning goal: deep Rails understanding through building features. Growth areas: 1) Ruby idioms 2) Rails conventions 3) AR depth 4) testing 5) auth." tags=profile,developer,ruby,rails
ts=2026-05-30T17:25:27.520Z type=learning scope=developer-profile content="mini-spec project at /Users/abdoachkar/programming/personal/ruby/mini-spec reveals coding style: functional instincts (Proc as matcher instead of matcher object), hashes over objects for known shapes, explicit self on methods unnecessarily, parentheses on if conditions. Good: blocks, yield, module DSL design, rescue handling, method chaining. These are the Ruby idioms to correct as we go." tags=ruby,style,gaps
ts=2026-05-30T17:25:32.838Z type=context scope=price-point-api content="price-point-api: Rails 8 API-only, Ruby 3.4.4. Models: User, Chain (has_many stores), Store (belongs_to chain, has_many price_reports), Product (has_many price_reports), PriceReport (belongs_to product/store/user, polymorphic moderation_logs/votes), Vote (polymorphic votable), ModerationLog (polymorphic moderatable). Active Storage on Chain/Store/Product. No auth wired up (password_digest exists, no has_secure_password). No service objects, concerns, serializers, or background jobs. Scaffold-style controllers and tests." tags=architecture,rails,models
ts=2026-05-30T17:25:34.868Z type=preference scope=developer-profile content="Teaching approach: guide through building features on price-point-api, not isolated drills. Socratic method — hints and questions before solutions. Correct Ruby idioms as they arise naturally. Priority: auth first (unblocks everything, hits multiple growth areas)." tags=teaching,approach

View file

@ -0,0 +1,42 @@
# frozen_string_literal: true
module Votable
extend ActiveSupport::Concern
included do
has_many :votes, as: :votable, dependent: :destroy
end
def upvote!(user)
vote = votes.find_by(user:)
if vote
if vote.upvote?
vote.destroy!
else
vote.update!(direction: :upvote)
end
else
Vote.create!(user: user, votable: self, direction: :upvote)
end
end
def downvote!(user)
vote = votes.find_by(user:)
if vote
if vote.downvote?
vote.destroy!
else
vote.update!(direction: :downvote)
end
else
Vote.create!(user: user, votable: self, direction: :downvote)
end
end
def upvote_count
votes.where(direction: :upvote).count
end
def downvote_count
votes.where(direction: :downvote).count
end
end