def retrier(opts = {}, &_block)
      tries  = opts.fetch(:tries,            4)
      wait   = opts.fetch(:sleep,            1)
      on     = opts.fetch(:on,   StandardError)
      match  = opts.fetch(:match,         /.*/)
      insure = opts.fetch(:ensure, Proc.new {})

      retries         = 0
      retry_exception = nil

      begin
        yield retries, retry_exception
      rescue *[on] => exception
        raise unless exception.message =~ match
        raise if retries + 1 >= tries

        begin
          sleep wait.respond_to?(:call) ? wait.call(retries) : wait
        rescue *[on]
        end

        retries += 1
        retry_exception = exception
        retry
      ensure
        insure.call(retries)
      end
    end
def retrier(opts = {}, &_block)
      tries  = opts.fetch(:tries,            4)
      wait   = opts.fetch(:sleep,            1)
      on     = opts.fetch(:on,   StandardError)
      match  = opts.fetch(:match,         /.*/)
      insure = opts.fetch(:ensure, Proc.new {})

      retries         = 0
      retry_exception = nil

      begin
        yield retries, retry_exception
      rescue *[on] => exception
        raise unless exception.message =~ match
        raise if retries + 1 >= tries

        begin
          sleep wait.respond_to?(:call) ? wait.call(retries) : wait
        rescue *[on]
        end

        retries += 1
        retry_exception = exception
        retry
      ensure
        insure.call(retries)
      end
    end
Runs a code block, and retries it when an exception occurs. It is
 configured using four optional parameters `:tries`, `:on`, `:sleep`,
 `:match`, `:ensure` and runs the passed block. Should an exception
 occur, it'll retry for (n-1) times. Should the number of retries be
 reached without success, the last exception will be raised.

 @example open an URL, retry up to two times when an OpenURI::HTTPError
   occurs.
     retrier(tries: 3, on: OpenURI::HTTPError) do
       xml = open('http://example.com/test.html').read
     end

 @example do _something_, retry up to four times for either ArgumentErro
   or TimeoutError exceptions.
     retrier(tries: 5, on: [ArgumentError, TimeoutError]) do
       # _something_ code
     end

 @example ensure that block of code is executed, regardless of whether an
   exception was raised. It doesn't matter if the block exits normally,
   if it retries to execute block of code, or if it is terminated by an
   uncaught exception -- the ensure block will get run.
     f = File.open('testfile')
     ensure_cb = Proc.new do |retries|
       puts "total retry attempts: #{retries}"
       f.close
     end
     retrier(insure: ensure_cb) do
       # process file
     end

 @example sleeping: by default Retrier waits for one second between
   retries. You can change this and even provide your own exponential
   backoff scheme.
     retrier(sleep:  0) { }              # don't pause between retries
     retrier(sleep: 10) { }              # sleep 10s between retries
     retrier(sleep: ->(n) { 4**n }) { }  # sleep 1, 4, 16, etc. each try

 @example matching error messages: you can also retry based on the
   exception message:
     retrier(matching: /IO timeout/) do |retries, exception|
       raise "yo, IO timeout!" if retries == 0
     end

 @example block parameters: your block is called with two optional
   parameters; the number of tries until now, and the most recent
   exception.
     retrier do |tries, exception|
       puts "try #{tries} failed with error: #{exception}" if retries > 0
       # keep trying...
     end

 @param opts [Hash]

 @option opts [Fixnum] :tries
   Number of attempts to retry before raising the last exception

 @option opts [Fixnum] :sleep
   Number of seconds to wait between retries, use lambda to exponentially
   increasing delay between retries.

 @option opts [Array(Exception)] :on
   The type of exception(s) to catch and retry on

 @option opts [Regex] :matching
   Match based on the exception message

 @option opts [Block] :ensure
   Ensure a block of code is executed, regardless of whether an exception
   is raised

 @yield [Proc]
   A block that will be run, and if it raises an error, re-run until
   success, or timeout is finally reached.

 @raise [Exception]
   Last Exception that caused the loop to retry before giving up.

 @return [Proc]
   The value of the passed block.

 @api public