I'm using state_machine with ActiveRecord. I want to make sure that I can't charge a payment more than once. So:
event :attempt do
transition :unprocessed => :attempted
end
after_transition :unprocessed => :attempted, :do => :charge!
And since I want to prevent multiple payments, I lock the record before transactions (using postgres)
before_transition do |payment, transition|
payment.lock!('FOR UPDATE')
end
the call to #lock! reloads the record.
If I then get two instances of the payment and attempt them
p1 = Payment.find(1)
p2 = Payment.find(1)
p1.attempt
p2.attempt
The problem is that it's too late: even though payment.state will be "processed" after the first charge, and lock! will reload it (so p2 will have the right state), but the transition will think it's from unprocessed to processed, and charge again.
Is there a way to kill the transition in the callback if payment.state != transition.from? Is there a better way to ensure that a stale instance of a record can't do a no-longer-valid transition?