stoplight 3.0.0 → 4.0.0

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