Blog

The joy of Ruby and small incremental improvements

Yesterday I added a small new feature to the Tries gem I authored a while ago and was once again reminded what a wonderful language Ruby is and how deeply satisfying it is to be able to work on one's own projects, yet opensource as much as possible to see others benefit from it as well.

The feature isn't anything world-changing, but I needed it right then, so I took 2 hours and implemented it. A feature born out of a real need from a real user of the Tries library so to say.

If you don't know what Tries does, it let's you wrap some code in a block, execute it and catch exceptions that might occur and retry the code. All this in a nice Ruby-ish syntax:

3.tries, delay: 1 do
  API::Twitter.get_all_tweets_ever_written
end

This piece of code will fetch each and every tweet ever authored (presumably to extract the answer to life, the universe and you-know-what), and if any exceptions occurs during this feat, it will rety a maximum number of three times, with a delay of one second in between. You can also explicitly limit the exceptions that will be caught or set an incremental delay.

The feature I added was an on_error block:

on_error = ->(exception, attempts, next_delay) {
  Rails.logger.error "Darn it, it happened again, a #{exception.class} error: #{exception.message}"
}

3.tries, delay: 1, on_error: on_error do
  API::Twitter.get_all_tweets_ever_written
end

That way you can keep retrying but still write out exceptions to a logfile or ping Airbrake or similar. The on_error callback can also be set globally in an initializer:

# config/initializers/tries.rb
Tries.configure do |config|
  config.on_error = lambda do |exception, attempts, next_delay|
    Rails.logger.error "Bish bash bosh, a #{exception.class} error crept up: #{exception.message}. Not to worry though, it was only attemp #{attempts} and I'll try again after waiting #{next_delay} seconds".
  end
end

It's one of those cases where, once the feature is there, you see immediately see how useful it is. It also let's you effectively disable Tries during development:

# config/initializers/tries.rb
Tries.configure do |config|
  config.on_error = lambda do |exception, attempts, next_delay|
    raise exception if Rails.env.development?
  end
end

Anyways, I hope other people find it as useful as I!

Discuss this post on Hacker News

Ideas? Constructive criticism? Think I'm stupid? Let me know in the comments!