Giter Club home page Giter Club logo

tries's Introduction

Tries

Gem Version Build Status

Solidify your code and retry on petty exceptions.

Tries lets you retry a block of code multiple times, which is convenient for example when communicating with external APIs that might return an error the one second but work fine the next.

You can specify exactly how often the block of code is retried and which exceptions are caught.

Requirements

Tests run on Ruby >= 2.5 is tested, older versions might work as well.

Installation

Add this line to your application's Gemfile:

gem 'tries'

And then execute:

$ bundle

Or install it yourself as:

$ gem install tries

Usage

3.tries on: Timeout::Error do
  Mechanize.new.get 'https://www.google.com/'
end

Detailed usage

Helper code to explain how it works

FooError = Class.new(StandardError)
BarError = Class.new(StandardError)

@error_counter = 0

def method_that_raises_exception
  @error_counter += 1
  puts "Counter is #{@error_counter}"

  case @error_counter
  when 1 then raise FooError, 'retry'
  when 2 then raise FooError, 'retry'
  when 3 then raise BarError
  when 4 then raise StandardError
  end

  puts 'You made it through!'
end

Rescue all errors

4.tries do
  method_that_raises_exception
end

# => Counter is 1
# => Counter is 2
# => Counter is 3
# => Counter is 4
# => Counter is 5
# => You made it through!

Rescue a specific error

3.tries on: FooError do
  method_that_raises_exception
end

# => Counter is 1
# => Counter is 2
# => Counter is 3
# => BarError

Rescue multiple errors

3.tries on: [FooError, BarError] do
  method_that_raises_exception
end

# => Counter is 1
# => Counter is 2
# => Counter is 3
# => Counter is 4
# => StandardError

Rescue error on condition

3.tries if: ->(error) { error.message == 'retry' } do
  method_that_raises_exception
end

# => Counter is 1
# => Counter is 2
# => Counter is 3
# => BarError

Delay execution after error

delay is in seconds, fractions are possible

Static delay

4.tries delay: 1.5 do
  method_that_raises_exception
end

# => Counter is 1
# waits 1.5 seconds...
# => Counter is 2
# waits 1.5 seconds...
# => Counter is 3
# waits 1.5 seconds...
# => Counter is 4
# waits 1.5 seconds...
# => Counter is 5
# => You made it through!

Incremental delay

4.tries delay: 1.5, incremental: true do
  method_that_raises_exception
end

# => Counter is 1
# waits 1.5 seconds...
# => Counter is 2
# waits 3 seconds...
# => Counter is 3
# waits 4.5 seconds...
# => Counter is 4
# waits 6 seconds...
# => Counter is 5
# => You made it through!

Callback on error

You can set a method or Proc to be called every time an exception occurs. Either set it globally in an initializer, e.g. to log all exceptions to a service like Airbrake, or locally when calling tries. If both a global callback and a local callback are set, both are called, the global one first.

Global callback

# config/initializers/tries.rb
Tries.configure do |config|
  config.on_error = lambda do |exception, attempts, next_delay|
    puts "Whow, a #{exception.class} just occurred! It was attempt nr. #{attempts} to do whatever I was doing."
    if next_delay
      puts "I'm gonna wait #{next_delay} seconds and try again."
    else
      puts "A delay was not configured so I'm gonna go for it again immediately."
    end
  end
end
3.tries delay: 0.5, incremental: true do
  method_that_raises_exception
end

# => Counter is 1
# => Whow, a FooError just occurred! It was attempt nr. 1 to do whatever I was doing.
# => I'm gonna wait 0.5 seconds and try again.
# waits 0.5 seconds...
# => Counter is 2
# => Whow, a FooError just occurred! It was attempt nr. 2 to do whatever I was doing.
# => I'm gonna wait 1.0 seconds and try again.
# waits 1 second...
# => Counter is 3
# => Whow, a BarError just occurred! It was attempt nr. 3 to do whatever I was doing.
# => I'm gonna wait 1.5 seconds and try again.
# waits 1.5 seconds...
# => Counter is 4
# => StandardError

When using Rails, a global callback also lets you effectively disable Tries in development environment:

# 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

Local callback

callback = lambda do |exception, attempts, next_delay|
  puts "Local callback! Exception: #{exception.class}, attempt: #{attempts}, next_delay: #{next_delay}"
end

3.tries delay: 0.5, incremental: true, on_error: callback do
  method_that_raises_exception
end

# => Counter is 1
# => Local callback! Exception: FooError, attempt: 1, next_delay: 0.5
# waits 0.5 seconds...
# => Counter is 2
# => Local callback! Exception: FooError, attempt: 2, next_delay: 1.0
# waits 1 second...
# => Counter is 3
# => Local callback! Exception: BarError, attempt: 3, next_delay: 1.5
# waits 1.5 seconds...
# => Counter is 4
# => StandardError

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

Support

If you like this project, consider buying me a coffee! :)

tries's People

Contributors

manuelmeurer avatar oriolgual avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

oriolgual

tries's Issues

Incremental delay

What do you think of adding an option to make the delay incremental?

We used a similar snippet, but instead of:

Kernel.sleep delay if delay

We used:

Kernel.sleep (delay * attempts) if delay

The use case is for example when you're hitting an API too many times per second and getting an error, this way you can relax de retrying.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.