Skip to content

Instantly share code, notes, and snippets.

@raul
Created October 7, 2011 06:42
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save raul/1269623 to your computer and use it in GitHub Desktop.
Save raul/1269623 to your computer and use it in GitHub Desktop.
retry_upto.rb
# Ruby `retry` with steroids:
#
# - retry up to 5 times without waiting between them and retrying after any exception
#
# retry_upto(5) do ... end
#
# - retry up to 5 times, waiting 2 seconds between retries
#
# retry_upto(5, :wait => 2) do ... end
#
# - retry up to 5 times, waiting 1 second initially and having increasing patience (2, 4, 8, ...)
#
# retry_upto(5, :wait => 1, :patience => 2) do ... end
#
# - retry up to 5 times, waiting 1 second initially and having decreasing patience (0.5, 0.25, 0.125, ...)
#
# retry_upto(5, :wait => 1, :patience => 0.5) do ... end
#
# - retry up to 5 times, waiting 1 second initially and retrying with a randomly increasing patience
#
# retry_upto(5, :wait => 1, :patience => lambda{ |x| x + rand(3) } ) do ... end
#
# - retry up to 5 times without waiting between retries, retrying only after a ZeroDivisionError
#
# retry_upto(5, :rescue_only => ZeroDivisionError) do ... end
#
# - retry up to 5 times, waiting 2 seconds between retries, retrying only after a ZeroDivisionError
#
# retry_upto(5, :wait => 2, :rescue_only => ZeroDivisionError) do ... end
#
def retry_upto(max_retries = 1, options = {})
yield
rescue (options[:rescue_only] || Exception)
raise if (max_retries -= 1) == 0
if options[:patience].respond_to?('*')
options[:wait] = options[:wait] * options[:patience]
elsif options[:patience].respond_to?(:call)
options[:wait] = options[:patience].call(options[:wait])
end
sleep(options[:wait] || 0)
retry
end
# Extends enumerator to allow usage like:
#
# 5.times.retry do
# ...
# end
#
class Enumerator
def retry(options = {}, &blk)
retry_upto(self.count, options, &blk)
end
end
@raul
Copy link
Author

raul commented Oct 7, 2011

It's taking a way better shape thanks to @ped and @glenngillen!!!

@raul
Copy link
Author

raul commented Oct 7, 2011

Added :patience feature as I've seen this use case several times too, thanks @jaimeiniesta!

@raul
Copy link
Author

raul commented Oct 7, 2011

Code compacted by @lleirborras, thanks man!

@ltello
Copy link

ltello commented Oct 8, 2011

I usually like to implement Object#first_you_respond_to(*methods) in my apps

Using it my implementation of retry_upto would be:

def retry_upto(max_retries = 1, opts = {})
yield
rescue (opts[:rescue_only] || Exception)
raise if (max_retries -= 1) == 0
if opts[:wait]
meth = opts[:patience].first_you_respond_to(:*, :call)
opts[:wait] = opts[:patience].send(meth, opts[:wait]) if meth
sleep opts[:wait]
end
retry
end

Hope you like it!

@raul
Copy link
Author

raul commented Oct 10, 2011

Sounds nice, though I'd prefer to keep this without external dependencies. I've finally published it as a gem: http://github.com/raul/retry_upto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment