Skip to content

Commit

Permalink
Expire Carts after 300 seconds #104
Browse files Browse the repository at this point in the history
After Cart expiry reserved products are released,
expired Carts are read-only
  • Loading branch information
thegcat committed Jun 16, 2017
1 parent e479abf commit 600c092
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 3 deletions.
2 changes: 1 addition & 1 deletion app/controllers/cart_payments_controller.rb
Expand Up @@ -12,7 +12,7 @@ class CartPaymentsController < ApplicationController
param :path, :cart_id, :integer, :required, 'Cart ID'
param :body, :cart, :versionCart, :required, 'Cart'
response :ok, 'Success', :readTransaction
response :not_found, 'No cart with that ID'
response :not_found, 'No cart with that ID or it is expired'
response :conflict, 'The user\'s balance is too low'
response :precondition_required, 'The cart is stale'
response :gone, 'The cart payment is already being processed'
Expand Down
4 changes: 4 additions & 0 deletions app/controllers/carts_controller.rb
Expand Up @@ -48,6 +48,9 @@ class CartsController < ApplicationController
property :id, :integer, :optional, 'Cart ID'
property :user_id, :integer, :optional, 'User ID'
property :total_price, :integer, :optional, 'Total Cart price'
property :expires_at, :date_time, :optional,
'Date and time at which the cart expires, making it read-only, '\
'not payable for, and releasing product reservations'
property :lock_version, :integer, :optional, 'Cart version'
property :cart_items, :array, :optional, 'Cart Items',
'items' => {'$ref' => 'readCartItem'}
Expand Down Expand Up @@ -103,6 +106,7 @@ def update
if cart.save
render json: cart, status: :ok, location: cart
else
cart.reload
render json: cart, status: :conflict, location: cart
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/interactors/resolve_cart_and_user.rb
Expand Up @@ -3,7 +3,7 @@ class ResolveCartAndUser

def call
begin
context.cart = Cart.find_by_id!(context.cart_id)
context.cart = Cart.unexpired.find_by_id!(context.cart_id)
rescue ActiveRecord::RecordNotFound
context.fail!(message: 'generic.not_found')
end
Expand Down
26 changes: 26 additions & 0 deletions app/models/cart.rb
@@ -1,8 +1,34 @@
class Cart < ActiveRecord::Base
EXPIRE_AFTER = 300 # seconds

belongs_to :user
has_many :cart_items, dependent: :destroy, autosave: true

validate :ensure_unexpired

scope :unexpired, -> { where unexpired_condition }

def self.unexpired_condition
arel_table[:updated_at].gteq(EXPIRE_AFTER.seconds.ago)
end

def total_price
cart_items.map(&:total_price).reduce(:+)
end

def expires_at
(updated_at || DateTime.now) + EXPIRE_AFTER.seconds
end

def expired?
DateTime.now > expires_at
end

private

def ensure_unexpired
if expired?
errors.add :expires_at, "can't be in the past"
end
end
end
2 changes: 2 additions & 0 deletions app/models/cart_item.rb
Expand Up @@ -5,6 +5,8 @@ class CartItem < ActiveRecord::Base

validate :enough_items_present_in_pricing

scope :unexpired, -> { joins(:cart).where(Cart.unexpired_condition) }

def product_name
product.try(:name) || ''
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/pricing.rb
Expand Up @@ -6,6 +6,6 @@ class Pricing < ActiveRecord::Base
validates :price, numericality: { only_integer: true }

def available_quantity
quantity - cart_items.sum(:quantity)
quantity - cart_items.unexpired.sum(:quantity)
end
end
1 change: 1 addition & 0 deletions app/representers/cart_representer.rb
Expand Up @@ -4,6 +4,7 @@ class CartRepresenter < ApplicationDecorator
property :lock_version, type: Integer
property :user_id, type: Integer
property :total_price, writeable: false, type: Integer
property :expires_at, writeable: false, type: Date

collection(
:cart_items,
Expand Down

0 comments on commit 600c092

Please sign in to comment.