retriable 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ - ree
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ ## 1.3.0
2
+
3
+ * Rewrote a lot of the code with inspiration from [attempt](https://rubygems.org/gems/attempt).
4
+ * Add timeout option to the code block.
5
+ * Include in Kernel by default, but allow require 'retriable/no_kernel' to load a non kernel version.
6
+ * Renamed `:times` option to `:tries`.
7
+ * Renamed `:sleep` option to `:interval`.
8
+ * Renamed `:then` option to `:on_retry`.
9
+ * Removed other callbacks, you can wrap retriable in a begin/rescue/else/ensure block if you need that functionality. It avoids the need to define multiple Procs and makes the code more readable.
10
+ * Rewrote most of the README
11
+
12
+ ## 1.2.0
13
+
14
+ * Forked the retryable-rb repo.
15
+ * Extend the Kernel module with the retriable method so you can use it anywhere without having to include it in every class.
16
+ * Update gemspec, Gemfile, and Raketask.
17
+ * Remove echoe dependency.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Robert Sosinski (http://www.robertsosinski.com)
1
+ Copyright (c) 2012 Jack Chu (http://www.jackchu.com)
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/README.md CHANGED
@@ -1,84 +1,125 @@
1
- Introduction
1
+ Retriable
2
2
  ============
3
3
 
4
- Retriable is an easy to use DSL to retry code if an exception is raised. This is especially useful when interacting with unreliable services that fail randomly.
4
+ [![Build Status](https://secure.travis-ci.org/kamui/retriable.png)](http://travis-ci.org/kamui/retriable)
5
+
6
+ Retriable is an simple DSL to retry code if an exception is raised. This is especially useful when interacting external api/services or file system calls.
5
7
 
6
8
  Installation
7
9
  ------------
10
+ Via command line:
8
11
 
9
- gem install retriable
10
-
11
- # In your ruby application
12
- require 'retriable'
13
-
14
- # In your Gemfile
15
- gem 'retriable'
16
-
17
- Using Retriable
18
- ---------------
19
-
20
- Code wrapped in a retriable block will be retried if a failure occurs. As such, code attempted once, will be retried again for another attempt if it fails to run.
21
-
22
- require 'retriable'
23
-
24
- class Api
25
- # Use it in methods that interact with unreliable services
26
- def get
27
- retriable do
28
- # code here...
29
- end
30
- end
31
- end
32
-
33
- By default, Retriable will rescue any exception inherited from `Exception`, retry once (for a total of two attempts) and sleep for a random amount time (between 0 to 100 milliseconds, in 10 millisecond increments). You can choose additional options by passing them via an options `Hash`.
34
-
35
- retriable :on => Timeout::Error, :times => 3, :sleep => 1 do
36
- # code here...
37
- end
38
-
39
- This example will only retry on a `Timeout::Error`, retry 3 times (for a total of 4 attempts) and sleep for a full second before each retry. You can also specify multiple errors to retry on by passing an array.
40
-
41
- retriable :on => [Timeout::Error, Errno::ECONNRESET] do
42
- # code here...
43
- end
44
-
45
- You can also have Ruby retry immediately after a failure by passing `false` as the sleep option.
12
+ ```ruby
13
+ gem install retriable
14
+ ```
46
15
 
47
- retriable :sleep => false do
48
- # code here...
49
- end
16
+ In your ruby script:
50
17
 
51
- Retriable also allows for callbacks to be defined, which is useful to log failures for analytics purposes or cleanup after repeated failures. Retriable has three types of callbacks: `then`, `finally`, and `always`.
18
+ ```ruby
19
+ require 'retriable'
20
+ ```
52
21
 
53
- `then`: Run every time a failure occurs.
22
+ In your Gemfile:
54
23
 
55
- `finally`: Run when the number of retries runs out.
24
+ ```ruby
25
+ gem 'retriable'
26
+ ```
56
27
 
57
- `always`: Run when the code wrapped in a retriable block passes or when the number of retries runs out.
28
+ By default, requiring 'retriable' will include the #retriable method into th Kernel so that you can use it anywhere. If you don't want this behaviour, you can load a non-kernel included version:
58
29
 
59
- The `then` and `finally` callbacks pass the exception raised, which can be used for logging or error control. All three callbacks also have a `handler`, which provides an interface to pass data between the code wrapped in the retriable block and the callbacks defined.
30
+ ```ruby
31
+ gem 'retriable', require => 'retriable/no_kernel'
32
+ ```
60
33
 
61
- Furthermore, each callback provides the number of `attempts`, `retries` and `times` that the wrapped code should be retried. As these are specified in a `Proc`, unnecessary variables can be left out of the parameter list.
34
+ Or in your ruby script:
62
35
 
63
- then_cb = Proc.new do |exception, handler, attempts, retries, times|
64
- log "#{exception.class}: '#{exception.message}' - #{attempts} attempts, #{retries} out of #{times} retries left."}
65
- end
36
+ ```ruby
37
+ require 'retriable/no_kernel'
38
+ ```
66
39
 
67
- finally_cb = Proc.new do |exception, handler|
68
- log "#{exception.class} raised too many times. First attempt at #{handler[:start]} and final attempt at #{Time.now}"
69
- end
40
+ Usage
41
+ ---------------
70
42
 
71
- always_cb = Proc.new do |handler, attempts|
72
- log "total time for #{attempts} attempts: #{Time.now - handler[:start]}"
73
- end
43
+ Code in a retriable block will be retried if an exception is raised. By default, Retriable will rescue any exception inherited from `Exception` and make 3 retry attempts before raising the last exception.
74
44
 
75
- retriable :then => then_cb do, :finally => finally_cb, :always => always_cb |handler|
76
- handler[:start] ||= Time.now
45
+ ```ruby
46
+ require 'retriable'
77
47
 
48
+ class Api
49
+ # Use it in methods that interact with unreliable services
50
+ def get
51
+ retriable do
78
52
  # code here...
79
53
  end
54
+ end
55
+ end
56
+ ```
57
+
58
+ Here are the available options:
59
+
60
+ `tries` (default: 3) - Number of attempts to make at running your code block
61
+ `interval` (default: 0) - Number of seconds to sleep between attempts
62
+ `timeout` (default: 0) - Number of seconds to allow the code block to run before raising a Timeout::Error
63
+ `on` (default: Exception) - Exception or array of exceptions to rescue for each attempt
64
+ `on_retry` - (default: nil) - Proc to call after each attempt is rescued
65
+
66
+ You can pass options via an options `Hash`. This example will only retry on a `Timeout::Error`, retry 3 times and sleep for a full second before each attempt.
67
+
68
+ ```ruby
69
+ retriable :on => Timeout::Error, :tries => 3, :interval => 1 do
70
+ # code here...
71
+ end
72
+ ```
73
+
74
+ You can also specify multiple errors to retry on by passing an array of exceptions.
75
+
76
+ ```ruby
77
+ retriable :on => [Timeout::Error, Errno::ECONNRESET] do
78
+ # code here...
79
+ end
80
+ ```
81
+
82
+ You can also specify a timeout if you want the code block to only make an attempt for X amount of seconds. This timeout is per attempt.
83
+
84
+ ```ruby
85
+ retriable :timeout => 1 do
86
+ # code here...
87
+ end
88
+ ```
89
+
90
+ If you need millisecond units of time for the sleep or the timeout:
91
+
92
+ ```ruby
93
+ retriable :interval => (200/1000.0), :timeout => (500/1000.0) do
94
+ # code here...
95
+ end
96
+ ```
97
+
98
+ Retriable also provides a callback called `:on_retry` that will run after an exception is rescued. This callback provides the number of `tries`, and the `exception` that was raised in the current attempt. As these are specified in a `Proc`, unnecessary variables can be left out of the parameter list.
99
+
100
+ ```ruby
101
+ on_retry = Proc.new do |exception, tries|
102
+ log "#{exception.class}: '#{exception.message}' - #{tries} attempts."}
103
+ end
104
+ ```
105
+
106
+ What if I want to execute a code block at the end, whether or not an exception was rescued ([ensure](http://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-ensure))? Or, what if I want to execute a code block if no exception is raised ([else](http://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-else))? Instead of providing more callbacks, I recommend you just wrap retriable in a begin/retry/else/ensure block:
107
+
108
+ ```ruby
109
+ begin
110
+ retriable do
111
+ # some code
112
+ end
113
+ rescue Exception => e
114
+ # run this if retriable ends up re-rasing the exception
115
+ else
116
+ # run this if retriable doesn't raise any exceptions
117
+ ensure
118
+ # run this no matter what, exception or no exception
119
+ end
120
+ ```
80
121
 
81
122
  Credits
82
123
  -------
83
124
 
84
- Retriable was originally forked from the retryable-rb gem by [Robert Sosinski](https://github.com/robertsosinski), which in turn originally inspired by code written by [Michael Celona](http://github.com/mcelona) and later assisted by [David Malin](http://github.com/dmalin).
125
+ Retriable was originally forked from the retryable-rb gem by [Robert Sosinski](https://github.com/robertsosinski), which in turn originally inspired by code written by [Michael Celona](http://github.com/mcelona) and later assisted by [David Malin](http://github.com/dmalin). The [attempt](https://rubygems.org/gems/attempt) gem by Daniel J. Berger was also an inspiration.
data/Rakefile CHANGED
@@ -4,7 +4,6 @@ require 'bundler'
4
4
  Bundler::GemHelper.install_tasks
5
5
 
6
6
  require 'rake/testtask'
7
-
8
7
  task :default => :test
9
8
 
10
9
  desc "Run tests"
data/lib/retriable.rb CHANGED
@@ -1,33 +1,4 @@
1
- require 'retriable/version'
1
+ # encoding: utf-8
2
2
 
3
- module Kernel
4
- def retriable(options = {})
5
- opts = {:on => Exception, :times => 1}.merge(options)
6
- handler = {}
7
-
8
- retry_exception = opts[:on].is_a?(Array) ? opts[:on] : [opts[:on]]
9
- times = retries = opts[:times]
10
- attempts = 0
11
-
12
- begin
13
- attempts += 1
14
-
15
- return yield(handler)
16
- rescue *retry_exception => exception
17
- opts[:then].call(exception, handler, attempts, retries, times) if opts[:then]
18
-
19
- if attempts <= times
20
- sleep(opts[:sleep] || (rand(11) / 100.0)) unless opts[:sleep] == false
21
- retries -= 1
22
- retry
23
- else
24
- opts[:finally].call(exception, handler, attempts, retries, times) if opts[:finally]
25
- raise exception
26
- end
27
- ensure
28
- opts[:always].call(handler, attempts, retries, times) if opts[:always]
29
- end
30
-
31
- yield(handler)
32
- end
33
- end
3
+ require 'retriable/no_kernel'
4
+ require 'retriable/core_ext/kernel'
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ module Kernel
4
+ include Retriable
5
+ end
6
+
7
+ class Object
8
+ include Kernel
9
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+
3
+ require 'retriable/retriable'
4
+ require 'retriable/version'
@@ -0,0 +1,64 @@
1
+ # encoding: utf-8
2
+
3
+ require 'timeout'
4
+
5
+ module Retriable
6
+ class Retry
7
+ attr_accessor :tries
8
+ attr_accessor :interval
9
+ attr_accessor :timeout
10
+ attr_accessor :on
11
+ attr_accessor :on_retry
12
+
13
+ def initialize
14
+ @tries = 3
15
+ @interval = 0
16
+ @timeout = nil
17
+ @on = Exception
18
+ @on_retry = nil
19
+
20
+ yield self if block_given?
21
+ end
22
+
23
+ def perform
24
+ count = 0
25
+ begin
26
+ if @timeout
27
+ Timeout::timeout(@timeout) { yield }
28
+ else
29
+ yield
30
+ end
31
+ rescue *[*on] => exception
32
+ @tries -= 1
33
+ if @tries > 0
34
+ count += 1
35
+ sleep @interval if @interval > 0
36
+ @on_retry.call(exception, count) if @on_retry
37
+ retry
38
+ else
39
+ raise
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def retriable(options = {}, &block)
46
+ opts = {
47
+ :tries => 3,
48
+ :on => Exception,
49
+ :interval => 1
50
+ }
51
+
52
+ opts.merge!(options)
53
+
54
+ raise 'No block given' unless block_given?
55
+
56
+ Retry.new do |r|
57
+ r.tries = opts[:tries]
58
+ r.on = opts[:on]
59
+ r.interval = opts[:interval]
60
+ r.timeout = opts[:timeout] if opts[:timeout]
61
+ r.on_retry = opts[:on_retry] if opts[:on_retry]
62
+ end.perform(&block)
63
+ end
64
+ end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Retriable
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.0'
5
5
  end
data/retriable.gemspec CHANGED
@@ -1,4 +1,5 @@
1
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
2
+
2
3
  $:.push File.expand_path("../lib", __FILE__)
3
4
  require "retriable/version"
4
5
 
@@ -18,4 +19,6 @@ Gem::Specification.new do |s|
18
19
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
21
  s.require_paths = ["lib"]
22
+
23
+ s.add_development_dependency 'minitest'
21
24
  end
@@ -1,72 +1,46 @@
1
- $:.unshift(File.dirname(__FILE__) + "../lib")
1
+ # encoding: utf-8
2
2
 
3
- require 'test/unit'
4
3
  require 'retriable'
4
+ require 'minitest/autorun'
5
5
 
6
- class RetriableTest < Test::Unit::TestCase
6
+ class RetriableTest < MiniTest::Unit::TestCase
7
7
  def test_without_arguments
8
8
  i = 0
9
9
 
10
10
  retriable do
11
11
  i += 1
12
-
13
12
  raise Exception.new
14
13
  end
15
14
  rescue Exception
16
- assert_equal i, 2
15
+ assert_equal 3, i
17
16
  end
18
17
 
19
- def test_with_one_exception_and_two_times
18
+ def test_with_one_exception_and_two_tries
20
19
  i = 0
21
20
 
22
- retriable :on => EOFError, :times => 2 do
21
+ retriable :on => EOFError, :tries => 2 do
23
22
  i += 1
24
-
25
23
  raise EOFError.new
26
24
  end
27
25
 
28
26
  rescue EOFError
29
- assert_equal i, 3
27
+ assert_equal i, 2
30
28
  end
31
29
 
32
- def test_with_arguments_and_handler
33
- i = 0
34
-
35
- then_cb = Proc.new do |e, h, a, r, t|
36
- assert_equal e.class, ArgumentError
37
- assert h[:value]
38
-
39
- assert_equal a, i
40
- assert_equal r, 6 - a
41
- assert_equal t, 5
42
- end
43
-
44
- finally_cb = Proc.new do |e, h, a, r, t|
45
- assert_equal e.class, ArgumentError
46
- assert h[:value]
47
-
48
- assert_equal a, 6
49
- assert_equal r, 0
50
- assert_equal t, 5
51
- end
52
-
53
- always_cb = Proc.new do |h, a, r, t|
54
- assert h[:value]
55
-
56
- assert_equal a, 6
57
- assert_equal r, 0
58
- assert_equal t, 5
59
- end
60
-
61
- retriable :on => [EOFError, ArgumentError], :then => then_cb, :finally => finally_cb, :always => always_cb, :times => 5, :sleep => 0.2 do |h|
62
- i += 1
63
-
64
- h[:value] = true
30
+ def test_with_arguments
31
+ i = 0
65
32
 
66
- raise ArgumentError.new
67
- end
33
+ on_retry = Proc.new do |exception, tries|
34
+ assert_equal exception.class, ArgumentError
35
+ assert_equal i, tries
36
+ end
68
37
 
69
- rescue ArgumentError
70
- assert_equal i, 6
38
+ retriable :on => [EOFError, ArgumentError], :on_retry => on_retry, :tries => 5, :sleep => 0.2 do |h|
39
+ i += 1
40
+ raise ArgumentError.new
71
41
  end
42
+
43
+ rescue ArgumentError
44
+ assert_equal 5, i
45
+ end
72
46
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: retriable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,19 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-10 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-02-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: &2163262320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2163262320
14
25
  description: Easy to use DSL to retry code if an exception is raised.
15
26
  email:
16
27
  - jack@jackchu.com
@@ -19,12 +30,16 @@ extensions: []
19
30
  extra_rdoc_files: []
20
31
  files:
21
32
  - .gitignore
22
- - CHANGELOG
33
+ - .travis.yml
34
+ - CHANGELOG.md
23
35
  - Gemfile
24
36
  - LICENSE
25
37
  - README.md
26
38
  - Rakefile
27
39
  - lib/retriable.rb
40
+ - lib/retriable/core_ext/kernel.rb
41
+ - lib/retriable/no_kernel.rb
42
+ - lib/retriable/retriable.rb
28
43
  - lib/retriable/version.rb
29
44
  - retriable.gemspec
30
45
  - test/retriable_test.rb
@@ -54,3 +69,4 @@ specification_version: 3
54
69
  summary: Easy to use DSL to retry code if an exception is raised.
55
70
  test_files:
56
71
  - test/retriable_test.rb
72
+ has_rdoc:
data/CHANGELOG DELETED
@@ -1 +0,0 @@
1
- v1.2.0. Fork the retryable-rb repo. Extend the Kernel module with the retriable method so you can use it anywhere without having to include it in every class. Update gemspec, Gemfile, and Raketask. Remove echoe dependency.