stubbornly 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +9 -5
- data/Gemfile.lock +3 -1
- data/Guardfile +2 -0
- data/README.markdown +36 -9
- data/Rakefile +4 -0
- data/examples/http_status +41 -0
- data/lib/stubbornly.rb +52 -15
- data/lib/stubbornly/version.rb +1 -1
- data/stubbornly.gemspec +2 -1
- metadata +16 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61541dc62beb9db0f9aeefe99bf5dd84d1773efe2af2994a445f1b5f33a102b0
|
4
|
+
data.tar.gz: e69c526fd99e7ac41e1e5b813511d24d0bf67948ca0ffd5cc7c5ce0d1adf202f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffeaf2ef474a9f22d0fff5d17dc9d03d1c08dc94862f9cf48b74181501bdace3f65d798ef03aec92d9e3732204b20fce3f72570c5bf9e5394169e7ad046568c3
|
7
|
+
data.tar.gz: e99e74cbe1ba5081f6db301e267ca7fc8b900fc3445782fbf7c0384c224684e06589a918b69e265f17bee9f21641f148ae4a0fb818a00e159b03c90d3ddf2338
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2018-07-01
|
3
|
+
# on 2018-07-01 19:37:52 +0200 using RuboCop version 0.57.2.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -8,18 +8,22 @@
|
|
8
8
|
|
9
9
|
# Offense count: 1
|
10
10
|
Metrics/AbcSize:
|
11
|
-
Max:
|
11
|
+
Max: 27
|
12
12
|
|
13
|
-
# Offense count:
|
13
|
+
# Offense count: 2
|
14
14
|
Metrics/CyclomaticComplexity:
|
15
15
|
Max: 7
|
16
16
|
|
17
17
|
# Offense count: 2
|
18
18
|
# Configuration parameters: CountComments.
|
19
19
|
Metrics/MethodLength:
|
20
|
-
Max:
|
20
|
+
Max: 27
|
21
21
|
|
22
22
|
# Offense count: 1
|
23
23
|
Security/Eval:
|
24
24
|
Exclude:
|
25
|
-
- 'spec/
|
25
|
+
- 'spec/system/readme_spec.rb'
|
26
|
+
|
27
|
+
Style/MutableConstant:
|
28
|
+
Exclude:
|
29
|
+
- 'lib/stubbornly/version.rb'
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
stubbornly (0.
|
4
|
+
stubbornly (0.2.0)
|
5
5
|
null-logger
|
6
6
|
|
7
7
|
GEM
|
@@ -101,6 +101,7 @@ GEM
|
|
101
101
|
unf_ext
|
102
102
|
unf_ext (0.0.7.5)
|
103
103
|
unicode-display_width (1.4.0)
|
104
|
+
yard (0.9.14)
|
104
105
|
|
105
106
|
PLATFORMS
|
106
107
|
ruby
|
@@ -117,6 +118,7 @@ DEPENDENCIES
|
|
117
118
|
rspec
|
118
119
|
rubocop
|
119
120
|
stubbornly!
|
121
|
+
yard
|
120
122
|
|
121
123
|
BUNDLED WITH
|
122
124
|
1.16.2
|
data/Guardfile
CHANGED
@@ -17,5 +17,7 @@ guard :rspec, cmd: 'bundle exec rspec' do
|
|
17
17
|
watch(rspec.spec_helper) { rspec.spec_dir }
|
18
18
|
watch(rspec.spec_files)
|
19
19
|
watch(%r{^lib/.*/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
20
|
+
watch('README.markdown') { 'spec/system/examples_spec.rb' }
|
21
|
+
watch(%r{^examples/(.+)}) { |m| "spec/system/examples/#{m[1]}_spec.rb" }
|
20
22
|
dsl.watch_spec_files_for(dsl.ruby.lib_files)
|
21
23
|
end
|
data/README.markdown
CHANGED
@@ -2,20 +2,20 @@
|
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/suhlig/stubbornly.svg?branch=master)](https://travis-ci.org/suhlig/stubbornly)
|
4
4
|
|
5
|
-
Stubbornly retries a given block until the maximum number of attempts or timeout has been reached.
|
5
|
+
Stubbornly retries a given block until the maximum number of attempts or a timeout has been reached.
|
6
6
|
|
7
|
-
##
|
7
|
+
## Examples
|
8
|
+
|
9
|
+
### Timeout
|
8
10
|
|
9
11
|
```ruby
|
10
12
|
require 'stubbornly'
|
11
13
|
require 'http'
|
12
|
-
require 'logger'
|
13
14
|
|
14
15
|
class Checker
|
15
16
|
def initialize(url)
|
16
17
|
@url = url
|
17
|
-
@
|
18
|
-
@stubbornly = Stubbornly.new(logger: @logger)
|
18
|
+
@stubbornly = Stubbornly.new
|
19
19
|
end
|
20
20
|
|
21
21
|
def up?
|
@@ -25,10 +25,37 @@ class Checker
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
warn "
|
28
|
+
begin
|
29
|
+
Checker.new('http://localhost:8765').up?
|
30
|
+
rescue => e
|
31
|
+
warn "Error: #{e.message}"
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
### Maximum number of attempts
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
require 'stubbornly'
|
39
|
+
require 'http'
|
40
|
+
|
41
|
+
class Checker
|
42
|
+
def initialize(url)
|
43
|
+
@url = url
|
44
|
+
@stubbornly = Stubbornly.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def up?
|
48
|
+
@stubbornly.retry(attempts: 3) do
|
49
|
+
response = HTTP.get(@url)
|
50
|
+
puts "The site at #{@url} is up 👍"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
Checker.new('http://localhost:8765').up?
|
57
|
+
rescue => e
|
58
|
+
warn "Error: #{e.message}"
|
32
59
|
end
|
33
60
|
```
|
34
61
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'stubbornly'
|
5
|
+
require 'http'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
#
|
9
|
+
# Checks a URL
|
10
|
+
#
|
11
|
+
class Checker
|
12
|
+
def initialize(url)
|
13
|
+
@url = url
|
14
|
+
@logger = Logger.new(STDERR, progname: self.class)
|
15
|
+
@stubbornly = Stubbornly.new(logger: Logger.new(STDERR, progname: Stubbornly))
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Return the status code of the given URL.
|
20
|
+
# Retry if failed, but
|
21
|
+
# - not more often than specified
|
22
|
+
# - only within the given timeout.
|
23
|
+
#
|
24
|
+
def status
|
25
|
+
@logger.info "Checking status of #{@url}"
|
26
|
+
@stubbornly.retry(timeout: 10, attempts: 4) do
|
27
|
+
HTTP.get(@url).code.tap do |result|
|
28
|
+
@logger.info "Result is #{result}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
url = ARGV.shift || 'http://example.com'
|
35
|
+
|
36
|
+
begin
|
37
|
+
puts Checker.new(url).status
|
38
|
+
rescue StandardError => e
|
39
|
+
warn "Error: #{e.message}"
|
40
|
+
exit 1
|
41
|
+
end
|
data/lib/stubbornly.rb
CHANGED
@@ -4,37 +4,70 @@ require 'null_logger'
|
|
4
4
|
require 'English'
|
5
5
|
|
6
6
|
class Stubbornly
|
7
|
-
|
7
|
+
#
|
8
|
+
# Constructs a new `Stubbornly` instance.
|
9
|
+
#
|
10
|
+
# If a block is passed, it will be called to calculate the sleep time (in
|
11
|
+
# seconds) before the next attempt is made. The attempt counter is passed
|
12
|
+
# as a block argument.
|
13
|
+
#
|
14
|
+
# This is useful to determine the backoff characteristics. The result of the
|
15
|
+
# block will be the sleep time in seconds. For instance, the following code
|
16
|
+
# would implement an exponential backoff with retries after 1, 3, 7, 15,
|
17
|
+
# 31, ... seconds:
|
18
|
+
#
|
19
|
+
# Stubbornly.new {|attempt| 2**attempt - 1 }
|
20
|
+
#
|
21
|
+
# The default backoff is 1 (constant), i.e. the attempts are equidistant at 1s.
|
22
|
+
#
|
23
|
+
def initialize(logger: NullLogger.new, &block)
|
8
24
|
@logger = logger
|
25
|
+
@backoff = block || proc { 1 }
|
9
26
|
end
|
10
27
|
|
11
|
-
|
12
|
-
|
28
|
+
#
|
29
|
+
# Calls the given block, rescuing its errors, and retries until either the
|
30
|
+
# given timeout or the maximum number of attempts was exceeded.
|
31
|
+
#
|
32
|
+
# When an attempt raises an error, this method will sleep before it retries.
|
33
|
+
# The number of seconds to sleep is determined by the block passed to {#initialize}.
|
34
|
+
#
|
35
|
+
# If the block succeeds, its value is returned.
|
36
|
+
#
|
37
|
+
def retry(timeout: Float::INFINITY, attempts: Float::INFINITY, &block)
|
38
|
+
@logger.debug("Attempting not more than #{attempts} times") unless attempts.infinite?
|
13
39
|
@logger.debug("Will time out after #{timeout}s") unless timeout.infinite?
|
14
40
|
|
15
|
-
attempt =
|
41
|
+
attempt = 1
|
16
42
|
start = Time.now
|
17
43
|
|
18
44
|
begin
|
19
|
-
@logger.info "Attempt ##{attempt}"
|
20
|
-
|
21
|
-
|
45
|
+
@logger.info "Attempt ##{attempt} #{after_elapsed_since(start)}"
|
46
|
+
|
47
|
+
if block
|
48
|
+
yield(attempt, elapsed_since(start)).tap do
|
49
|
+
@logger.info "Success #{after_elapsed_since(start)} and #{attempt} attempts"
|
50
|
+
end
|
22
51
|
end
|
23
|
-
rescue StandardError
|
24
|
-
|
25
|
-
retry_after = 2**attempt - 1
|
26
|
-
@logger.warn "#{$ERROR_INFO.message}. Trying again in #{retry_after}s."
|
52
|
+
rescue StandardError => error
|
53
|
+
@logger.warn error.message
|
27
54
|
|
28
55
|
if attempt >= attempts
|
29
|
-
@logger.error "Maximum number of attempts (#{
|
30
|
-
|
56
|
+
@logger.error "Maximum number of attempts (#{attempts}) reached (#{after_elapsed_since(start)})"
|
57
|
+
raise
|
31
58
|
end
|
32
59
|
|
60
|
+
# TODO: Give up NOW if retry_after would exceed timeout
|
61
|
+
|
33
62
|
if elapsed_since(start) >= timeout
|
34
|
-
@logger.info "Timed out
|
35
|
-
|
63
|
+
@logger.info "Timed out #{after_elapsed_since(start)} (#{attempt} attempts)"
|
64
|
+
raise
|
36
65
|
end
|
37
66
|
|
67
|
+
attempt += 1
|
68
|
+
retry_after = @backoff.call(attempt)
|
69
|
+
|
70
|
+
@logger.warn "Trying again in #{retry_after}s."
|
38
71
|
sleep(retry_after)
|
39
72
|
retry
|
40
73
|
end
|
@@ -45,4 +78,8 @@ class Stubbornly
|
|
45
78
|
def elapsed_since(start)
|
46
79
|
Time.now - start
|
47
80
|
end
|
81
|
+
|
82
|
+
def after_elapsed_since(start)
|
83
|
+
"after #{elapsed_since(start).round(2)}s"
|
84
|
+
end
|
48
85
|
end
|
data/lib/stubbornly/version.rb
CHANGED
data/stubbornly.gemspec
CHANGED
@@ -28,10 +28,11 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'guard'
|
29
29
|
spec.add_development_dependency 'guard-bundler'
|
30
30
|
spec.add_development_dependency 'guard-rspec'
|
31
|
-
spec.add_development_dependency 'http'
|
31
|
+
spec.add_development_dependency 'http' # examples
|
32
32
|
spec.add_development_dependency 'pry'
|
33
33
|
spec.add_development_dependency 'pry-byebug'
|
34
34
|
spec.add_development_dependency 'rake'
|
35
35
|
spec.add_development_dependency 'rspec'
|
36
36
|
spec.add_development_dependency 'rubocop'
|
37
|
+
spec.add_development_dependency 'yard'
|
37
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stubbornly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steffen Uhlig
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: yard
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
description: Stubbornly retries a given block until the maximum number of attempts
|
168
182
|
or timeout has been reached.
|
169
183
|
email:
|
@@ -183,6 +197,7 @@ files:
|
|
183
197
|
- LICENSE.txt
|
184
198
|
- README.markdown
|
185
199
|
- Rakefile
|
200
|
+
- examples/http_status
|
186
201
|
- lib/stubbornly.rb
|
187
202
|
- lib/stubbornly/version.rb
|
188
203
|
- stubbornly.gemspec
|