stoplight 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/LICENSE.md +1 -1
- data/README.md +66 -63
- data/lib/stoplight.rb +10 -15
- data/lib/stoplight/color.rb +9 -0
- data/lib/stoplight/data_store.rb +0 -146
- data/lib/stoplight/data_store/base.rb +7 -130
- data/lib/stoplight/data_store/memory.rb +25 -100
- data/lib/stoplight/data_store/redis.rb +61 -119
- data/lib/stoplight/default.rb +34 -0
- data/lib/stoplight/error.rb +0 -42
- data/lib/stoplight/failure.rb +21 -25
- data/lib/stoplight/light.rb +42 -127
- data/lib/stoplight/light/runnable.rb +97 -0
- data/lib/stoplight/notifier/base.rb +1 -4
- data/lib/stoplight/notifier/hip_chat.rb +17 -32
- data/lib/stoplight/notifier/io.rb +9 -9
- data/lib/stoplight/state.rb +9 -0
- data/spec/spec_helper.rb +2 -3
- data/spec/stoplight/color_spec.rb +39 -0
- data/spec/stoplight/data_store/base_spec.rb +56 -36
- data/spec/stoplight/data_store/memory_spec.rb +120 -2
- data/spec/stoplight/data_store/redis_spec.rb +123 -24
- data/spec/stoplight/data_store_spec.rb +2 -69
- data/spec/stoplight/default_spec.rb +86 -0
- data/spec/stoplight/error_spec.rb +29 -0
- data/spec/stoplight/failure_spec.rb +61 -51
- data/spec/stoplight/light/runnable_spec.rb +234 -0
- data/spec/stoplight/light_spec.rb +143 -191
- data/spec/stoplight/notifier/base_spec.rb +8 -11
- data/spec/stoplight/notifier/hip_chat_spec.rb +66 -55
- data/spec/stoplight/notifier/io_spec.rb +49 -21
- data/spec/stoplight/notifier_spec.rb +3 -0
- data/spec/stoplight/state_spec.rb +39 -0
- data/spec/stoplight_spec.rb +2 -65
- metadata +55 -19
- data/spec/support/data_store.rb +0 -36
- data/spec/support/fakeredis.rb +0 -3
- data/spec/support/hipchat.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad268d747b85be7b3a0d773d9aeec70b1b22c25a
|
4
|
+
data.tar.gz: 56c5c8f88361a3c9d705212d9725251017097ddb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 043418c2e767031b2a229ad2e22d378c52101d342cdec3bb600bec88c5c46238c345eadb558d779ccedb8f60122b81b5cddf969dfb7dbf475e7bfe3e0cb7b3c2
|
7
|
+
data.tar.gz: 8a9952d663eb04f03e05736f3541ac09b69e69776c80265116d43a7c4c4e2e8d699691598ef8ae35d1c0018b5bc7880f73614894d79e194987961e857c2f7016
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
This project uses [Semantic Versioning][1].
|
4
|
+
|
5
|
+
- Data stores and notifiers can be configured on a per-stoplight basis. This
|
6
|
+
allows stoplights to use stoplights internally.
|
7
|
+
- Stoplights use stoplights internally to wrap calls to data stores and
|
8
|
+
notifiers. This means they gracefully handle either going down.
|
9
|
+
- Data stores only store failures and states. Also failures are stored in a ring
|
10
|
+
buffer. This drastically reduces the amount of data stored.
|
11
|
+
- Stoplights will use the fallback (if it's given) when they fail while they're
|
12
|
+
green. This means they won't re-raise exceptions if you provide a fallback.
|
13
|
+
- Stoplights pass the error to their notifiers when transitioning from green to
|
14
|
+
red.
|
15
|
+
|
3
16
|
## v0.4.1 (2014-10-03)
|
4
17
|
|
5
18
|
- Fixed a bug that caused green to red notifications to sometimes not be sent.
|
@@ -62,3 +75,5 @@
|
|
62
75
|
## v0.1.0 (2014-08-12)
|
63
76
|
|
64
77
|
- Initial release.
|
78
|
+
|
79
|
+
[1]: http://semver.org/spec/v2.0.0.html
|
data/LICENSE.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2014 Cameron Desautels &
|
1
|
+
Copyright (c) 2014 Cameron Desautels, Taylor Fausak & Justin Steffy
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
4
|
this software and associated documentation files (the "Software"), to deal in
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ Check out [stoplight-admin][12] for controlling your stoplights.
|
|
33
33
|
Add it to your Gemfile:
|
34
34
|
|
35
35
|
``` rb
|
36
|
-
gem 'stoplight', '~> 0.
|
36
|
+
gem 'stoplight', '~> 0.5.0'
|
37
37
|
```
|
38
38
|
|
39
39
|
Or install it manually:
|
@@ -42,8 +42,6 @@ Or install it manually:
|
|
42
42
|
$ gem install stoplight
|
43
43
|
```
|
44
44
|
|
45
|
-
This project uses [Semantic Versioning][13].
|
46
|
-
|
47
45
|
## Setup
|
48
46
|
|
49
47
|
### Data store
|
@@ -53,22 +51,22 @@ Stoplight uses an in-memory data store out of the box.
|
|
53
51
|
``` irb
|
54
52
|
>> require 'stoplight'
|
55
53
|
=> true
|
56
|
-
>> Stoplight.
|
54
|
+
>> Stoplight::Light.default_data_store
|
57
55
|
=> #<Stoplight::DataStore::Memory:...>
|
58
56
|
```
|
59
57
|
|
60
58
|
If you want to use a persistent data store, you'll have to set it up. Currently
|
61
59
|
the only supported persistent data store is Redis. Make sure you have [the Redis
|
62
|
-
gem][
|
60
|
+
gem][13] installed before configuring Stoplight.
|
63
61
|
|
64
62
|
``` irb
|
65
63
|
>> require 'redis'
|
66
64
|
=> true
|
67
|
-
>> redis = Redis.new
|
68
|
-
=> #<Redis
|
65
|
+
>> redis = Redis.new
|
66
|
+
=> #<Redis client ...>
|
69
67
|
>> data_store = Stoplight::DataStore::Redis.new(redis)
|
70
68
|
=> #<Stoplight::DataStore::Redis:...>
|
71
|
-
>> Stoplight.
|
69
|
+
>> Stoplight::Light.default_data_store = data_store
|
72
70
|
=> #<Stoplight::DataStore::Redis:...>
|
73
71
|
```
|
74
72
|
|
@@ -77,22 +75,22 @@ gem][14] installed before configuring Stoplight.
|
|
77
75
|
Stoplight sends notifications to standard error by default.
|
78
76
|
|
79
77
|
``` irb
|
80
|
-
>> Stoplight.
|
78
|
+
>> Stoplight::Light.default_notifiers
|
81
79
|
=> [#<Stoplight::Notifier::IO:...>]
|
82
80
|
```
|
83
81
|
|
84
82
|
If you want to send notifications elsewhere, you'll have to set them up.
|
85
83
|
Currently the only other supported notifier is HipChat. Make sure you have [the
|
86
|
-
HipChat gem][
|
84
|
+
HipChat gem][14] installed before configuring Stoplight.
|
87
85
|
|
88
86
|
``` irb
|
89
87
|
>> require 'hipchat'
|
90
88
|
=> true
|
91
|
-
>>
|
89
|
+
>> hip_chat = HipChat::Client.new('token')
|
92
90
|
=> #<HipChat::Client:...>
|
93
|
-
>> notifier = Stoplight::Notifier::HipChat.new(
|
91
|
+
>> notifier = Stoplight::Notifier::HipChat.new(hip_chat, 'room')
|
94
92
|
=> #<Stoplight::Notifier::HipChat:...>
|
95
|
-
>> Stoplight.
|
93
|
+
>> Stoplight::Light.default_notifiers += [notifier]
|
96
94
|
=> [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::HipChat:...>]
|
97
95
|
```
|
98
96
|
|
@@ -106,8 +104,8 @@ Stoplight:
|
|
106
104
|
``` rb
|
107
105
|
# config/initializers/stoplight.rb
|
108
106
|
require 'stoplight'
|
109
|
-
Stoplight.
|
110
|
-
Stoplight.
|
107
|
+
Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(...)
|
108
|
+
Stoplight::Light.default_notifiers += [Stoplight::Notifier::HipChat.new(...)]
|
111
109
|
```
|
112
110
|
|
113
111
|
## Basic usage
|
@@ -125,8 +123,8 @@ the green state.
|
|
125
123
|
``` irb
|
126
124
|
>> light.run
|
127
125
|
=> 3.142857142857143
|
128
|
-
>> light.
|
129
|
-
=>
|
126
|
+
>> light.color
|
127
|
+
=> "green"
|
130
128
|
```
|
131
129
|
|
132
130
|
If everything goes well, you shouldn't even be able to tell that you're using a
|
@@ -147,12 +145,12 @@ ZeroDivisionError: divided by 0
|
|
147
145
|
>> light.run
|
148
146
|
ZeroDivisionError: divided by 0
|
149
147
|
>> light.run
|
148
|
+
Switching example-2 from green to red because ZeroDivisionError divided by 0
|
150
149
|
ZeroDivisionError: divided by 0
|
151
150
|
>> light.run
|
152
|
-
Switching example-2 from green to red.
|
153
151
|
Stoplight::Error::RedLight: example-2
|
154
|
-
>> light.
|
155
|
-
=>
|
152
|
+
>> light.color
|
153
|
+
=> "red"
|
156
154
|
```
|
157
155
|
|
158
156
|
When the stoplight changes from green to red, it will notify every configured
|
@@ -165,7 +163,7 @@ these are handled elsewhere in your stack and don't represent real failures. A
|
|
165
163
|
good example is `ActiveRecord::RecordNotFound`.
|
166
164
|
|
167
165
|
``` irb
|
168
|
-
>> light = Stoplight::Light.new('example-
|
166
|
+
>> light = Stoplight::Light.new('example-3') { User.find(123) }.
|
169
167
|
.. with_allowed_errors([ActiveRecord::RecordNotFound])
|
170
168
|
=> #<Stoplight::Light:...>
|
171
169
|
>> light.run
|
@@ -174,28 +172,34 @@ ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
|
174
172
|
ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
175
173
|
>> light.run
|
176
174
|
ActiveRecord::RecordNotFound: Couldn't find User with ID=123
|
177
|
-
>> light.
|
178
|
-
=>
|
175
|
+
>> light.color
|
176
|
+
=> "green"
|
179
177
|
```
|
180
178
|
|
181
179
|
### Custom fallback
|
182
180
|
|
183
|
-
|
184
|
-
|
185
|
-
|
181
|
+
By default, stoplights will re-raise errors when they're green. When they're
|
182
|
+
red, they'll raise a `Stoplight::Error::RedLight` error. You can provide a
|
183
|
+
fallback that will be called in both of these cases. It will be passed the error
|
184
|
+
if the light was green.
|
186
185
|
|
187
186
|
``` irb
|
188
|
-
>> light = Stoplight::Light.new('example-
|
189
|
-
.. with_fallback {
|
190
|
-
=> #<Stoplight::Light
|
187
|
+
>> light = Stoplight::Light.new('example-4') { 1 / 0 }.
|
188
|
+
.. with_fallback { |e| p e; 'default' }
|
189
|
+
=> #<Stoplight::Light:..>
|
191
190
|
>> light.run
|
192
|
-
|
191
|
+
#<ZeroDivisionError: divided by 0>
|
192
|
+
=> "default"
|
193
193
|
>> light.run
|
194
|
-
|
194
|
+
#<ZeroDivisionError: divided by 0>
|
195
|
+
=> "default"
|
195
196
|
>> light.run
|
196
|
-
|
197
|
+
Switching example-4 from green to red because ZeroDivisionError divided by 0
|
198
|
+
#<ZeroDivisionError: divided by 0>
|
199
|
+
=> "default"
|
197
200
|
>> light.run
|
198
|
-
|
201
|
+
nil
|
202
|
+
=> "default"
|
199
203
|
```
|
200
204
|
|
201
205
|
### Custom threshold
|
@@ -204,13 +208,14 @@ Some bits of code might be allowed to fail more or less frequently than others.
|
|
204
208
|
You can configure this by setting a custom threshold in seconds.
|
205
209
|
|
206
210
|
``` irb
|
207
|
-
>> light = Stoplight::Light.new('example-
|
211
|
+
>> light = Stoplight::Light.new('example-5') { fail }.
|
208
212
|
.. with_threshold(1)
|
209
213
|
=> #<Stoplight::Light:...>
|
210
214
|
>> light.run
|
215
|
+
Switching example-5 from green to red because RuntimeError
|
211
216
|
RuntimeError:
|
212
217
|
>> light.run
|
213
|
-
Stoplight::Error::RedLight: example-
|
218
|
+
Stoplight::Error::RedLight: example-5
|
214
219
|
```
|
215
220
|
|
216
221
|
### Custom timeout
|
@@ -220,7 +225,7 @@ A light in the red state for longer than the timeout will transition to the
|
|
220
225
|
yellow state. This timeout is customizable.
|
221
226
|
|
222
227
|
``` irb
|
223
|
-
>> light = Stoplight::Light.new('example-
|
228
|
+
>> light = Stoplight::Light.new('example-6') { fail }.
|
224
229
|
.. with_timeout(1)
|
225
230
|
=> #<Stoplight::Light:...>
|
226
231
|
>> light.run
|
@@ -228,14 +233,12 @@ RuntimeError:
|
|
228
233
|
>> light.run
|
229
234
|
RuntimeError:
|
230
235
|
>> light.run
|
236
|
+
Switching example-6 from green to red because RuntimeError
|
231
237
|
RuntimeError:
|
232
|
-
>> light.run
|
233
|
-
Switching example-7 from green to red.
|
234
|
-
Stoplight::Error::RedLight: example-7
|
235
238
|
>> sleep(1)
|
236
239
|
=> 1
|
237
|
-
>> light.
|
238
|
-
=>
|
240
|
+
>> light.color
|
241
|
+
=> "yellow"
|
239
242
|
>> light.run
|
240
243
|
RuntimeError:
|
241
244
|
```
|
@@ -254,7 +257,10 @@ class ApplicationController < ActionController::Base
|
|
254
257
|
def stoplight(&block)
|
255
258
|
Stoplight::Light.new("#{params[:controller]}##{params[:action]}", &block)
|
256
259
|
.with_allowed_errors([ActiveRecord::RecordNotFound])
|
257
|
-
.with_fallback
|
260
|
+
.with_fallback do |error|
|
261
|
+
Rails.logger.error(error)
|
262
|
+
render(nothing: true, status: :service_unavailable)
|
263
|
+
end
|
258
264
|
.run
|
259
265
|
end
|
260
266
|
end
|
@@ -269,16 +275,14 @@ override the default behavior. You can lock a light in either the green or red
|
|
269
275
|
state using `set_state`.
|
270
276
|
|
271
277
|
``` irb
|
272
|
-
>> light = Stoplight::Light.new('example-
|
273
|
-
=> #<Stoplight::Light
|
278
|
+
>> light = Stoplight::Light.new('example-7') { true }
|
279
|
+
=> #<Stoplight::Light:..>
|
274
280
|
>> light.run
|
275
281
|
=> true
|
276
|
-
>>
|
277
|
-
.. light.name, Stoplight::DataStore::STATE_LOCKED_RED)
|
282
|
+
>> light.data_store.set_state(light, Stoplight::State::LOCKED_RED)
|
278
283
|
=> "locked_red"
|
279
284
|
>> light.run
|
280
|
-
|
281
|
-
Stoplight::Error::RedLight: example-8
|
285
|
+
Stoplight::Error::RedLight: example-7
|
282
286
|
```
|
283
287
|
|
284
288
|
**Code in locked red lights may still run under certain conditions!** If you
|
@@ -288,13 +292,13 @@ locked state of any stoplights.
|
|
288
292
|
|
289
293
|
## Credits
|
290
294
|
|
291
|
-
Stoplight is brought to you by [@camdez][
|
292
|
-
[@OrgSync][
|
295
|
+
Stoplight is brought to you by [@camdez][15] and [@tfausak][16] from
|
296
|
+
[@OrgSync][17]. We were inspired by Martin Fowler's [CircuitBreaker][18]
|
293
297
|
article.
|
294
298
|
|
295
299
|
If this gem isn't cutting it for you, there are a few alternatives, including:
|
296
|
-
[circuit_b][
|
297
|
-
[ya_circuit_breaker][
|
300
|
+
[circuit_b][19], [circuit_breaker][20], [simple_circuit_breaker][21], and
|
301
|
+
[ya_circuit_breaker][22].
|
298
302
|
|
299
303
|
[1]: https://github.com/orgsync/stoplight
|
300
304
|
[2]: https://badge.fury.io/rb/stoplight.svg
|
@@ -308,14 +312,13 @@ If this gem isn't cutting it for you, there are a few alternatives, including:
|
|
308
312
|
[10]: https://gemnasium.com/orgsync/stoplight.svg
|
309
313
|
[11]: https://gemnasium.com/orgsync/stoplight
|
310
314
|
[12]: https://github.com/orgsync/stoplight-admin
|
311
|
-
[13]:
|
312
|
-
[14]: https://rubygems.org/gems/
|
313
|
-
[15]: https://
|
314
|
-
[16]: https://github.com/
|
315
|
-
[17]: https://github.com/
|
316
|
-
[18]:
|
317
|
-
[19]:
|
318
|
-
[20]: https://github.com/
|
319
|
-
[21]: https://github.com/
|
320
|
-
[22]: https://github.com/
|
321
|
-
[23]: https://github.com/wooga/circuit_breaker
|
315
|
+
[13]: https://rubygems.org/gems/redis
|
316
|
+
[14]: https://rubygems.org/gems/hipchat
|
317
|
+
[15]: https://github.com/camdez
|
318
|
+
[16]: https://github.com/tfausak
|
319
|
+
[17]: https://github.com/OrgSync
|
320
|
+
[18]: http://martinfowler.com/bliki/CircuitBreaker.html
|
321
|
+
[19]: https://github.com/alg/circuit_b
|
322
|
+
[20]: https://github.com/wsargent/circuit_breaker
|
323
|
+
[21]: https://github.com/soundcloud/simple_circuit_breaker
|
324
|
+
[22]: https://github.com/wooga/circuit_breaker
|
data/lib/stoplight.rb
CHANGED
@@ -1,29 +1,24 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
+
require 'stoplight/color'
|
4
|
+
require 'stoplight/error'
|
5
|
+
require 'stoplight/failure'
|
6
|
+
require 'stoplight/state'
|
7
|
+
|
3
8
|
require 'stoplight/data_store'
|
4
9
|
require 'stoplight/data_store/base'
|
5
10
|
require 'stoplight/data_store/memory'
|
6
11
|
require 'stoplight/data_store/redis'
|
7
|
-
|
8
|
-
require 'stoplight/failure'
|
9
|
-
require 'stoplight/light'
|
12
|
+
|
10
13
|
require 'stoplight/notifier'
|
11
14
|
require 'stoplight/notifier/base'
|
12
15
|
require 'stoplight/notifier/hip_chat'
|
13
16
|
require 'stoplight/notifier/io'
|
14
17
|
|
15
|
-
|
16
|
-
# @return [Gem::Version]
|
17
|
-
VERSION = Gem::Version.new('0.4.1')
|
18
|
-
|
19
|
-
@data_store = DataStore::Memory.new
|
20
|
-
@notifiers = [Notifier::IO.new($stderr)]
|
18
|
+
require 'stoplight/default'
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
attr_accessor :data_store
|
20
|
+
require 'stoplight/light/runnable'
|
21
|
+
require 'stoplight/light'
|
25
22
|
|
26
|
-
|
27
|
-
attr_accessor :notifiers
|
28
|
-
end
|
23
|
+
module Stoplight
|
29
24
|
end
|
data/lib/stoplight/data_store.rb
CHANGED
@@ -2,151 +2,5 @@
|
|
2
2
|
|
3
3
|
module Stoplight
|
4
4
|
module DataStore
|
5
|
-
KEY_PREFIX = 'stoplight'.freeze
|
6
|
-
|
7
|
-
COLOR_GREEN = 'green'.freeze
|
8
|
-
COLOR_YELLOW = 'yellow'.freeze
|
9
|
-
COLOR_RED = 'red'.freeze
|
10
|
-
COLORS = Set.new([
|
11
|
-
COLOR_GREEN,
|
12
|
-
COLOR_YELLOW,
|
13
|
-
COLOR_RED
|
14
|
-
]).freeze
|
15
|
-
|
16
|
-
STATE_UNLOCKED = 'unlocked'.freeze
|
17
|
-
STATE_LOCKED_GREEN = 'locked_green'.freeze
|
18
|
-
STATE_LOCKED_RED = 'locked_red'.freeze
|
19
|
-
STATES = Set.new([
|
20
|
-
STATE_UNLOCKED,
|
21
|
-
STATE_LOCKED_GREEN,
|
22
|
-
STATE_LOCKED_RED
|
23
|
-
]).freeze
|
24
|
-
|
25
|
-
DEFAULT_ATTEMPTS = 0
|
26
|
-
DEFAULT_FAILURES = []
|
27
|
-
DEFAULT_STATE = STATE_UNLOCKED
|
28
|
-
DEFAULT_THRESHOLD = 3
|
29
|
-
DEFAULT_TIMEOUT = 60
|
30
|
-
|
31
|
-
module_function
|
32
|
-
|
33
|
-
# @group Colors
|
34
|
-
|
35
|
-
# @param state [String]
|
36
|
-
# @param threshold [Integer]
|
37
|
-
# @param failures [Array<Failure>]
|
38
|
-
# @param timeout [Integer]
|
39
|
-
# @return [String]
|
40
|
-
def colorize(state, threshold, failures, timeout)
|
41
|
-
case
|
42
|
-
when state == STATE_LOCKED_GREEN then COLOR_GREEN
|
43
|
-
when state == STATE_LOCKED_RED then COLOR_RED
|
44
|
-
when failures.size < threshold then COLOR_GREEN
|
45
|
-
when Time.now - failures.last.time > timeout then COLOR_YELLOW
|
46
|
-
else COLOR_RED
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# @group Validation
|
51
|
-
|
52
|
-
# @param color [String]
|
53
|
-
# @raise [ArgumentError]
|
54
|
-
def validate_color!(color)
|
55
|
-
return if valid_color?(color)
|
56
|
-
fail Error::InvalidColor, color.inspect
|
57
|
-
end
|
58
|
-
|
59
|
-
# @param color [String]
|
60
|
-
# @return [Boolean]
|
61
|
-
def valid_color?(color)
|
62
|
-
COLORS.include?(color)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param failure [Failure]
|
66
|
-
# @raise [ArgumentError]
|
67
|
-
def validate_failure!(failure)
|
68
|
-
return if valid_failure?(failure)
|
69
|
-
fail Error::InvalidFailure, failure.inspect
|
70
|
-
end
|
71
|
-
|
72
|
-
# @param failure [Failure]
|
73
|
-
# @return [Boolean]
|
74
|
-
def valid_failure?(failure)
|
75
|
-
failure.is_a?(Failure)
|
76
|
-
end
|
77
|
-
|
78
|
-
# @param state [String]
|
79
|
-
# @raise [ArgumentError]
|
80
|
-
def validate_state!(state)
|
81
|
-
return if valid_state?(state)
|
82
|
-
fail Error::InvalidState, state.inspect
|
83
|
-
end
|
84
|
-
|
85
|
-
# @param state [String]
|
86
|
-
# @return [Boolean]
|
87
|
-
def valid_state?(state)
|
88
|
-
STATES.include?(state)
|
89
|
-
end
|
90
|
-
|
91
|
-
# @param threshold [Integer]
|
92
|
-
# @raise [ArgumentError]
|
93
|
-
def validate_threshold!(threshold)
|
94
|
-
return if valid_threshold?(threshold)
|
95
|
-
fail Error::InvalidThreshold, threshold.inspect
|
96
|
-
end
|
97
|
-
|
98
|
-
# @param threshold [Integer]
|
99
|
-
# @return [Boolean]
|
100
|
-
def valid_threshold?(threshold)
|
101
|
-
threshold.is_a?(Integer) && threshold > 0
|
102
|
-
end
|
103
|
-
|
104
|
-
# @param timeout [Integer]
|
105
|
-
# @raise [ArgumentError]
|
106
|
-
def validate_timeout!(timeout)
|
107
|
-
return if valid_timeout?(timeout)
|
108
|
-
fail Error::InvalidTimeout, timeout.inspect
|
109
|
-
end
|
110
|
-
|
111
|
-
# @param timeout [Integer]
|
112
|
-
# @return [Boolean]
|
113
|
-
def valid_timeout?(timeout)
|
114
|
-
timeout.is_a?(Integer)
|
115
|
-
end
|
116
|
-
|
117
|
-
# @group Keys
|
118
|
-
|
119
|
-
# @return (see #key)
|
120
|
-
def attempts_key
|
121
|
-
key('attempts')
|
122
|
-
end
|
123
|
-
|
124
|
-
# @param name [String]
|
125
|
-
# @return (see #key)
|
126
|
-
def failures_key(name)
|
127
|
-
key('failures', name)
|
128
|
-
end
|
129
|
-
|
130
|
-
# @return (see #key)
|
131
|
-
def states_key
|
132
|
-
key('states')
|
133
|
-
end
|
134
|
-
|
135
|
-
# @return (see #key)
|
136
|
-
def thresholds_key
|
137
|
-
key('thresholds')
|
138
|
-
end
|
139
|
-
|
140
|
-
# @return (see #key)
|
141
|
-
def timeouts_key
|
142
|
-
key('timeouts')
|
143
|
-
end
|
144
|
-
|
145
|
-
# @param slug [String]
|
146
|
-
# @param suffix [String, nil]
|
147
|
-
# @return [String]
|
148
|
-
def key(slug, suffix = nil)
|
149
|
-
[KEY_PREFIX, slug, suffix].compact.join(':')
|
150
|
-
end
|
151
5
|
end
|
152
6
|
end
|