Anticipate

About

Anticipate is a fluent interface for retrying blocks of code:

sleeping(0.1).seconds.between_tries.failing_after(20).tries do

  # Calls block every 0.1 seconds until it stops raising errors

end

Including the Anticipate module into your class gives you:

failing_after(3).tries {}
sleeping(0.1).seconds.between_tries {}
sleeping(123).seconds.between_tries.failing_after(456).tries {}

Blocks should contain an assertion, i.e. raise a descriptive error if some condition is unsatisfied. On the last iteration, any error will be wrapped in an Anticipate::TimeoutError and re-raised.

The first try is attempted immediately, there is no sleep before the first yield.

Defining your own timings

If lots of your code needs to wait for things to happen, you might consider defining your own timing module, e.g:

module Timing
  include Anticipate

  def very_soon
    sleeping(0.1).seconds.between_tries.failing_after(10).tries do
      yield
    end
  end

  def eventually
    sleeping(2).seconds.between_tries.failing_after(5).tries do
      yield
    end
  end
end

…that way you can adjust timing details in one place as the system grows.

Alternatives

I’m aware of a couple of gems performing the begin-rescue-sleep-retry dance, namely ‘retry’ and ‘attempt’. The aim of this library is to offer more expressive syntax.