stoplight 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2da543aa5426e5354ed20f5e6d8547190597073d
4
- data.tar.gz: 70aa5cfae4dbeec33fbefcadff88098317ccf0cc
3
+ metadata.gz: d93190b30a8d042b53c5b2cf115a02dc4d2ca890
4
+ data.tar.gz: 0c4f3b4da7602df759e976eed9d73dfdb87fa7e5
5
5
  SHA512:
6
- metadata.gz: 7c022ff3500336be177976073695d9d197800e2ecdcfc9e1a498215f239b69e302b82c475f2ab8f7cfd6be508d20b9d9fd0c1170adfa70de1006de11751013c8
7
- data.tar.gz: 857d9ea2421f83e516f64e6319421100d0bda5992875f023909c474ef78d140a173570a28944abc5865fbf5c2b603a79ce9689acd21013f5fa3e0b6e1c6174a8
6
+ metadata.gz: efbc488c2ebea3bca686f190d14a06bf48b563b2d1c4d07fc30692901d5d72aa353dd93b2d422d0dac0affe01d3cd16d2ea293b767c70ead80a571810b787190
7
+ data.tar.gz: 93bd99e0422058fb537555e872baf50513672f9c0035eb6f717dae6157ae049c94ac8d810045a825b3d364dc0014a307f7705adbabcc36b6fb28b252b27ddf17
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Stoplight uses [Semantic Versioning][1].
4
4
 
5
+ ## v1.2.0 (2015-09-11)
6
+
7
+ - #78: Added a Bugsnag notifier. Thanks, @bolshakov!
8
+ - Added a Honeybadger notifier.
9
+ - #77: Added a logger notifier. Thanks again, @bolshakov!
10
+
5
11
  ## v1.1.1 (2015-07-16)
6
12
 
7
13
  - Introduced a generic notifier to reduce duplication between the IO and Slack
data/README.md CHANGED
@@ -12,9 +12,9 @@ breaker pattern in Ruby.
12
12
 
13
13
  ---
14
14
 
15
- Does your code use unreliable systems, like a flaky database or a
16
- spotty web service? Wrap those up with stoplights to prevent them
17
- from affecting the rest of your application.
15
+ Does your code use unreliable systems, like a flaky database or a spotty web
16
+ service? Wrap calls to those up in stoplights to prevent them from affecting
17
+ the rest of your application.
18
18
 
19
19
  Check out [stoplight-admin][] for controlling your stoplights.
20
20
 
@@ -27,8 +27,12 @@ Check out [stoplight-admin][] for controlling your stoplights.
27
27
  - [Rails](#rails)
28
28
  - [Setup](#setup)
29
29
  - [Data store](#data-store)
30
+ - [Redis](#redis)
30
31
  - [Notifiers](#notifiers)
32
+ - [Bugsnag](#bugsnag)
31
33
  - [HipChat](#hipchat)
34
+ - [Honeybadger](#honeybadger)
35
+ - [Logger](#logger)
32
36
  - [Slack](#slack)
33
37
  - [Rails](#rails-1)
34
38
  - [Advanced usage](#advanced-usage)
@@ -64,9 +68,9 @@ light = Stoplight('example-1') { 22.0 / 7 }
64
68
  # => #<Stoplight::Light:...>
65
69
  ```
66
70
 
67
- Then you can run it and it will return the result of calling the
68
- block. This is the green state. (The green state corresponds to the
69
- closed state for circuit breakers.)
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.)
70
74
 
71
75
  ``` rb
72
76
  light.run
@@ -75,19 +79,19 @@ light.color
75
79
  # => "green"
76
80
  ```
77
81
 
78
- If everything goes well, you shouldn't even be able to tell that
79
- you're using a stoplight. That's not very interesting though. Let's
80
- create a failing stoplight:
82
+ 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:
81
85
 
82
86
  ``` rb
83
87
  light = Stoplight('example-2') { 1 / 0 }
84
88
  # => #<Stoplight::Light:...>
85
89
  ```
86
90
 
87
- Now when you run it, the error will be recorded and passed through.
88
- After running it a few times, the stoplight will stop trying and
89
- fail fast. This is the red state. (The red state corresponds to the
90
- open state for circuit breakers.)
91
+ Now when you run it, the error will be recorded and passed through. After
92
+ running it a few times, the stoplight will stop trying and fail fast. This is
93
+ the red state. (The red state corresponds to the open state for circuit
94
+ breakers.)
91
95
 
92
96
  ``` rb
93
97
  light.run
@@ -103,23 +107,21 @@ light.color
103
107
  # => "red"
104
108
  ```
105
109
 
106
- When the stoplight changes from green to red, it will notify every
107
- configured notifier. See [the notifiers section][] to learn more
108
- about notifiers.
110
+ When the stoplight changes from green to red, it will notify every configured
111
+ notifier. See [the notifiers section][] to learn more about notifiers.
109
112
 
110
- The stoplight will move into the yellow state after being in the
111
- red state for a while. (The yellow state corresponds to the half
112
- open state for circuit breakers.) To configure how long it takes
113
- to switch into the yellow state, check out [the timeout section][]
114
- When stoplights are yellow, they'll try to run their code. If it
115
- fails, they'll switch back to red. If it succeeds, they'll switch
116
- to green.
113
+ The stoplight will move into the yellow state after being in the red state for
114
+ a while. (The yellow state corresponds to the half open state for circuit
115
+ breakers.) To configure how long it takes to switch into the yellow state,
116
+ check out [the timeout section][] When stoplights are yellow, they will try
117
+ to run their code. If it fails, they'll switch back to red. If it succeeds,
118
+ they'll switch to green.
117
119
 
118
120
  ### Custom errors
119
121
 
120
- Some errors shouldn't cause your stoplight to move into the red
121
- state. Usually these are handled elsewhere in your stack and don't
122
- represent real failures. A good example is `ActiveRecord::RecordNotFound`.
122
+ Some errors shouldn't cause your stoplight to move into the red state. Usually
123
+ these are handled elsewhere in your stack and don't represent real failures. A
124
+ good example is `ActiveRecord::RecordNotFound`.
123
125
 
124
126
  ``` rb
125
127
  light = Stoplight('example-3') { User.find(123) }
@@ -140,10 +142,10 @@ The following errors are always allowed: `NoMemoryError`, `ScriptError`,
140
142
 
141
143
  ### Custom fallback
142
144
 
143
- By default, stoplights will re-raise errors when they're green.
144
- When they're red, they'll raise a `Stoplight::Error::RedLight`
145
- error. You can provide a fallback that will be called in both of
146
- these cases. It will be passed the error if the light was green.
145
+ By default, stoplights will re-raise errors when they're green. When they're
146
+ red, they'll raise a `Stoplight::Error::RedLight` error. You can provide a
147
+ fallback that will be called in both of these cases. It will be passed the
148
+ error if the light was green.
147
149
 
148
150
  ``` rb
149
151
  light = Stoplight('example-4') { 1 / 0 }
@@ -166,9 +168,8 @@ light.run
166
168
 
167
169
  ### Custom threshold
168
170
 
169
- Some bits of code might be allowed to fail more or less frequently
170
- than others. You can configure this by setting a custom threshold
171
- in seconds.
171
+ Some bits of code might be allowed to fail more or less frequently than others.
172
+ You can configure this by setting a custom threshold.
172
173
 
173
174
  ``` rb
174
175
  light = Stoplight('example-5') { fail }
@@ -185,9 +186,9 @@ The default threshold is `3`.
185
186
 
186
187
  ### Custom timeout
187
188
 
188
- Stoplights will automatically attempt to recover after a certain
189
- amount of time. A light in the red state for longer than the timeout
190
- will transition to the yellow state. This timeout is customizable.
189
+ Stoplights will automatically attempt to recover after a certain amount of
190
+ time. A light in the red state for longer than the timeout will transition to
191
+ the yellow state. This timeout is customizable.
191
192
 
192
193
  ``` rb
193
194
  light = Stoplight('example-6') { fail }
@@ -208,13 +209,15 @@ light.run
208
209
  # RuntimeError:
209
210
  ```
210
211
 
211
- The default timeout is `60` seconds. Set the timeout to `-1` to disable
212
- automatic recovery.
212
+ The default timeout is `60` seconds. To disable automatic recovery, set the
213
+ timeout to `Float::INFINITY`. To make automatic recovery instantaneous, set the
214
+ timeout to `0` seconds. Note that this is not recommended, as it effectively
215
+ replaces the red state with yellow.
213
216
 
214
217
  ### Rails
215
218
 
216
- Stoplight was designed to wrap Rails actions with minimal effort.
217
- Here's an example configuration:
219
+ Stoplight was designed to wrap Rails actions with minimal effort. Here's an
220
+ example configuration:
218
221
 
219
222
  ``` rb
220
223
  class ApplicationController < ActionController::Base
@@ -247,9 +250,12 @@ Stoplight::Light.default_data_store
247
250
  # => #<Stoplight::DataStore::Memory:...>
248
251
  ```
249
252
 
250
- If you want to use a persistent data store, you'll have to set it
251
- up. Currently the only supported persistent data store is Redis.
252
- Make sure you have [the Redis gem][] installed before configuring
253
+ If you want to use a persistent data store, you'll have to set it up. Currently
254
+ the only supported persistent data store is Redis.
255
+
256
+ #### Redis
257
+
258
+ Make sure you have [the Redis gem][] (`~> 3.2`) installed before configuring
253
259
  Stoplight.
254
260
 
255
261
  ``` rb
@@ -273,11 +279,25 @@ Stoplight::Light.default_notifiers
273
279
  ```
274
280
 
275
281
  If you want to send notifications elsewhere, you'll have to set them up.
276
- Currently the only supported notifiers are HipChat and Slack.
282
+
283
+ #### Bugsnag
284
+
285
+ Make sure you have [the Bugsnag gem][] (`~> 2.8`) installed before configuring
286
+ Stoplight.
287
+
288
+ ``` rb
289
+ require 'bugsnag'
290
+ # => true
291
+ notifier = Stoplight::Notifier::Bugsnag.new(Bugsnag)
292
+ # => #<Stoplight::Notifier::Bugsnag:...>
293
+ Stoplight::Light.default_notifiers += [notifier]
294
+ # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Bugsnag:...>]
295
+ ```
277
296
 
278
297
  #### HipChat
279
298
 
280
- Make sure you have [the HipChat gem][] installed before configuring Stoplight.
299
+ Make sure you have [the HipChat gem][] (`~> 1.5`) installed before configuring
300
+ Stoplight.
281
301
 
282
302
  ``` rb
283
303
  require 'hipchat'
@@ -290,9 +310,40 @@ Stoplight::Light.default_notifiers += [notifier]
290
310
  # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::HipChat:...>]
291
311
  ```
292
312
 
313
+ #### Honeybadger
314
+
315
+ Make sure you have [the Honeybadger gem][] (`~> 2.1`) installed before
316
+ configuring Stoplight.
317
+
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:...>]
325
+ ```
326
+
327
+ #### Logger
328
+
329
+ Stoplight can be configured to use [the Logger class][] from the standard
330
+ library.
331
+
332
+ ``` rb
333
+ require 'logger'
334
+ # => true
335
+ logger = Logger.new(STDERR)
336
+ # => #<Logger:...>
337
+ notifier = Stoplight::Notifier::Logger.new(logger)
338
+ # => #<Stoplight::Notifier::Logger:...>
339
+ Stoplight::Light.default_notifiers += [notifier]
340
+ # => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Logger:...>]
341
+ ```
342
+
293
343
  #### Slack
294
344
 
295
- Make sure you have [the Slack gem][] installed before configuring Stoplight.
345
+ Make sure you have [the Slack gem][] (`~> 1.3`) installed before configuring
346
+ Stoplight.
296
347
 
297
348
  ``` rb
298
349
  require 'slack-notifier'
@@ -307,25 +358,25 @@ Stoplight::Light.default_notifiers += [notifier]
307
358
 
308
359
  ### Rails
309
360
 
310
- Stoplight is designed to work seamlessly with Rails. If you want
311
- to use the in-memory data store, you don't need to do anything
312
- special. If you want to use a persistent data store, you'll need
313
- to configure it. Create an initializer for Stoplight:
361
+ Stoplight is designed to work seamlessly with Rails. If you want to use the
362
+ in-memory data store, you don't need to do anything special. If you want to use
363
+ a persistent data store, you'll need to configure it. Create an initializer for
364
+ Stoplight:
314
365
 
315
366
  ``` rb
316
367
  # config/initializers/stoplight.rb
317
368
  require 'stoplight'
318
369
  Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(...)
319
- Stoplight::Light.default_notifiers += [Stoplight::Notifier::HipChat.new(...)]
370
+ Stoplight::Light.default_notifiers += [Stoplight::Notifier::Logger.new(Rails.logger)]
320
371
  ```
321
372
 
322
373
  ## Advanced usage
323
374
 
324
375
  ### Locking
325
376
 
326
- Although stoplights can operate on their own, occasionally you may
327
- want to override the default behavior. You can lock a light in
328
- either the green or red state using `set_state`.
377
+ Although stoplights can operate on their own, occasionally you may want to
378
+ override the default behavior. You can lock a light in either the green or red
379
+ state using `set_state`.
329
380
 
330
381
  ``` rb
331
382
  light = Stoplight('example-7') { true }
@@ -338,12 +389,18 @@ light.run
338
389
  # Stoplight::Error::RedLight: example-7
339
390
  ```
340
391
 
341
- **Code in locked red lights may still run under certain conditions!**
342
- If you have configured a custom data store and that data store
343
- fails, Stoplight will switch over to using a blank in-memory data
344
- store. That means you will lose the locked state of any stoplights.
392
+ **Code in locked red lights may still run under certain conditions!** If you
393
+ have configured a custom data store and that data store fails, Stoplight will
394
+ switch over to using a blank in-memory data store. That means you will lose the
395
+ locked state of any stoplights.
396
+
397
+ You can go back to using the default behavior by unlocking the stoplight.
398
+
399
+ ``` rb
400
+ light.data_store.set_state(light, Stoplight::State::UNLOCKED)
401
+ ```
345
402
 
346
- ## Testing
403
+ ### Testing
347
404
 
348
405
  Stoplights typically work as expected without modification in test suites.
349
406
  However there are a few things you can do to make them behave better. If your
@@ -393,7 +450,10 @@ Stoplight is licensed under [the MIT License][].
393
450
  [the notifiers section]: #notifiers
394
451
  [the timeout section]: #custom-timeout
395
452
  [the redis gem]: https://rubygems.org/gems/redis
453
+ [the bugsnag gem]: https://rubygems.org/gems/bugsnag
396
454
  [the hipchat gem]: https://rubygems.org/gems/hipchat
455
+ [the honeybadger gem]: https://rubygems.org/gems/honeybadger
456
+ [the logger class]: http://ruby-doc.org/stdlib-2.2.3/libdoc/logger/rdoc/Logger.html
397
457
  [the slack gem]: https://rubygems.org/gems/slack-notifier
398
458
  [@camdez]: https://github.com/camdez
399
459
  [@tfausak]: https://github.com/tfausak
data/lib/stoplight.rb CHANGED
@@ -18,8 +18,12 @@ require 'stoplight/data_store/redis'
18
18
  require 'stoplight/notifier'
19
19
  require 'stoplight/notifier/base'
20
20
  require 'stoplight/notifier/generic'
21
+
22
+ require 'stoplight/notifier/bugsnag'
21
23
  require 'stoplight/notifier/hip_chat'
24
+ require 'stoplight/notifier/honeybadger'
22
25
  require 'stoplight/notifier/io'
26
+ require 'stoplight/notifier/logger'
23
27
  require 'stoplight/notifier/slack'
24
28
 
25
29
  require 'stoplight/default'
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+
3
+ module Stoplight
4
+ module Notifier
5
+ # @see Base
6
+ class Bugsnag < Base
7
+ DEFAULT_OPTIONS = {
8
+ severity: 'info'
9
+ }
10
+
11
+ StoplightStatusChange = Class.new(Error::Base)
12
+
13
+ # @return [Proc]
14
+ attr_reader :formatter
15
+ # @return [::Bugsnag]
16
+ attr_reader :bugsnag
17
+ # @return [Hash{Symbol => Object}]
18
+ attr_reader :options
19
+
20
+ # @param bugsnag [::Bugsnag]
21
+ # @param formatter [Proc, nil]
22
+ # @param options [Hash{Symbol => Object}]
23
+ # @option options [String] :severity
24
+ def initialize(bugsnag, formatter = nil, options = {})
25
+ @bugsnag = bugsnag
26
+ @formatter = formatter || Default::FORMATTER
27
+ @options = DEFAULT_OPTIONS.merge(options)
28
+ end
29
+
30
+ def notify(light, from_color, to_color, error)
31
+ message = formatter.call(light, from_color, to_color, error)
32
+ bugsnag.notify(StoplightStatusChange.new(message), options)
33
+ message
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+
3
+ module Stoplight
4
+ module Notifier
5
+ # @see Base
6
+ class Honeybadger < Base
7
+ DEFAULT_OPTIONS = {
8
+ parameters: {},
9
+ session: {},
10
+ context: {}
11
+ }.freeze
12
+
13
+ # @return [String]
14
+ attr_reader :api_key
15
+ # @return [Proc]
16
+ attr_reader :formatter
17
+ # @return [Hash{Symbol => Object}]
18
+ attr_reader :options
19
+
20
+ # @param api_key [String]
21
+ # @param formatter [Proc, nil]
22
+ # @param options [Hash{Symbol => Object}]
23
+ # @option options [Hash] :parameters
24
+ # @option options [Hash] :session
25
+ # @option options [Hash] :context
26
+ def initialize(api_key, formatter = nil, options = {})
27
+ @api_key = api_key
28
+ @formatter = formatter || Default::FORMATTER
29
+ @options = DEFAULT_OPTIONS.merge(options)
30
+ end
31
+
32
+ def notify(light, from_color, to_color, error)
33
+ message = formatter.call(light, from_color, to_color, error)
34
+ h = options.merge(
35
+ api_key: api_key,
36
+ error_message: message,
37
+ backtrace: (error.backtrace if error))
38
+ ::Honeybadger.notify(h)
39
+ message
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ # coding: utf-8
2
+
3
+ module Stoplight
4
+ module Notifier
5
+ # @see Base
6
+ class Logger < Base
7
+ include Generic
8
+
9
+ # @return [::Logger]
10
+ def logger
11
+ @object
12
+ end
13
+
14
+ def put(message)
15
+ logger.warn(message)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Stoplight
4
- VERSION = Gem::Version.new('1.1.1')
4
+ VERSION = Gem::Version.new('1.2.0')
5
5
  end
@@ -0,0 +1,89 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'bugsnag'
5
+
6
+ RSpec.describe Stoplight::Notifier::Bugsnag do
7
+ StoplightStatusChange = Stoplight::Notifier::Bugsnag::StoplightStatusChange
8
+
9
+ it 'is a class' do
10
+ expect(described_class).to be_a(Class)
11
+ end
12
+
13
+ it 'is a subclass of Base' do
14
+ expect(described_class).to be < Stoplight::Notifier::Base
15
+ end
16
+
17
+ describe '#formatter' do
18
+ it 'is initially the default' do
19
+ expect(described_class.new(nil, nil).formatter)
20
+ .to eql(Stoplight::Default::FORMATTER)
21
+ end
22
+
23
+ it 'reads the formatter' do
24
+ formatter = proc {}
25
+ expect(described_class.new(nil, formatter).formatter)
26
+ .to eql(formatter)
27
+ end
28
+ end
29
+
30
+ describe '#options' do
31
+ it 'is initially the default' do
32
+ expect(described_class.new(nil, nil).options)
33
+ .to eql(Stoplight::Notifier::Bugsnag::DEFAULT_OPTIONS)
34
+ end
35
+
36
+ it 'reads the options' do
37
+ options = { key: :value }
38
+ expect(described_class.new(nil, nil, options).options)
39
+ .to eql(Stoplight::Notifier::Bugsnag::DEFAULT_OPTIONS.merge(options))
40
+ end
41
+ end
42
+
43
+ describe '#bugsnag' do
44
+ it 'reads the Bugsnag client' do
45
+ client = ::Bugsnag
46
+ expect(described_class.new(client, nil).bugsnag)
47
+ .to eql(client)
48
+ end
49
+ end
50
+
51
+ describe '#notify' do
52
+ let(:light) { Stoplight::Light.new(name, &code) }
53
+ let(:name) { ('a'..'z').to_a.shuffle.join }
54
+ let(:code) { -> {} }
55
+ let(:from_color) { Stoplight::Color::GREEN }
56
+ let(:to_color) { Stoplight::Color::RED }
57
+ let(:notifier) { described_class.new(bugsnag) }
58
+ let(:bugsnag) { double(Bugsnag) }
59
+
60
+ subject(:result) do
61
+ notifier.notify(light, from_color, to_color, error)
62
+ end
63
+
64
+ before do
65
+ status_change = StoplightStatusChange.new(message)
66
+ expect(bugsnag).to receive(:notify).with(status_change, severity: 'info')
67
+ end
68
+
69
+ context 'when no error given' do
70
+ let(:error) { nil }
71
+
72
+ it 'logs message' do
73
+ expect(result).to eq(message)
74
+ end
75
+ end
76
+
77
+ context 'when message with an error given' do
78
+ let(:error) { ZeroDivisionError.new('divided by 0') }
79
+
80
+ it 'logs message' do
81
+ expect(result).to eq(message)
82
+ end
83
+ end
84
+
85
+ def message
86
+ notifier.formatter.call(light, from_color, to_color, error)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'honeybadger'
3
+
4
+ RSpec.describe Stoplight::Notifier::Honeybadger do
5
+ it 'is a class' do
6
+ expect(described_class).to be_a(Class)
7
+ end
8
+
9
+ it 'is a subclass of Base' do
10
+ expect(described_class).to be < Stoplight::Notifier::Base
11
+ end
12
+
13
+ describe '#formatter' do
14
+ it 'is initially the default' do
15
+ expect(described_class.new(nil).formatter).to eql(
16
+ Stoplight::Default::FORMATTER)
17
+ end
18
+
19
+ it 'reads the formatter' do
20
+ formatter = proc {}
21
+ expect(described_class.new(nil, formatter).formatter).to eql(formatter)
22
+ end
23
+ end
24
+
25
+ describe '#options' do
26
+ it 'is initially the default' do
27
+ expect(described_class.new(nil).options).to eql(
28
+ Stoplight::Notifier::Honeybadger::DEFAULT_OPTIONS)
29
+ end
30
+
31
+ it 'reads the options' do
32
+ options = { key: :value }
33
+ expect(described_class.new(nil, nil, options).options).to eql(
34
+ Stoplight::Notifier::Honeybadger::DEFAULT_OPTIONS.merge(options))
35
+ end
36
+ end
37
+
38
+ describe '#notify' do
39
+ let(:light) { Stoplight::Light.new(name, &code) }
40
+ let(:name) { ('a'..'z').to_a.shuffle.join }
41
+ let(:code) { -> {} }
42
+ let(:from_color) { Stoplight::Color::GREEN }
43
+ let(:to_color) { Stoplight::Color::RED }
44
+ let(:notifier) { described_class.new(api_key) }
45
+ let(:api_key) { ('a'..'z').to_a.shuffle.join }
46
+
47
+ before do
48
+ allow(Honeybadger).to receive(:notify)
49
+ end
50
+
51
+ it 'returns the message' do
52
+ error = nil
53
+ message = notifier.formatter.call(light, from_color, to_color, error)
54
+ expect(notifier.notify(light, from_color, to_color, error)).to eql(
55
+ message)
56
+ expect(Honeybadger).to have_received(:notify).with(
57
+ hash_including(
58
+ api_key: api_key,
59
+ error_message: message))
60
+ end
61
+
62
+ it 'returns the message with an error' do
63
+ error = ZeroDivisionError.new('divided by 0')
64
+ message = notifier.formatter.call(light, from_color, to_color, error)
65
+ expect(notifier.notify(light, from_color, to_color, error)).to eql(
66
+ message)
67
+ expect(Honeybadger).to have_received(:notify).with(
68
+ hash_including(
69
+ api_key: api_key,
70
+ error_message: message,
71
+ backtrace: error.backtrace))
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,70 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Stoplight::Notifier::Logger do
6
+ it 'is a class' do
7
+ expect(described_class).to be_a(Class)
8
+ end
9
+
10
+ it 'is a subclass of Base' do
11
+ expect(described_class).to be < Stoplight::Notifier::Base
12
+ end
13
+
14
+ describe '#formatter' do
15
+ it 'is initially the default' do
16
+ expect(described_class.new(nil, nil).formatter)
17
+ .to eql(Stoplight::Default::FORMATTER)
18
+ end
19
+
20
+ it 'reads the formatter' do
21
+ formatter = proc {}
22
+ expect(described_class.new(nil, formatter).formatter)
23
+ .to eql(formatter)
24
+ end
25
+ end
26
+
27
+ describe '#logger' do
28
+ it 'reads the logger' do
29
+ logger = Logger.new(StringIO.new)
30
+ expect(described_class.new(logger, nil).logger)
31
+ .to eql(logger)
32
+ end
33
+ end
34
+
35
+ describe '#notify' do
36
+ let(:light) { Stoplight::Light.new(name, &code) }
37
+ let(:name) { ('a'..'z').to_a.shuffle.join }
38
+ let(:code) { -> {} }
39
+ let(:from_color) { Stoplight::Color::GREEN }
40
+ let(:to_color) { Stoplight::Color::RED }
41
+ let(:notifier) { described_class.new(Logger.new(io)) }
42
+ let(:io) { StringIO.new }
43
+
44
+ before do
45
+ notifier.notify(light, from_color, to_color, error)
46
+ end
47
+
48
+ subject(:result) { io.string }
49
+
50
+ context 'when no error given' do
51
+ let(:error) { nil }
52
+
53
+ it 'logs message' do
54
+ expect(result).to match(/.+#{message}/)
55
+ end
56
+ end
57
+
58
+ context 'when message with an error given' do
59
+ let(:error) { ZeroDivisionError.new('divided by 0') }
60
+
61
+ it 'logs message' do
62
+ expect(result).to match(/.+#{message}/)
63
+ end
64
+ end
65
+
66
+ def message
67
+ notifier.formatter.call(light, from_color, to_color, error)
68
+ end
69
+ end
70
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stoplight
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Desautels
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-07-16 00:00:00.000000000 Z
13
+ date: 2015-09-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: benchmark-ips
@@ -18,14 +18,28 @@ dependencies:
18
18
  requirements:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: '2.2'
21
+ version: '2.3'
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: '2.2'
28
+ version: '2.3'
29
+ - !ruby/object:Gem::Dependency
30
+ name: bugsnag
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '2.8'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '2.8'
29
43
  - !ruby/object:Gem::Dependency
30
44
  name: coveralls
31
45
  requirement: !ruby/object:Gem::Requirement
@@ -60,14 +74,14 @@ dependencies:
60
74
  requirements:
61
75
  - - "~>"
62
76
  - !ruby/object:Gem::Version
63
- version: '2.12'
77
+ version: '2.13'
64
78
  type: :development
65
79
  prerelease: false
66
80
  version_requirements: !ruby/object:Gem::Requirement
67
81
  requirements:
68
82
  - - "~>"
69
83
  - !ruby/object:Gem::Version
70
- version: '2.12'
84
+ version: '2.13'
71
85
  - !ruby/object:Gem::Dependency
72
86
  name: guard-rspec
73
87
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +124,20 @@ dependencies:
110
124
  - - "~>"
111
125
  - !ruby/object:Gem::Version
112
126
  version: '1.5'
127
+ - !ruby/object:Gem::Dependency
128
+ name: honeybadger
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - "~>"
132
+ - !ruby/object:Gem::Version
133
+ version: '2.1'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - "~>"
139
+ - !ruby/object:Gem::Version
140
+ version: '2.1'
113
141
  - !ruby/object:Gem::Dependency
114
142
  name: rake
115
143
  requirement: !ruby/object:Gem::Requirement
@@ -158,42 +186,42 @@ dependencies:
158
186
  requirements:
159
187
  - - "~>"
160
188
  - !ruby/object:Gem::Version
161
- version: '0.32'
189
+ version: '0.34'
162
190
  type: :development
163
191
  prerelease: false
164
192
  version_requirements: !ruby/object:Gem::Requirement
165
193
  requirements:
166
194
  - - "~>"
167
195
  - !ruby/object:Gem::Version
168
- version: '0.32'
196
+ version: '0.34'
169
197
  - !ruby/object:Gem::Dependency
170
198
  name: slack-notifier
171
199
  requirement: !ruby/object:Gem::Requirement
172
200
  requirements:
173
201
  - - "~>"
174
202
  - !ruby/object:Gem::Version
175
- version: 1.2.1
203
+ version: '1.3'
176
204
  type: :development
177
205
  prerelease: false
178
206
  version_requirements: !ruby/object:Gem::Requirement
179
207
  requirements:
180
208
  - - "~>"
181
209
  - !ruby/object:Gem::Version
182
- version: 1.2.1
210
+ version: '1.3'
183
211
  - !ruby/object:Gem::Dependency
184
212
  name: timecop
185
213
  requirement: !ruby/object:Gem::Requirement
186
214
  requirements:
187
215
  - - "~>"
188
216
  - !ruby/object:Gem::Version
189
- version: '0.7'
217
+ version: '0.8'
190
218
  type: :development
191
219
  prerelease: false
192
220
  version_requirements: !ruby/object:Gem::Requirement
193
221
  requirements:
194
222
  - - "~>"
195
223
  - !ruby/object:Gem::Version
196
- version: '0.7'
224
+ version: '0.8'
197
225
  - !ruby/object:Gem::Dependency
198
226
  name: yard
199
227
  requirement: !ruby/object:Gem::Requirement
@@ -233,9 +261,12 @@ files:
233
261
  - lib/stoplight/light/runnable.rb
234
262
  - lib/stoplight/notifier.rb
235
263
  - lib/stoplight/notifier/base.rb
264
+ - lib/stoplight/notifier/bugsnag.rb
236
265
  - lib/stoplight/notifier/generic.rb
237
266
  - lib/stoplight/notifier/hip_chat.rb
267
+ - lib/stoplight/notifier/honeybadger.rb
238
268
  - lib/stoplight/notifier/io.rb
269
+ - lib/stoplight/notifier/logger.rb
239
270
  - lib/stoplight/notifier/slack.rb
240
271
  - lib/stoplight/state.rb
241
272
  - lib/stoplight/version.rb
@@ -251,9 +282,12 @@ files:
251
282
  - spec/stoplight/light/runnable_spec.rb
252
283
  - spec/stoplight/light_spec.rb
253
284
  - spec/stoplight/notifier/base_spec.rb
285
+ - spec/stoplight/notifier/bugsnag_spec.rb
254
286
  - spec/stoplight/notifier/generic_spec.rb
255
287
  - spec/stoplight/notifier/hip_chat_spec.rb
288
+ - spec/stoplight/notifier/honeybadger_spec.rb
256
289
  - spec/stoplight/notifier/io_spec.rb
290
+ - spec/stoplight/notifier/logger_spec.rb
257
291
  - spec/stoplight/notifier/slack_spec.rb
258
292
  - spec/stoplight/notifier_spec.rb
259
293
  - spec/stoplight/state_spec.rb
@@ -296,9 +330,12 @@ test_files:
296
330
  - spec/stoplight/light/runnable_spec.rb
297
331
  - spec/stoplight/light_spec.rb
298
332
  - spec/stoplight/notifier/base_spec.rb
333
+ - spec/stoplight/notifier/bugsnag_spec.rb
299
334
  - spec/stoplight/notifier/generic_spec.rb
300
335
  - spec/stoplight/notifier/hip_chat_spec.rb
336
+ - spec/stoplight/notifier/honeybadger_spec.rb
301
337
  - spec/stoplight/notifier/io_spec.rb
338
+ - spec/stoplight/notifier/logger_spec.rb
302
339
  - spec/stoplight/notifier/slack_spec.rb
303
340
  - spec/stoplight/notifier_spec.rb
304
341
  - spec/stoplight/state_spec.rb