retriable 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.