Using Enums as a Simple State Machine

Even without a dedicated state machine, you can enforce some logic using enums.

Example: Enforcing Transitions in Methods

class Order < ApplicationRecord
  enum :status, { pending: 0, confirmed: 1, shipped: 2, delivered: 3, canceled: 4 }

  def confirm!
    update!(status: :confirmed) if pending?
  end

  def ship!
    update!(status: :shipped) if confirmed?
  end

  def deliver!
    update!(status: :delivered) if shipped?
  end
end

Now, calling order.ship! directly won’t work unless it’s confirmed first.

order = Order.new
order.ship!  # ❌ This won’t work because the order isn’t confirmed.
order.confirm!
order.ship!  # ✅ Now it's valid.

While this improves state management, it still doesn’t strictly prevent developers from manually setting invalid states.