Tuesday, 4 September 2012

Rubygem try_again

Earlier this year when I started working at PT Inovação, my job then (and now) was to take in specification written in gherkin and make the corresponding Ruby code that Cucumber would execute.
The applications we were  testing are really big, they're spread over many machines and use several databases.
One part of the application will create an entry on a database, later this has to be synced to another database, until this sync happen another part of the program might not work as indented.
Similarly, some parts of the application would add actions to a queue, this queue would later be processed and an entry to a database would be added, but anytime from zero seconds to one minute  was acceptable in terms of delay.
So, one of the problems my team was facing was some random failures, some times it was due to this difference in synchronization or delays, other times times was something simpler like a hiccup on the network that caused an http request to hang.
Trying to minimize the damage it caused to the global status of our tests on Jenkins I've came up with a little piece of code I've called Try Again and since then it has been widely used.

Unfortunately I don't have testes written for it, as it was one of the first pieces Ruby I ever did, but you can get as a nice little gem here. It was fun to code it, I had to understand how to pass blocks into methods and how to begin, raise, rescue all work together. It made me see how much I'd like Ruby!

An example with output set to stdout so we can see what's happening:

awls99@artworking:~$ jirb
irb(main):001:0> require 'rubygems' #ruby 1.8
=> true
irb(main):002:0>
irb(main):003:0* require 'try_again'
=> true
irb(main):004:0>
irb(main):005:0* class TooLow < StandardError;end
=> nil
irb(main):006:0>
irb(main):007:0* i = 0
=> 0
irb(main):008:0> TryAgain.retry(:error => TooLow, :output => $stdout) do
irb(main):009:1* puts i
irb(main):010:1> i += 1
irb(main):011:1> raise TooLow if i < 2
irb(main):012:1> end
0
TooLow for the 1# attempt
1
TooLow took 2 attempts to pass
=> true

Returns true because it succeeded. Wasn't it fun?
Between the first line and second line of output there's a 3 seconds delay.
You probably noticed I'm passing some options to the method, I've made it a bit customizable to fit many uses, you can read all the details on the readme.

I honestly don't expect this gem to get much use, but I'm sure somewhere someone will have a similar need and will install it, hopefully that person will find this before recreating it's own.