feat: add votable concern
This commit is contained in:
parent
82f48c9ad3
commit
da586ae3d3
2 changed files with 46 additions and 0 deletions
4
.opencode/memory/2026-05-30.logfmt
Normal file
4
.opencode/memory/2026-05-30.logfmt
Normal 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
|
||||||
42
app/models/concerns/votable.rb
Normal file
42
app/models/concerns/votable.rb
Normal 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
|
||||||
Loading…
Reference in a new issue