Commit, Recover & Transactions in Ruby

The spec says ‘transactions’, we immediately think ‘InnoDB’. But what if you’re not using a database, working on a light-weight application, or simply want to ensure a graceful recovery process? In the past that usually meant layers of additional logic, variables to keep and capture intermediate state, and all the other associated headaches. That is, until Gregory Brown and Austin Ziegler published Transaction::Simple - a gem, both literally and figuratively, which makes transaction support in Ruby a walk in the park.

Transaction::Simple provides a generic way to inject transaction support into any object - that’s right, any Ruby object. All the magic happens directly in memory, and thus there are no dependencies on additional backends. Your only theoretical limit is the memory of your machine, as object versions are created transparently and on the fly. In addition, the gem offers nested transactions, named transactions, transaction groups, commit and recovery procedures. Talk about fully featured! An example is due:

require 'rubygems' require 'transaction/simple' # Create a Ruby hash to store the balance bank = { "Bob" => 100 , "Jon" => 50 } # Add tranasction support to our Ruby hash bank . extend ( Transaction :: Simple ) bank . start_transaction ( :deposit ) bank [ 'Bob' ] += 25 # -> 125 bank . commit_transaction ( :deposit ) # block-form usage Transaction :: Simple . start ( bank ) do | t | # v has been extended with Transaction::Simple and an unnamed t . transaction_open? # -> true bank [ 'Bob' ] -= 25 # -> 100 # Hmm, I think we made an error, let's abort! t . rewind_transaction t . transaction_open? # -> true bank [ 'Bob' ] -= 25 # -> 100 puts "Bob's balance: #{ bank [ 'Bob' ] } , expecting: 100" # Break out of the transaction block and abort the transaction t . abort_transaction end puts "Bob's balance: #{ bank [ 'Bob' ] } , expecting: 125" # -> Bob's balance: 100, expecting: 100 # -> Bob's balance: 125, expecting: 125

Advanced Commit and Recovery

Applying similar patterns as we saw above, we can greatly simplify our recovery procedures in almost any Ruby program with minimal intervention:

# applying to arbitrary objects obj = SomeObject . new obj . extend ( Transaction :: Simple ) # Recover, rewind, and retry use-case begin v . start_transaction if v . do_work v . commit_transaction else v . abort_transaction rescue Exception v . rewind_transaction v . recover retry end

And as if this is not enough, make sure to explore transaction groups once you dive into the documentation. Named transactions, in conjunction with transaction groups will allow you to easily manage a group of objects as if they were a single object. Man, I love Ruby. What is your (most recent) favorite gem?