stoplight 3.0.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +176 -180
  3. data/lib/stoplight/builder.rb +68 -0
  4. data/lib/stoplight/circuit_breaker.rb +92 -0
  5. data/lib/stoplight/configurable.rb +95 -0
  6. data/lib/stoplight/configuration.rb +126 -0
  7. data/lib/stoplight/data_store/memory.rb +20 -5
  8. data/lib/stoplight/data_store/redis.rb +37 -5
  9. data/lib/stoplight/default.rb +2 -0
  10. data/lib/stoplight/error.rb +1 -0
  11. data/lib/stoplight/light/deprecated.rb +44 -0
  12. data/lib/stoplight/light/lockable.rb +45 -0
  13. data/lib/stoplight/light/runnable.rb +25 -24
  14. data/lib/stoplight/light.rb +69 -63
  15. data/lib/stoplight/rspec/generic_notifier.rb +42 -0
  16. data/lib/stoplight/rspec.rb +3 -0
  17. data/lib/stoplight/version.rb +1 -1
  18. data/lib/stoplight.rb +32 -8
  19. data/spec/spec_helper.rb +7 -0
  20. data/spec/stoplight/builder_spec.rb +165 -0
  21. data/spec/stoplight/circuit_breaker_spec.rb +35 -0
  22. data/spec/stoplight/configurable_spec.rb +25 -0
  23. data/spec/stoplight/data_store/memory_spec.rb +12 -149
  24. data/spec/stoplight/data_store/redis_spec.rb +26 -158
  25. data/spec/stoplight/error_spec.rb +10 -0
  26. data/spec/stoplight/light/lockable_spec.rb +93 -0
  27. data/spec/stoplight/light/runnable_spec.rb +12 -273
  28. data/spec/stoplight/light_spec.rb +4 -28
  29. data/spec/stoplight/notifier/generic_spec.rb +35 -35
  30. data/spec/stoplight/notifier/io_spec.rb +1 -0
  31. data/spec/stoplight/notifier/logger_spec.rb +3 -0
  32. data/spec/stoplight_spec.rb +17 -6
  33. data/spec/support/configurable.rb +69 -0
  34. data/spec/support/data_store/base/clear_failures.rb +18 -0
  35. data/spec/support/data_store/base/clear_state.rb +20 -0
  36. data/spec/support/data_store/base/get_all.rb +44 -0
  37. data/spec/support/data_store/base/get_failures.rb +30 -0
  38. data/spec/support/data_store/base/get_state.rb +7 -0
  39. data/spec/support/data_store/base/names.rb +29 -0
  40. data/spec/support/data_store/base/record_failures.rb +70 -0
  41. data/spec/support/data_store/base/set_state.rb +15 -0
  42. data/spec/support/data_store/base/with_notification_lock.rb +27 -0
  43. data/spec/support/data_store/base.rb +21 -0
  44. data/spec/support/database_cleaner.rb +26 -0
  45. data/spec/support/exception_helpers.rb +9 -0
  46. data/spec/support/light/runnable/color.rb +79 -0
  47. data/spec/support/light/runnable/run.rb +247 -0
  48. data/spec/support/light/runnable.rb +4 -0
  49. metadata +51 -231
  50. data/lib/stoplight/notifier/bugsnag.rb +0 -37
  51. data/lib/stoplight/notifier/honeybadger.rb +0 -44
  52. data/lib/stoplight/notifier/pagerduty.rb +0 -21
  53. data/lib/stoplight/notifier/raven.rb +0 -40
  54. data/lib/stoplight/notifier/rollbar.rb +0 -39
  55. data/lib/stoplight/notifier/slack.rb +0 -21
  56. data/spec/stoplight/notifier/bugsnag_spec.rb +0 -90
  57. data/spec/stoplight/notifier/honeybadger_spec.rb +0 -88
  58. data/spec/stoplight/notifier/pagerduty_spec.rb +0 -40
  59. data/spec/stoplight/notifier/raven_spec.rb +0 -90
  60. data/spec/stoplight/notifier/rollbar_spec.rb +0 -90
  61. data/spec/stoplight/notifier/slack_spec.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbfed26c7b63f56e88cd970d5db820022a89d689570cd4e98168b7226d718791
4
- data.tar.gz: 9206b2c2a66e78d39f37e7932fc95faaf850a2cc54d024eab2a2d90dc14396b4
3
+ metadata.gz: '0194283983ed6568bca930b6640a7d397dd06e779d921e8bd169cb6a8e743448'
4
+ data.tar.gz: 9b68b81b05c15cbed7885d8f10a042c0e0ecacafacb0fe6e621e3c124007d74e
5
5
  SHA512:
6
- metadata.gz: 34d477b3b8e0c6e9b34a0a1ea7e0e6fab4950601d914407063a113f019b9e4a7420b4b291f76702e326b41f3b7cd780adb55f9c2df28bd659c9be7d384af35b4
7
- data.tar.gz: ab49c8f6cac59358b78807fa82e88e8ee3cec5c668dad2f6cc0ca8183507dc64373f611c23b83dce4d2d5958f176d6c447e796a9137e87a165b0cabd12b7dc7c
6
+ metadata.gz: 64c7f5d19bb00b46f9908d938a120c475f1fd60e481e58400c49360dae260bd6c83690e61f34362a20dea0b3b955f4f9b9d76030b8ccbb7c788fd7a531ad395b
7
+ data.tar.gz: e48e470e7f72d811852df5576b132a11f6f69dedf711cf371c9cfbc4dc1a4600f217b147ba53c9313a8863a06aaef88e279d3cd5f7e8ff51bd69c731123b1093
data/README.md CHANGED
@@ -9,6 +9,8 @@ Stoplight is traffic control for code. It's an implementation of the circuit
9
9
  breaker pattern in Ruby.
10
10
 
11
11
  ---
12
+ :warning: You're browsing the documentation of the not realized version. You can find the documentation
13
+ for the most recent version 3.0.1 [here](https://github.com/bolshakov/stoplight/tree/v3.0.1).
12
14
 
13
15
  Does your code use unreliable systems, like a flaky database or a spotty web
14
16
  service? Wrap calls to those up in stoplights to prevent them from affecting
@@ -17,97 +19,88 @@ the rest of your application.
17
19
  Check out [stoplight-admin][] for controlling your stoplights.
18
20
 
19
21
  - [Installation](#installation)
20
- - [Basic usage](#basic-usage)
21
- - [Custom errors](#custom-errors)
22
- - [Custom fallback](#custom-fallback)
23
- - [Custom threshold](#custom-threshold)
24
- - [Custom cool off time](#custom-cool-off-time)
22
+ - [Basic Usage](#basic-usage)
23
+ - [Custom Errors](#custom-errors)
24
+ - [Custom Fallback](#custom-fallback)
25
+ - [Custom Threshold](#custom-threshold)
26
+ - [Custom Window Size](#custom-window-size)
27
+ - [Custom Cool Off Time](#custom-cool-off-time)
25
28
  - [Rails](#rails)
26
29
  - [Setup](#setup)
27
- - [Data store](#data-store)
30
+ - [Data Store](#data-store)
28
31
  - [Redis](#redis)
29
32
  - [Notifiers](#notifiers)
30
- - [Bugsnag](#bugsnag)
31
- - [Honeybadger](#honeybadger)
33
+ - [IO](#io)
32
34
  - [Logger](#logger)
33
- - [Pagerduty](#pagerduty)
34
- - [Rollbar](#rollbar)
35
- - [Sentry](#sentry)
36
- - [Slack](#slack)
35
+ - [Community-supported Notifiers](#community-supported-notifiers)
36
+ - [How to Implement Your Own Notifier?](#how-to-implement-your-own-notifier)
37
37
  - [Rails](#rails-1)
38
- - [Advanced usage](#advanced-usage)
38
+ - [Advanced Usage](#advanced-usage)
39
39
  - [Locking](#locking)
40
40
  - [Testing](#testing)
41
+ - [Maintenance Policy][#maintenance-policy]
41
42
  - [Credits](#credits)
42
43
 
43
44
  ## Installation
44
45
 
45
46
  Add it to your Gemfile:
46
47
 
47
- ``` rb
48
+ ```ruby
48
49
  gem 'stoplight'
49
50
  ```
50
51
 
51
52
  Or install it manually:
52
53
 
53
- ``` sh
54
+ ```sh
54
55
  $ gem install stoplight
55
56
  ```
56
57
 
57
58
  Stoplight uses [Semantic Versioning][]. Check out [the change log][] for a
58
59
  detailed list of changes.
59
60
 
60
- Stoplight works with all supported versions of Ruby (2.1 through 2.3).
61
-
62
- ## Basic usage
61
+ ## Basic Usage
63
62
 
64
63
  To get started, create a stoplight:
65
64
 
66
- ``` rb
67
- light = Stoplight('example-pi') { 22.0 / 7 }
68
- # => #<Stoplight::Light:...>
65
+ ```ruby
66
+ light = Stoplight('example-pi')
69
67
  ```
70
68
 
71
- Then you can run it and it will return the result of calling the block. This is
72
- the green state. (The green state corresponds to the closed state for circuit
73
- breakers.)
69
+ Then you can run it with a block of code and it will return the result of calling the block. This is
70
+ the green state. (The green state corresponds to the closed state for circuit breakers.)
74
71
 
75
- ``` rb
76
- light.run
72
+ ```ruby
73
+ light.run { 22.0 / 7 }
77
74
  # => 3.142857142857143
78
75
  light.color
79
76
  # => "green"
80
77
  ```
81
78
 
82
79
  If everything goes well, you shouldn't even be able to tell that you're using a
83
- stoplight. That's not very interesting though, so let's create a failing
84
- stoplight:
80
+ stoplight. That's not very interesting though, so let's make stoplight fail.
85
81
 
86
- ``` rb
87
- light = Stoplight('example-zero') { 1 / 0 }
88
- # => #<Stoplight::Light:...>
89
- ```
90
-
91
- Now when you run it, the error will be recorded and passed through. After
82
+ When you run it, the error will be recorded and passed through. After
92
83
  running it a few times, the stoplight will stop trying and fail fast. This is
93
84
  the red state. (The red state corresponds to the open state for circuit
94
- breakers.)
85
+ breakers.)
95
86
 
96
- ``` rb
97
- light.run
87
+ ```ruby
88
+ light = Stoplight('example-zero')
89
+ # => #<Stoplight::CircuitBreaker:...>
90
+ light.run { 1 / 0 }
98
91
  # ZeroDivisionError: divided by 0
99
- light.run
92
+ light.run { 1 / 0 }
100
93
  # ZeroDivisionError: divided by 0
101
- light.run
94
+ light.run { 1 / 0 }
102
95
  # Switching example-zero from green to red because ZeroDivisionError divided by 0
103
96
  # ZeroDivisionError: divided by 0
104
- light.run
97
+ light.run { 1 / 0 }
105
98
  # Stoplight::Error::RedLight: example-zero
106
99
  light.color
107
100
  # => "red"
108
101
  ```
109
102
 
110
- When the stoplight changes from green to red, it will notify every configured
103
+ When the Stoplight changes from green to red, it will notify every configured
111
104
  notifier. See [the notifiers section][] to learn more about notifiers.
112
105
 
113
106
  The stoplight will move into the yellow state after being in the red state for
@@ -117,7 +110,7 @@ check out [the cool off time section][] When stoplights are yellow, they will
117
110
  try to run their code. If it fails, they'll switch back to red. If it succeeds,
118
111
  they'll switch to green.
119
112
 
120
- ### Custom errors
113
+ ### Custom Errors
121
114
 
122
115
  Some errors shouldn't cause your stoplight to move into the red state. Usually
123
116
  these are handled elsewhere in your stack and don't represent real failures. A
@@ -140,89 +133,126 @@ provide a custom block that will be called with the error and a handler
140
133
  never ignore the error. That means a `NoMemoryError` could change the color
141
134
  of your stoplights.
142
135
 
143
- ``` rb
144
- light = Stoplight('example-not-found') { User.find(123) }
136
+ ```ruby
137
+ light = Stoplight('example-not-found')
145
138
  .with_error_handler do |error, handle|
146
- raise error if error.is_a?(ActiveRecord::RecordNotFound)
147
- handle.call(error)
139
+ if error.is_a?(ActiveRecord::RecordNotFound)
140
+ raise error
141
+ else
142
+ handle.call(error)
143
+ end
148
144
  end
149
- # => #<Stoplight::Light:...>
150
- light.run
145
+ # => #<Stoplight::CircuitBreaker:...>
146
+ light.run { User.find(123) }
151
147
  # ActiveRecord::RecordNotFound: Couldn't find User with ID=123
152
- light.run
148
+ light.run { User.find(123) }
153
149
  # ActiveRecord::RecordNotFound: Couldn't find User with ID=123
154
- light.run
150
+ light.run { User.find(123) }
155
151
  # ActiveRecord::RecordNotFound: Couldn't find User with ID=123
156
152
  light.color
157
153
  # => "green"
158
154
  ```
159
155
 
160
- ### Custom fallback
156
+ ### Custom Fallback
161
157
 
162
158
  By default, stoplights will re-raise errors when they're green. When they're
163
159
  red, they'll raise a `Stoplight::Error::RedLight` error. You can provide a
164
160
  fallback that will be called in both of these cases. It will be passed the
165
161
  error if the light was green.
166
162
 
167
- ``` rb
168
- light = Stoplight('example-fallback') { 1 / 0 }
163
+ ```ruby
164
+ light = Stoplight('example-fallback')
169
165
  .with_fallback { |e| p e; 'default' }
170
- # => #<Stoplight::Light:..>
171
- light.run
166
+ # => #<Stoplight::CircuitBreaker:..>
167
+ light.run { 1 / 0 }
172
168
  # #<ZeroDivisionError: divided by 0>
173
169
  # => "default"
174
- light.run
170
+ light.run { 1 / 0 }
175
171
  # #<ZeroDivisionError: divided by 0>
176
172
  # => "default"
177
- light.run
173
+ light.run { 1 / 0 }
178
174
  # Switching example-fallback from green to red because ZeroDivisionError divided by 0
179
175
  # #<ZeroDivisionError: divided by 0>
180
176
  # => "default"
181
- light.run
177
+ light.run { 1 / 0 }
182
178
  # nil
183
179
  # => "default"
184
180
  ```
185
181
 
186
- ### Custom threshold
182
+ ### Custom Threshold
187
183
 
188
184
  Some bits of code might be allowed to fail more or less frequently than others.
189
185
  You can configure this by setting a custom threshold.
190
186
 
191
- ``` rb
192
- light = Stoplight('example-threshold') { fail }
187
+ ```ruby
188
+ light = Stoplight('example-threshold')
193
189
  .with_threshold(1)
194
- # => #<Stoplight::Light:...>
195
- light.run
190
+ # => #<Stoplight::CircuitBreaker:...>
191
+ light.run { fail }
196
192
  # Switching example-threshold from green to red because RuntimeError
197
193
  # RuntimeError:
198
- light.run
194
+ light.run { fail }
199
195
  # Stoplight::Error::RedLight: example-threshold
200
196
  ```
201
197
 
202
198
  The default threshold is `3`.
203
199
 
204
- ### Custom cool off time
200
+ ### Custom Window Size
201
+
202
+ By default, all recorded failures, regardless of the time these happen, will count to reach
203
+ the threshold (hence turning the light to red). If needed, a window size can be set,
204
+ meaning you can control how many errors per period of time will count to reach the red
205
+ state.
206
+
207
+ By default, every recorded failure contributes to reaching the threshold, regardless of when it occurs,
208
+ causing the stoplight to turn red. By configuring a custom window size, you control how errors are
209
+ counted within a specified time frame. Here's how it works:
210
+
211
+ Let's say you set the window size to 2 seconds:
212
+
213
+ ```ruby
214
+ window_size_in_seconds = 2
215
+
216
+ light = Stoplight('example-threshold')
217
+ .with_window_size(window_size_in_seconds)
218
+ .with_threshold(1) #=> #<Stoplight::CircuitBreaker:...>
219
+
220
+ light.run { 1 / 0 } #=> #<ZeroDivisionError: divided by 0>
221
+ sleep(3)
222
+ light.run { 1 / 0 }
223
+ ```
224
+
225
+ Without the window size configuration, the second `light.run { 1 / 0 }` call will result in a
226
+ `Stoplight::Error::RedLight` exception being raised, as the stoplight transitions to the red state
227
+ after the first call. With a sliding window of 2 seconds, only the errors that occur within the latest
228
+ 2 seconds are considered. The first error causes the stoplight to turn red, but after 3 seconds
229
+ (when the second error occurs), the window has shifted, and the stoplight switches to green state
230
+ causing the error to raise again. This provides a way to focus on the most recent errors.
231
+
232
+ The default window size is infinity, so all failures counts.
233
+
234
+ ### Custom Cool Off Time
205
235
 
206
236
  Stoplights will automatically attempt to recover after a certain amount of
207
237
  time. A light in the red state for longer than the cool off period will
208
238
  transition to the yellow state. This cool off time is customizable.
209
239
 
210
- ``` rb
211
- light = Stoplight('example-cool-off') { fail }
240
+ ```ruby
241
+ light = Stoplight('example-cool-off')
212
242
  .with_cool_off_time(1)
213
- # => #<Stoplight::Light:...>
214
- light.run
243
+ # => #<Stoplight::CircuitBreaker:...>
244
+ light.run { fail }
215
245
  # RuntimeError:
216
- light.run
246
+ light.run { fail }
217
247
  # RuntimeError:
218
- light.run
248
+ light.run { fail }
219
249
  # Switching example-cool-off from green to red because RuntimeError
220
250
  # RuntimeError:
221
251
  sleep(1)
222
252
  # => 1
223
253
  light.color
224
254
  # => "yellow"
225
- light.run
255
+ light.run { fail }
226
256
  # RuntimeError:
227
257
  ```
228
258
 
@@ -236,19 +266,19 @@ effectively replaces the red state with yellow.
236
266
  Stoplight was designed to wrap Rails actions with minimal effort. Here's an
237
267
  example configuration:
238
268
 
239
- ``` rb
269
+ ```ruby
240
270
  class ApplicationController < ActionController::Base
241
271
  around_action :stoplight
242
272
 
243
273
  private
244
274
 
245
275
  def stoplight(&block)
246
- Stoplight("#{params[:controller]}##{params[:action]}", &block)
276
+ Stoplight("#{params[:controller]}##{params[:action]}")
247
277
  .with_fallback do |error|
248
278
  Rails.logger.error(error)
249
279
  render(nothing: true, status: :service_unavailable)
250
280
  end
251
- .run
281
+ .run(&block)
252
282
  end
253
283
  end
254
284
  ```
@@ -259,10 +289,10 @@ end
259
289
 
260
290
  Stoplight uses an in-memory data store out of the box.
261
291
 
262
- ``` rb
292
+ ```ruby
263
293
  require 'stoplight'
264
294
  # => true
265
- Stoplight::Light.default_data_store
295
+ Stoplight.default_data_store
266
296
  # => #<Stoplight::DataStore::Memory:...>
267
297
  ```
268
298
 
@@ -271,17 +301,16 @@ the only supported persistent data store is Redis.
271
301
 
272
302
  #### Redis
273
303
 
274
- Make sure you have [the Redis gem][] (`~> 3.2`) installed before configuring
275
- Stoplight.
304
+ Make sure you have [the Redis gem][] installed before configuring Stoplight.
276
305
 
277
- ``` rb
306
+ ```ruby
278
307
  require 'redis'
279
308
  # => true
280
309
  redis = Redis.new
281
310
  # => #<Redis client ...>
282
311
  data_store = Stoplight::DataStore::Redis.new(redis)
283
312
  # => #<Stoplight::DataStore::Redis:...>
284
- Stoplight::Light.default_data_store = data_store
313
+ Stoplight.default_data_store = data_store
285
314
  # => #<Stoplight::DataStore::Redis:...>
286
315
  ```
287
316
 
@@ -290,38 +319,26 @@ Stoplight::Light.default_data_store = data_store
290
319
  Stoplight sends notifications to standard error by default.
291
320
 
292
321
  ``` rb
293
- Stoplight::Light.default_notifiers
322
+ Stoplight.default_notifiers
294
323
  # => [#<Stoplight::Notifier::IO:...>]
295
324
  ```
296
325
 
297
326
  If you want to send notifications elsewhere, you'll have to set them up.
298
327
 
299
- #### Bugsnag
300
-
301
- Make sure you have [the Bugsnag gem][] (`~> 4.0`) installed before configuring
302
- Stoplight.
303
-
304
- ``` rb
305
- require 'bugsnag'
306
- # => true
307
- notifier = Stoplight::Notifier::Bugsnag.new(Bugsnag)
308
- # => #<Stoplight::Notifier::Bugsnag:...>
309
- Stoplight::Light.default_notifiers += [notifier]
310
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Bugsnag:...>]
311
- ```
328
+ #### IO
312
329
 
313
- #### Honeybadger
330
+ Stoplight can notify not only into STDOUT, but into any IO object. You can configure
331
+ the `Stoplight::Notifier::IO` notifier for that.
314
332
 
315
- Make sure you have [the Honeybadger gem][] (`~> 2.5`) installed before
316
- configuring Stoplight.
333
+ ```ruby
334
+ require 'stringio'
317
335
 
318
- ``` rb
319
- require 'honeybadger'
320
- # => true
321
- notifier = Stoplight::Notifier::Honeybadger.new('api key')
322
- # => #<Stoplight::Notifier::Honeybadger:...>
323
- Stoplight::Light.default_notifiers += [notifier]
324
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Honeybadger:...>]
336
+ io = StringIO.new
337
+ # => #<StringIO:...>
338
+ notifier = Stoplight::Notifier::IO.new(io)
339
+ # => #<Stoplight::Notifier::IO:...>
340
+ Stoplight.default_notifiers += [notifier]
341
+ # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::IO:...>]
325
342
  ```
326
343
 
327
344
  #### Logger
@@ -329,77 +346,48 @@ Stoplight::Light.default_notifiers += [notifier]
329
346
  Stoplight can be configured to use [the Logger class][] from the standard
330
347
  library.
331
348
 
332
- ``` rb
349
+ ```ruby
333
350
  require 'logger'
334
351
  # => true
335
352
  logger = Logger.new(STDERR)
336
353
  # => #<Logger:...>
337
354
  notifier = Stoplight::Notifier::Logger.new(logger)
338
355
  # => #<Stoplight::Notifier::Logger:...>
339
- Stoplight::Light.default_notifiers += [notifier]
356
+ Stoplight.default_notifiers += [notifier]
340
357
  # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Logger:...>]
341
358
  ```
342
359
 
343
- #### Pagerduty
344
-
345
- Make sure you have [the Pagerduty gem][] (`~> 2.1`) installed before configuring
346
- Stoplight.
347
-
348
- ``` rb
349
- require 'pagerduty'
350
- # => true
351
- pagerduty = Pagerduty.new('http://www.example.com/webhook-url')
352
- # => #<Pagerduty:...>
353
- notifier = Stoplight::Notifier::Pagerduty.new(pagerduty)
354
- # => #<Stoplight::Notifier::Pagerduty:...>
355
- Stoplight::Light.default_notifiers += [notifier]
356
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Pagerduty:...>]
357
- ```
360
+ #### Community-supported Notifiers
358
361
 
359
- #### Rollbar
362
+ * [stoplight-sentry]
360
363
 
361
- Make sure you have [the Rollbar gem][] (`~> 2.0`) installed before configuring
362
- Stoplight.
364
+ You you want to implement your own notifier, the following section contains all the required information.
363
365
 
364
- ``` rb
365
- require 'rollbar'
366
- # => true
367
- notifier = Stoplight::Notifier::Rollbar.new(Rollbar)
368
- # => #<Stoplight::Notifier::Rollbar:...>
369
- Stoplight::Light.default_notifiers += [notifier]
370
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Rollbar:...>]
371
- ```
366
+ Pull requests to update this section are welcome.
372
367
 
373
- #### Sentry
368
+ #### How to implement your own notifier?
374
369
 
375
- Make sure you have [the Sentry gem][] (`~> 1.2`) installed before configuring
376
- Stoplight.
370
+ A notifier has to implement the `Stoplight::Notifier::Base` interface:
377
371
 
378
- ``` rb
379
- require 'sentry-raven'
380
- # => true
381
- configuration = Raven::Configuration.new
382
- # => #<Raven::Configuration:...>
383
- notifier = Stoplight::Notifier::Raven.new(configuration)
384
- # => #<Stoplight::Notifier::Raven:...>
385
- Stoplight::Light.default_notifiers += [notifier]
386
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Raven:...>]
372
+ ```ruby
373
+ def notify(light, from_color, to_color, error)
374
+ raise NotImplementedError
375
+ end
387
376
  ```
388
377
 
389
- #### Slack
378
+ For convenience, you can use the `Stoplight::Notifier::Generic` module. It takes care of
379
+ the message formatting, and you have to implement only the `put` method, which takes message sting as an argument:
390
380
 
391
- Make sure you have [the Slack gem][] (`~> 1.3`) installed before configuring
392
- Stoplight.
393
-
394
- ``` rb
395
- require 'slack-notifier'
396
- # => true
397
- slack = Slack::Notifier.new('http://www.example.com/webhook-url')
398
- # => #<Slack::Notifier:...>
399
- notifier = Stoplight::Notifier::Slack.new(slack)
400
- # => #<Stoplight::Notifier::Slack:...>
401
- Stoplight::Light.default_notifiers += [notifier]
402
- # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Slack:...>]
381
+ ```ruby
382
+ class IO < Stoplight::Notifier::Base
383
+ include Generic
384
+
385
+ private
386
+
387
+ def put(message)
388
+ @object.puts(message)
389
+ end
390
+ end
403
391
  ```
404
392
 
405
393
  ### Rails
@@ -409,11 +397,11 @@ in-memory data store, you don't need to do anything special. If you want to use
409
397
  a persistent data store, you'll need to configure it. Create an initializer for
410
398
  Stoplight:
411
399
 
412
- ``` rb
400
+ ```ruby
413
401
  # config/initializers/stoplight.rb
414
402
  require 'stoplight'
415
- Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(...)
416
- Stoplight::Light.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.logger)]
403
+ Stoplight.default_data_store = Stoplight::DataStore::Redis.new(...)
404
+ Stoplight.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.logger)]
417
405
  ```
418
406
 
419
407
  ## Advanced usage
@@ -421,17 +409,17 @@ Stoplight::Light.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.log
421
409
  ### Locking
422
410
 
423
411
  Although stoplights can operate on their own, occasionally you may want to
424
- override the default behavior. You can lock a light in either the green or red
425
- state using `set_state`.
412
+ override the default behavior. You can lock a light using `#lock(color)` method.
413
+ Color should be either `Stoplight::Color::GREEN` or ``Stoplight::Color::RED``.
426
414
 
427
- ``` rb
428
- light = Stoplight('example-locked') { true }
429
- # => #<Stoplight::Light:..>
430
- light.run
415
+ ```ruby
416
+ light = Stoplight('example-locked')
417
+ # => #<Stoplight::CircuitBreaker:..>
418
+ light.run { true }
431
419
  # => true
432
- light.data_store.set_state(light, Stoplight::State::LOCKED_RED)
433
- # => "locked_red"
434
- light.run
420
+ light.lock(Stoplight::Color::RED)
421
+ # => #<Stoplight::CircuitBreaker:..>
422
+ light.run { true }
435
423
  # Stoplight::Error::RedLight: example-locked
436
424
  ```
437
425
 
@@ -440,10 +428,11 @@ have configured a custom data store and that data store fails, Stoplight will
440
428
  switch over to using a blank in-memory data store. That means you will lose the
441
429
  locked state of any stoplights.
442
430
 
443
- You can go back to using the default behavior by unlocking the stoplight.
431
+ You can go back to using the default behavior by unlocking the stoplight using `#unlock`.
444
432
 
445
- ``` rb
446
- light.data_store.set_state(light, Stoplight::State::UNLOCKED)
433
+ ```ruby
434
+ light.unlock
435
+ # => #<Stoplight::CircuitBreaker:..>
447
436
  ```
448
437
 
449
438
  ### Testing
@@ -453,28 +442,34 @@ However there are a few things you can do to make them behave better. If your
453
442
  stoplights are spewing messages into your test output, you can silence them
454
443
  with a couple configuration changes.
455
444
 
456
- ``` rb
457
- Stoplight::Light.default_error_notifier = -> _ {}
458
- Stoplight::Light.default_notifiers = []
445
+ ```ruby
446
+ Stoplight.default_error_notifier = -> _ {}
447
+ Stoplight.default_notifiers = []
459
448
  ```
460
449
 
461
450
  If your tests mysteriously fail because stoplights are the wrong color, you can
462
451
  try resetting the data store before each test case. For example, this would
463
452
  give each test case a fresh data store with RSpec.
464
453
 
465
- ``` rb
454
+ ```ruby
466
455
  before(:each) do
467
- Stoplight::Light.default_data_store = Stoplight::DataStore::Memory.new
456
+ Stoplight.default_data_store = Stoplight::DataStore::Memory.new
468
457
  end
469
458
  ```
470
459
 
471
460
  Sometimes you may want to test stoplights directly. You can avoid resetting the
472
461
  data store by giving each stoplight a unique name.
473
462
 
474
- ``` rb
475
- stoplight = Stoplight("test-#{rand}") { ... }
463
+ ```ruby
464
+ stoplight = Stoplight("test-#{rand}")
476
465
  ```
477
466
 
467
+ ## Maintenance Policy
468
+
469
+ Stoplight supports the latest three minor versions of Ruby, which currently are: `3.0.x`, `3.1.x`, and `3.2.x`. Changing
470
+ the minimum supported Ruby version is not considered a breaking change.
471
+ We support the current stable Redis version (`7.2`) and the latest release of the previous major version (`6.2.9`)
472
+
478
473
  ## Credits
479
474
 
480
475
  Stoplight is brought to you by [@camdez][] and [@tfausak][] from [@OrgSync][]. [@bolshakov][] is the current
@@ -512,3 +507,4 @@ Stoplight is licensed under [the MIT License][].
512
507
  [complete list of contributors]: https://github.com/bolshakov/stoplight/graphs/contributors
513
508
  [CircuitBreaker]: http://martinfowler.com/bliki/CircuitBreaker.html
514
509
  [the MIT license]: LICENSE.md
510
+ [stoplight-sentry]: https://github.com/bolshakov/stoplight-sentry