respawn 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/lib/respawn/array_try.rb +17 -0
- data/lib/respawn/enum.rb +7 -0
- data/lib/respawn/environment.rb +33 -0
- data/lib/respawn/exception_detector.rb +13 -15
- data/lib/respawn/handler.rb +1 -1
- data/lib/respawn/notifier_detector.rb +20 -4
- data/lib/respawn/setup.rb +37 -5
- data/lib/respawn/try.rb +39 -41
- data/lib/respawn/version.rb +1 -1
- data/lib/respawn.rb +4 -17
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 907c5b6836d134a5d5aad923a8a3998e471e853175f38eb03d8102f5ded7f75f
|
|
4
|
+
data.tar.gz: a36a96ac08c14eb76be898aaa47f9d45c05d7010acb787d9c02a37cb2fa2981e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb18883bbbeb24d44c61d8a33b3e9d57141f668ca0fe9ec5c8f5ac718d920e65786579ecdf163f5ade8883a1f44f1b4b065c8e0e80068cbc2b49726e670a5b40
|
|
7
|
+
data.tar.gz: 449b7fc2bd2530194bf7c515df721186df79420121de956f37c9fad5f47bd4b91419899a6ee62fb7f1ff6347ab1f2eec80f54bdd4780e7be100bb39240ff5348
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Respawn
|
|
4
|
+
module ArrayTry
|
|
5
|
+
refine Array do
|
|
6
|
+
def try!(element)
|
|
7
|
+
include?(element) or
|
|
8
|
+
raise(
|
|
9
|
+
ArgumentError,
|
|
10
|
+
%(Element "#{element}" not found in array #{self}),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
element
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/respawn/enum.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Respawn
|
|
4
|
+
ENVIRONMENTS = %w[
|
|
5
|
+
development
|
|
6
|
+
production
|
|
7
|
+
test
|
|
8
|
+
].freeze
|
|
9
|
+
|
|
10
|
+
Environment = Data.define(:env) do
|
|
11
|
+
using ArrayTry
|
|
12
|
+
|
|
13
|
+
def initialize(env:)
|
|
14
|
+
ENVIRONMENTS.try!(env)
|
|
15
|
+
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
ENVIRONMENTS.each do |name|
|
|
20
|
+
define_method("#{name}?") do
|
|
21
|
+
env == name
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.default
|
|
26
|
+
new(
|
|
27
|
+
ENV.fetch("RUBY_ENV") do
|
|
28
|
+
ENV.fetch("RAILS_ENV", "production")
|
|
29
|
+
end,
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
module Respawn
|
|
4
4
|
class ExceptionDetector
|
|
5
|
-
|
|
6
|
-
EOFError,
|
|
7
|
-
Errno::ECONNABORTED,
|
|
8
|
-
Errno::ECONNRESET,
|
|
9
|
-
Errno::EHOSTUNREACH,
|
|
10
|
-
].freeze
|
|
11
|
-
|
|
12
|
-
DYNAMIC_EXCEPTIONS = [
|
|
5
|
+
EXCEPTIONS = [
|
|
6
|
+
"EOFError",
|
|
7
|
+
"Errno::ECONNABORTED",
|
|
8
|
+
"Errno::ECONNRESET",
|
|
9
|
+
"Errno::EHOSTUNREACH",
|
|
13
10
|
"SocketError",
|
|
14
11
|
"Faraday::ConnectionFailed",
|
|
15
12
|
"Faraday::TimeoutError",
|
|
@@ -19,18 +16,19 @@ module Respawn
|
|
|
19
16
|
"Net::ReadTimeout",
|
|
20
17
|
"OpenSSL::SSL::SSLError",
|
|
21
18
|
"OpenURI::HTTPError",
|
|
19
|
+
"TestException",
|
|
22
20
|
].freeze
|
|
23
21
|
|
|
24
|
-
def self.call(
|
|
25
|
-
|
|
22
|
+
def self.call(env: Environment.default)
|
|
23
|
+
if env.test?
|
|
24
|
+
new.call
|
|
25
|
+
else
|
|
26
|
+
@_call ||= new.call
|
|
27
|
+
end
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def call
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def dynamic_exceptions
|
|
33
|
-
DYNAMIC_EXCEPTIONS.filter_map do
|
|
31
|
+
EXCEPTIONS.filter_map do
|
|
34
32
|
Object.const_get(it) if Object.const_defined?(it)
|
|
35
33
|
end
|
|
36
34
|
end
|
data/lib/respawn/handler.rb
CHANGED
|
@@ -2,8 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module Respawn
|
|
4
4
|
class NotifierDetector
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
# Postpone the actual setup until the first use of the method, to make
|
|
6
|
+
# sure that all the dependencies are loaded and all constants are already
|
|
7
|
+
# available. Memoize the result of first run, to avoid processing all the
|
|
8
|
+
# logic in subsequent invocations.
|
|
9
|
+
|
|
10
|
+
def self.call(env: Environment.default)
|
|
11
|
+
if env.test?
|
|
12
|
+
new.call
|
|
13
|
+
else
|
|
14
|
+
@_call ||= new.call
|
|
15
|
+
end
|
|
7
16
|
end
|
|
8
17
|
|
|
9
18
|
def call
|
|
@@ -12,8 +21,10 @@ module Respawn
|
|
|
12
21
|
|
|
13
22
|
private
|
|
14
23
|
|
|
24
|
+
attr_accessor :notifier
|
|
25
|
+
|
|
15
26
|
def detect_notifier
|
|
16
|
-
if defined?(TestNotifier)
|
|
27
|
+
if defined?(::TestNotifier)
|
|
17
28
|
TestNotifier.method(:call)
|
|
18
29
|
elsif defined?(::Sentry)
|
|
19
30
|
Sentry.method(:capture_exception)
|
|
@@ -24,7 +35,12 @@ module Respawn
|
|
|
24
35
|
elsif defined?(::Rollbar)
|
|
25
36
|
:rollbar
|
|
26
37
|
else
|
|
27
|
-
proc
|
|
38
|
+
proc do
|
|
39
|
+
raise(
|
|
40
|
+
Error,
|
|
41
|
+
"Notifier called, but no notifier detected!",
|
|
42
|
+
)
|
|
43
|
+
end
|
|
28
44
|
end
|
|
29
45
|
end
|
|
30
46
|
end
|
data/lib/respawn/setup.rb
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Respawn
|
|
4
|
+
ONFAIL = [
|
|
5
|
+
:notify,
|
|
6
|
+
:nothing,
|
|
7
|
+
:raise,
|
|
8
|
+
:handler,
|
|
9
|
+
].freeze
|
|
10
|
+
|
|
11
|
+
OPTIONS = {
|
|
12
|
+
notifier: NotifierDetector,
|
|
13
|
+
ex: ExceptionDetector,
|
|
14
|
+
onfail: :raise,
|
|
15
|
+
predicate: [],
|
|
16
|
+
tries: 5,
|
|
17
|
+
wait: 0.5,
|
|
18
|
+
env: -> { Environment.default },
|
|
19
|
+
setup: nil,
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
4
22
|
Setup =
|
|
5
|
-
Data.define(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
Data.define(*OPTIONS.keys) do
|
|
24
|
+
def initialize(**options)
|
|
25
|
+
with_defaults =
|
|
26
|
+
OPTIONS.map.to_h do |key, value|
|
|
27
|
+
[
|
|
28
|
+
key,
|
|
29
|
+
options.fetch(key) do
|
|
30
|
+
if value.respond_to?(:call)
|
|
31
|
+
value.call
|
|
32
|
+
else
|
|
33
|
+
value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
super(with_defaults)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
10
42
|
end
|
data/lib/respawn/try.rb
CHANGED
|
@@ -1,43 +1,33 @@
|
|
|
1
1
|
module Respawn
|
|
2
|
-
Environment = Data.define(:env) do
|
|
3
|
-
def test? = env == "test"
|
|
4
|
-
end
|
|
5
|
-
|
|
6
2
|
class Try
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ONFAIL = [
|
|
10
|
-
:notify,
|
|
11
|
-
:nothing,
|
|
12
|
-
:raise,
|
|
13
|
-
:handler,
|
|
14
|
-
].freeze
|
|
3
|
+
using ArrayTry
|
|
15
4
|
|
|
16
5
|
def self.call(*, **, &)
|
|
17
6
|
new(*, **).call(&)
|
|
18
7
|
end
|
|
19
8
|
|
|
20
|
-
def initialize(*exceptions,
|
|
21
|
-
|
|
9
|
+
def initialize(*exceptions, **options)
|
|
10
|
+
options.keys.each { OPTIONS.keys.try!(it) }
|
|
11
|
+
|
|
12
|
+
self.setup = options.fetch(:setup, Setup.new(**options))
|
|
13
|
+
|
|
14
|
+
self.notifier = options.fetch(:notifier, setup.notifier)
|
|
15
|
+
self.predicate = options.fetch(:predicate, setup.predicate)
|
|
16
|
+
|
|
17
|
+
exceptions = setup.ex if exceptions.empty?
|
|
18
|
+
|
|
22
19
|
self.exceptions = parse_exceptions(exceptions) + [PredicateError]
|
|
23
|
-
self.tries = tries
|
|
24
|
-
self.onfail = ONFAIL.
|
|
25
|
-
self.wait = wait
|
|
20
|
+
self.tries = options.fetch(:tries, setup.tries)
|
|
21
|
+
self.onfail = ONFAIL.try! options.fetch(:onfail, setup.onfail)
|
|
22
|
+
self.wait = options.fetch(:wait, setup.wait)
|
|
23
|
+
self.env = options.fetch(:env, Environment.default)
|
|
24
|
+
|
|
26
25
|
self.handler = Handler.new(onfail)
|
|
27
|
-
self.env = env || Environment.new(default_environment)
|
|
28
26
|
end
|
|
29
27
|
|
|
30
28
|
def call
|
|
31
|
-
yield(handler)
|
|
32
|
-
|
|
33
|
-
if condition.call(result)
|
|
34
|
-
raise(
|
|
35
|
-
PredicateError,
|
|
36
|
-
"Predicate ##{index} matched (#{condition.inspect})",
|
|
37
|
-
)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
29
|
+
yield(handler)
|
|
30
|
+
.tap(&method(:check_predicates))
|
|
41
31
|
rescue *exceptions => e
|
|
42
32
|
self.tries = tries - 1
|
|
43
33
|
handler.retry_number += 1
|
|
@@ -52,12 +42,25 @@ module Respawn
|
|
|
52
42
|
|
|
53
43
|
private
|
|
54
44
|
|
|
55
|
-
attr_accessor
|
|
45
|
+
attr_accessor(
|
|
46
|
+
:exceptions,
|
|
47
|
+
:tries,
|
|
48
|
+
:onfail,
|
|
49
|
+
:wait,
|
|
50
|
+
:handler,
|
|
51
|
+
:env,
|
|
52
|
+
:predicate,
|
|
53
|
+
:setup,
|
|
54
|
+
:notifier,
|
|
55
|
+
)
|
|
56
56
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
def check_predicates(result)
|
|
58
|
+
Array(predicate).each.with_index do |condition, index|
|
|
59
|
+
if condition.call(result)
|
|
60
|
+
raise(
|
|
61
|
+
PredicateError,
|
|
62
|
+
"Predicate ##{index} matched (#{condition.inspect})",
|
|
63
|
+
)
|
|
61
64
|
end
|
|
62
65
|
end
|
|
63
66
|
end
|
|
@@ -65,7 +68,7 @@ module Respawn
|
|
|
65
68
|
def perform_fail(exception)
|
|
66
69
|
case onfail
|
|
67
70
|
in :notify
|
|
68
|
-
|
|
71
|
+
notifier.call(exception)
|
|
69
72
|
in :nothing
|
|
70
73
|
nil
|
|
71
74
|
in :raise
|
|
@@ -76,16 +79,11 @@ module Respawn
|
|
|
76
79
|
end
|
|
77
80
|
|
|
78
81
|
def parse_exceptions(list)
|
|
79
|
-
list.
|
|
80
|
-
if exception == :network_errors
|
|
81
|
-
Respawn.default_setup.cause
|
|
82
|
-
|
|
82
|
+
list.each do |exception|
|
|
83
83
|
# This comparision will raise an error if the exception is not
|
|
84
84
|
# a class, which is what we want.
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
exception
|
|
88
|
-
end
|
|
86
|
+
raise Error, "Invalid exception passsed" unless exception <= Exception
|
|
89
87
|
end
|
|
90
88
|
end
|
|
91
89
|
end
|
data/lib/respawn/version.rb
CHANGED
data/lib/respawn.rb
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "zeitwerk"
|
|
4
4
|
|
|
5
|
-
loader = Zeitwerk::Loader.for_gem
|
|
6
|
-
loader.setup
|
|
7
|
-
loader.eager_load
|
|
8
|
-
|
|
9
5
|
module Respawn
|
|
10
6
|
class Error < StandardError; end
|
|
11
7
|
class PredicateError < StandardError; end
|
|
@@ -13,17 +9,8 @@ module Respawn
|
|
|
13
9
|
def self.try(...)
|
|
14
10
|
Try.call(...)
|
|
15
11
|
end
|
|
16
|
-
|
|
17
|
-
# Postpone the actuall setup until the first use of the method, to make
|
|
18
|
-
# sure that all the dependencies are loaded and all constants are already
|
|
19
|
-
# available.
|
|
20
|
-
|
|
21
|
-
def self.default_setup
|
|
22
|
-
@_default_setup ||=
|
|
23
|
-
Setup.new(
|
|
24
|
-
notifier: NotifierDetector.call,
|
|
25
|
-
cause: ExceptionDetector.call,
|
|
26
|
-
predicate: [],
|
|
27
|
-
)
|
|
28
|
-
end
|
|
29
12
|
end
|
|
13
|
+
|
|
14
|
+
loader = Zeitwerk::Loader.for_gem
|
|
15
|
+
loader.setup
|
|
16
|
+
loader.eager_load
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: respawn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mariusz Drozdziel
|
|
@@ -58,6 +58,9 @@ extensions: []
|
|
|
58
58
|
extra_rdoc_files: []
|
|
59
59
|
files:
|
|
60
60
|
- lib/respawn.rb
|
|
61
|
+
- lib/respawn/array_try.rb
|
|
62
|
+
- lib/respawn/enum.rb
|
|
63
|
+
- lib/respawn/environment.rb
|
|
61
64
|
- lib/respawn/exception_detector.rb
|
|
62
65
|
- lib/respawn/handler.rb
|
|
63
66
|
- lib/respawn/notifier_detector.rb
|