Thursday, February 21, 2008

Coupling rollback actions with transaction actions

The system I'm currently developing needs to import a variety of data from another system. The data arrives in batches, and within a batch some items might be valid and some might be invalid - I expect that this is a common problem. An item in the batch might also update many different objects within my domain model, and I won't necessarily know if the item is invalid until I try to commit the changes to the domain model. It became fairly clear that I had some recurring code patterns in my application, so of course I wanted to extract them into some abstraction and Ruby's reflection gave me a nice way to capture this.



What I needed was to be able to associate a piece of code that created or modified some domain objects with the code that cleaned up the changes to the domain model on rollback (assuming that the database transaction handled the persistence parts). I could have used blocks, but I find that unlabelled blocks aren't very expressive so I decided to use methods (and their names) instead of blocks. Here's an example of using my method, #within_transaction_with_rollback; in the example :create_reference_objects and :clear_reference_objects are methods defined somewhere else in the class.




[sourcecode language='ruby']
def process
within_transaction_with_rollback(:create_reference_objects, :clear_reference_objects)
end
[/sourcecode]


And here's the implementation of my method, for those who are interested. I'm sure that there are plenty of other possible implementations :-)




[sourcecode language='ruby']
module Transactions

def within_transaction_with_rollback(transaction_method, rollback_method)
begin
within_transaction(transaction_method)
rescue
self.send(rollback_method)
end
end

def within_transaction(create_method)
self.class.transaction do
self.send(create_method)
end
end

end

[/sourcecode]


No comments:

Post a Comment