feat(controllers): add ClaimsController and create
This commit is contained in:
1 parent
89fb0a8d43
commit
259484c7d3
1 file changed
+40
@@ -0,0 +1,40 @@
|
|||||||
|
class ClaimsController < ApplicationController
|
||||||
|
before_action :require_authentication
|
||||||
|
|
||||||
|
# Giveaway slots are a hard cap that has to hold under real concurrency,
|
||||||
|
# not just in whichever request happens to run first: two visitors
|
||||||
|
# racing the same "claim a slot" button must never both walk away
|
||||||
|
# believing they won one of the last N copies. Post#with_lock
|
||||||
|
# (Prism::Orm::Pessimistic, wired into every Model since session ~99)
|
||||||
|
# holds Store's real cross-process file lock for this entire
|
||||||
|
# reload-check-decide-write critical section, so a concurrent request -
|
||||||
|
# even from a completely separate `Prism::Server` OS process sharing this
|
||||||
|
# same database file, not just another thread of this one, see
|
||||||
|
# blog_giveaway_demo.rb/blog_giveaway_worker.rb - blocks on its own
|
||||||
|
# transaction until this one has already committed, instead of both
|
||||||
|
# reading the same not-yet-updated claims_count and both deciding a slot
|
||||||
|
# was still free.
|
||||||
|
def create
|
||||||
|
post = Post.find_by!(slug: params['post_id'])
|
||||||
|
raise Prism::Orm::RecordNotFound unless post.giveaway?
|
||||||
|
|
||||||
|
outcome = nil
|
||||||
|
post.with_lock do |locked_post|
|
||||||
|
if locked_post.giveaway_sold_out?
|
||||||
|
outcome = :sold_out
|
||||||
|
else
|
||||||
|
claim = Claim.new(post_id: locked_post.id, user_id: current_user.id)
|
||||||
|
# The only realistic validation failure left once
|
||||||
|
# giveaway_sold_out? has already passed is the uniqueness scope -
|
||||||
|
# the same visitor double-submitting the claim form (e.g. a
|
||||||
|
# doubled-click, or hitting back and resubmitting).
|
||||||
|
outcome = claim.save ? :claimed : :already_claimed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
flash[:notice] = t("claims.#{outcome}_notice")
|
||||||
|
redirect url_for(:post, slug: post.to_param)
|
||||||
|
rescue Prism::Orm::RecordNotFound
|
||||||
|
respond_not_found(t('posts.not_found'))
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user