rackerlabs/otter — pull request #786 by radix (2014-11-18 18:13:21+00:00)
Title: Represent retry logic as transparent, easily testable policy objects

This branch allows code that wants to retry effects to represent this as a transparent policy object (with pretty minimal changes to the API of retrying). The way it's done is as follows:

- a new Effect intent, called "Retry", is added. It wraps the effect to retry and the retry policy (which is also just a callable).
- other things that were previously functions that closed over an environment have been replaced with classes with explicit attributes representing their data.
- should_retry_effect was dropped and now the users of this code should be using "retry_effect", in a way similar to the old way:

```new_eff = retry_effect(my_eff, retry_times(5), exponential_backoff_interval(2))```

To explain the change from function closures to objects-with-attributes:
- retry_times, which used to return a function closure, now returns an instance of a characteristic-based class RetryTimes (that is still a callable), which has a "max_retries" attribute (and "tries" to keep track of the state of how many times it's tried)
- exponential_backoff_interval has likewise been refactored into a characteristic-based ExponentialBackoffInterval class, with similar attributes.
- the inner function that used to be in should_retry_effect that invokes the can_retry function and the next_interval function and delays the retry has been factored into characteristic-based ShouldDelayAndRetry

This all comes together in that Retry.should_retry becomes a nice transparent tree of characteristic-based objects which expose all of their state and support equality. Now, unit tests just need to ensure that the policy looks sensible, instead of