stoplight 3.0.1 → 4.1.0

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