The Setup
We have a model that has a state machine on it to activate, close, and cancel after meeting certain conditions. When closing this model, we want to manipulate an associated model as well, and we want these in a transaction to preserve data integrity. Unfortunately, there is logic in the associated model that doesn't like to have the original model in the new state before saving. The obvious solution is to set up the following callbacks:
state :closed, :enter => :enter_close, :after => :after_close def enter_closed # do stuff to model end def after_close # do stuff to associated model end
The Problem
It turns out that the callbacks are not wrapped in a transaction and that #after_close is called after the model saves, leaving the associated model in danger of getting in a back state or failing validations.The Solution
Using this post as a guide, we ended up with this code:class ActiveRecord::Base def self.transaction_around_transitions event_table.keys.each do |t| define_method("#{t}_with_lock!") do transaction do send("#{t}_without_lock!") end end alias_method_chain "#{t}!", :lock end end endAnd call that method after setting up the transitions:
event :close do transitions :from => :foo, :to => :bar end # other transitions... transaction_around_transitionsNow there is a transaction around the entire state change and its callbacks, and we'll be able to sleep tonight knowing that all the saving is safe and sound, all wrapped up in a nice, warm transaction.
No comments:
Post a Comment