ruby 37 lines · 7 steps

Merging a guest cart into a user cart in Rails

A service object that folds an anonymous cart's items into a logged-in user's cart atomically, respecting stock limits.

Explained by highlit
1class CartMergeService
2 def initialize(guest_cart:, user_cart:)
3 @guest_cart = guest_cart
4 @user_cart = user_cart
5 end
6 
7 def call
8 return @user_cart if @guest_cart.nil? || @guest_cart == @user_cart
9 
10 ActiveRecord::Base.transaction do
11 @guest_cart.line_items.includes(:variant).find_each do |guest_item|
12 merge_line_item(guest_item)
13 end
14 
15 @guest_cart.destroy!
16 end
17 
18 @user_cart.reload
19 end
20 
21 private
22 
23 def merge_line_item(guest_item)
24 existing = @user_cart.line_items.find_by(variant_id: guest_item.variant_id)
25 
26 if existing
27 new_quantity = [existing.quantity + guest_item.quantity, guest_item.variant.stock].min
28 existing.update!(quantity: new_quantity)
29 else
30 @user_cart.line_items.create!(
31 variant: guest_item.variant,
32 quantity: [guest_item.quantity, guest_item.variant.stock].min,
33 price: guest_item.variant.current_price
34 )
35 end
36 end
37end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Wrapping multi-step writes in a transaction keeps the merge all-or-nothing, so a failure never leaves both carts half-updated.
  2. 2Guarding against nil and identical inputs up front makes the operation safe to call repeatedly without side effects.
  3. 3Clamping quantities to available stock at merge time prevents the combined cart from exceeding what can actually ship.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Merging a guest cart into a user cart in Rails — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code