flipper-echo 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d0193b01d3747866567392b00529405385d4b23
4
- data.tar.gz: 03d2d537df9c51aca849cc279245dd39f63835cb
3
+ metadata.gz: 8b2f87cd93693142ff67b6ceeed1444b6f7b012f
4
+ data.tar.gz: 31f38685142d8e670acaf735beaea1432810b873
5
5
  SHA512:
6
- metadata.gz: 361febaf3cf8772c5eb2f723a3896068c00588e9f6e2aaf2003d3a52fb00a65356ba0dd3c94fb70b08767bfacdf3dd209e5f1e649140c95ec3b7b0b35d355982
7
- data.tar.gz: 94c0a649e23ff84a9b9c41fce70c1145c3a24a9222377c073e237c4fae93ceabb7a12f1fa82cfdd4f8594d2a21c89d220e80f11256eb57c01c39b26ad25ea378
6
+ metadata.gz: 5d465bf8cf158efb1f6edb3c57ef6f952d9f6b2667cf39c59a1040884b7cbab83b69f2bd33758bd50187b47604b9385b635e65894bd91dcd92695e61582c59bb
7
+ data.tar.gz: 318e9de9d3ce75c78bd78d2802fecd91eb855f0120b55793428e7a3ee01896f3f56cbf5c004ed5855ba524969a931613f03cf1c4aec1e3faef2113167a0400ed
data/.gitignore CHANGED
@@ -12,4 +12,5 @@
12
12
  *.o
13
13
  *.a
14
14
  mkmf.log
15
+ assets
15
16
  *.gem
data/README.md CHANGED
@@ -1,12 +1,15 @@
1
1
  # Flipper::Echo
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/flipper-echo.svg)](http://badge.fury.io/rb/flipper-echo)
4
+
3
5
  This gem adds a simple callback interface for
4
6
  [Flipper](https://github.com/jnunemaker/flipper) adapter events.
5
7
 
6
8
  For example, when a Flipper feature is changed, you can:
7
9
 
8
- * send a Slack notification
10
+ * send a Slack notification (built-in)
9
11
  * write the change to a database or log file
12
+ * notify a performance monitoring application
10
13
  * send a YO
11
14
 
12
15
  ## Installation
@@ -34,9 +37,9 @@ normally would (any adapter will do), e.g.:
34
37
  FLIPPER = Flipper.new(Flipper::Adapters::Memory.new)
35
38
  ```
36
39
 
37
- Then configure `Flipper::Echo`:
40
+ Then configure this gem in any of the following ways:
38
41
 
39
- #### Option 1: handle event with a proc
42
+ #### Handle event with a proc
40
43
 
41
44
  ```ruby
42
45
  Flipper::Echo.configure do |config|
@@ -50,12 +53,12 @@ Flipper::Echo.configure do |config|
50
53
  end
51
54
  ```
52
55
 
53
- #### Option 2: handle event with a notifier class
56
+ #### Handle event with an object
54
57
 
55
- `Flipper::Echo` can also use any object that has a `notify` method:
58
+ You can provide any Ruby object with a `notify` method:
56
59
 
57
60
  ```ruby
58
- class FlipperNotifier
61
+ class CustomNotifier
59
62
  def notify(event)
60
63
  # Do something with the event...
61
64
  end
@@ -63,10 +66,46 @@ end
63
66
 
64
67
  Flipper::Echo.configure do |config|
65
68
  config.flipper = FLIPPER
66
- config.notifier = FlipperNotifier.new
69
+ config.notifier = CustomNotifier.new
70
+ end
71
+ ```
72
+
73
+ #### Configure multiple notifiers
74
+
75
+ ```ruby
76
+ Flipper::Echo.configure do |config|
77
+ config.flipper = FLIPPER
78
+
79
+ config.notifiers << NewrelicNotifier.new
80
+ config.notifiers << GraphiteNotifier.new
81
+ config.notifiers << Flipper::Echo::Stdout::Notifier.new
82
+ end
83
+ ```
84
+
85
+ ## Built-in notifiers
86
+
87
+ #### Slack
88
+
89
+ First [create a Slack webhook url](https://slack.com/services/new/incoming-webhook),
90
+ then use it in your configuration:
91
+
92
+ ```ruby
93
+ Flipper::Echo.configure do |config|
94
+ config.notifiers << Flipper::Echo::Slack::Notifier.new(
95
+ 'https://hooks.slack.com/your/webhook...', channel: '#eng')
67
96
  end
68
97
  ```
69
98
 
99
+ ![Slack example 1](https://s3-us-west-2.amazonaws.com/mode.production/flipper-echo/prod-search-admins.png)
100
+
101
+ ![Slack example 2](https://s3-us-west-2.amazonaws.com/mode.production/flipper-echo/prod-risky-actors.png)
102
+
103
+ ![Slack example 3](https://s3-us-west-2.amazonaws.com/mode.production/flipper-echo/staging-rolled-out-removed.png)
104
+
105
+ ## Documentation
106
+
107
+ [http://www.rubydoc.info/github/mode/flipper-echo/master](http://www.rubydoc.info/github/mode/flipper-echo/master)
108
+
70
109
  ## Contributing
71
110
 
72
111
  1. Fork it ( https://github.com/mode/flipper-echo/fork )
@@ -74,3 +113,7 @@ end
74
113
  3. Commit your changes (`git commit -am 'Add some feature'`)
75
114
  4. Push to the branch (`git push origin my-new-feature`)
76
115
  5. Create a new Pull Request
116
+
117
+ ## Actual screenshots
118
+
119
+ ![Newrelic example 1](https://s3-us-west-2.amazonaws.com/mode.production/flipper-echo/actual-newrelic-screenshot.png)
data/lib/flipper/echo.rb CHANGED
@@ -2,6 +2,8 @@
2
2
  #
3
3
  require 'flipper/echo/configuration'
4
4
  require 'flipper/echo/event'
5
+ require 'flipper/echo/slack'
6
+ require 'flipper/echo/stdout'
5
7
  require 'flipper/echo/version'
6
8
 
7
9
  # Flipper namespace
@@ -32,7 +34,7 @@ module Flipper
32
34
  def enable(feature, gate, target)
33
35
  super.tap do
34
36
  Flipper::Echo::Event.new(
35
- feature, :enable, gate: gate, target: target).notify
37
+ feature, :enabled, gate: gate, target: target).notify
36
38
  end
37
39
  end
38
40
 
@@ -41,20 +43,20 @@ module Flipper
41
43
  def disable(feature, gate, target)
42
44
  super.tap do
43
45
  Flipper::Echo::Event.new(
44
- feature, :disable, gate: gate, target: target).notify
46
+ feature, :disabled, gate: gate, target: target).notify
45
47
  end
46
48
  end
47
49
 
48
50
  # Notify adapter remove events
49
51
  #
50
52
  def remove(feature)
51
- super.tap { Flipper::Echo::Event.new(feature, :remove).notify }
53
+ super.tap { Flipper::Echo::Event.new(feature, :removed).notify }
52
54
  end
53
55
 
54
56
  # Notify adapter clear events
55
57
  #
56
58
  def clear(feature)
57
- super.tap { Flipper::Echo::Event.new(feature, :clear).notify }
59
+ super.tap { Flipper::Echo::Event.new(feature, :cleared).notify }
58
60
  end
59
61
  end
60
62
  end
@@ -10,7 +10,33 @@ module Flipper
10
10
  # intended behavior.
11
11
  #
12
12
  class Configuration
13
- attr_accessor :notifier
13
+ # All configured notifiers
14
+ #
15
+ # @return [Array]
16
+ #
17
+ def notifiers
18
+ @notifiers ||= []
19
+ end
20
+
21
+ # Convenience method for assigning a single notifier
22
+ #
23
+ # @return [Array] all notifiers
24
+ #
25
+ def notifier=(notifier)
26
+ notifiers << notifier
27
+ end
28
+
29
+ # Optional environment name
30
+ #
31
+ attr_writer :environment
32
+
33
+ # Configured environment name
34
+ #
35
+ # @return [String]
36
+ #
37
+ def environment
38
+ @environment ||= ENV['FLIPPER_ECHO_ENVIRONMENT']
39
+ end
14
40
 
15
41
  # Assign Flipper instance
16
42
  #
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  #
3
+ require 'flipper'
4
+
3
5
  module Flipper
4
6
  module Echo
5
7
  # Encapsulates relevant information about a Flipper adapter event
@@ -11,7 +13,7 @@ module Flipper
11
13
  #
12
14
  # @param feature [Flipper::Feature] the feature that was changed
13
15
  # @param action [Symbol] the action
14
- # (`:enable`, `:disable`, `:remove` or `:clear`)
16
+ # (`:enabled`, `:disabled`, `:removed` or `:cleared`)
15
17
  # @param options [optional, Hash] hash of options
16
18
  #
17
19
  # @option options [Flipper::Type] :target the event target
@@ -68,10 +70,14 @@ module Flipper
68
70
  # Passes this event to the configured notifier
69
71
  #
70
72
  def notify
71
- if notifier.is_a?(Proc)
72
- notifier.call(self)
73
- elsif notifier.respond_to?(:notify)
74
- notifier.notify(self)
73
+ notifiers.each do |notifier|
74
+ method = if notifier.is_a?(Proc)
75
+ :call
76
+ elsif notifier.respond_to?(:notify)
77
+ :notify
78
+ end
79
+
80
+ notifier.send(method, self) if method
75
81
  end
76
82
  end
77
83
 
@@ -93,8 +99,8 @@ module Flipper
93
99
 
94
100
  private
95
101
 
96
- def notifier
97
- Flipper::Echo.configuration.notifier
102
+ def notifiers
103
+ Flipper::Echo.configuration.notifiers
98
104
  end
99
105
  end
100
106
  end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'flipper/echo/slack/message'
4
+ require 'flipper/echo/slack/notifier'
5
+
6
+ module Flipper
7
+ module Echo
8
+ # Slack notifier add-on module
9
+ #
10
+ module Slack
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Flipper
4
+ module Echo
5
+ module Slack
6
+ # Slack message formatter
7
+ #
8
+ class Message
9
+ attr_reader :event
10
+
11
+ # Construct a new Message instance
12
+ #
13
+ # @param event [Flipper::Echo::Event] the Flipper event
14
+ #
15
+ # @return [Flipper::Echo::Slack::Message] the instance
16
+ #
17
+ def initialize(event)
18
+ @event = event
19
+ end
20
+
21
+ # Build message body
22
+ #
23
+ # @return [String] the message body
24
+ #
25
+ def to_s
26
+ [base, event.action, filters].compact.join(' ')
27
+ end
28
+
29
+ private
30
+
31
+ # @return [String]
32
+ #
33
+ def base
34
+ parts = []
35
+ parts << "[*#{environment}*]" if environment
36
+ parts << "Feature `#{event.feature.name}`"
37
+ parts.join(' ')
38
+ end
39
+
40
+ # @return [String, nil]
41
+ #
42
+ def filters
43
+ if event.group
44
+ "for group `#{event.group.name}`"
45
+ elsif event.actor
46
+ "for actor `#{event.actor.flipper_id}`"
47
+ elsif event.percentage_of_actors
48
+ "for #{event.percentage_of_actors.value}% of actors"
49
+ elsif event.percentage_of_random
50
+ "for #{event.percentage_of_random.value}% of random"
51
+ end
52
+ end
53
+
54
+ # @return [String, nil]
55
+ #
56
+ def environment
57
+ Flipper::Echo.configuration.environment
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,143 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'json'
4
+ require 'net/https'
5
+ require 'uri'
6
+
7
+ module Flipper
8
+ module Echo
9
+ module Slack
10
+ # Customizable Slack webhook notifier
11
+ #
12
+ class Notifier
13
+ attr_reader :webhook_url, :options
14
+
15
+ # Construct a new Notifier instance
16
+ #
17
+ # @param webhook_url [String] the Slack webhook url
18
+ # @param options [optional, Hash] hash of options
19
+ #
20
+ # @option options [String] :username Slack bot username
21
+ # @option options [String] :channel name of Slack channel
22
+ # @option options [String] :icon_url the message icon url
23
+ #
24
+ # @return [Flipper::Echo::Slack::Notifier] the instance
25
+ #
26
+ def initialize(webhook_url, options = {})
27
+ @webhook_url = webhook_url
28
+ @options = self.class.symbolize_keys(options)
29
+ end
30
+
31
+ # Normalized channel name
32
+ #
33
+ # @return [String, nil]
34
+ #
35
+ def channel
36
+ @channel ||= self.class.normalize_channel(options[:channel])
37
+ end
38
+
39
+ # Slack message username
40
+ #
41
+ # @return [String, nil]
42
+ #
43
+ def username
44
+ options.fetch(:username, DEFAULT_USERNAME)
45
+ end
46
+
47
+ # Slack message icon url
48
+ #
49
+ # @return [String, nil]
50
+ #
51
+ def icon_url
52
+ options.fetch(:icon_url, DEFAULT_ICON_URL)
53
+ end
54
+
55
+ # Post notification to Slack webhook url
56
+ #
57
+ # @return [HTTPResponse]
58
+ #
59
+ def notify(event)
60
+ message = Flipper::Echo::Slack::Message.new(event)
61
+
62
+ payload = {
63
+ text: message.to_s,
64
+ channel: channel,
65
+ username: username,
66
+ icon_url: icon_url
67
+ }
68
+
69
+ post(payload)
70
+ end
71
+
72
+ class << self
73
+ # Normalize Slack channel name for API
74
+ #
75
+ # @param name [String] the original name
76
+ #
77
+ # @return [String] the normalized name
78
+ #
79
+ def normalize_channel(name)
80
+ return unless name && name.size > 0
81
+
82
+ name =~ /\A[#@]/ ? name : "##{name}"
83
+ end
84
+
85
+ # Symbolize hash keys
86
+ #
87
+ # @param hash [Hash] the original hash
88
+ #
89
+ # @return [Hash] hash with symbolized keys
90
+ #
91
+ def symbolize_keys(hash)
92
+ hash.each_with_object({}) do |(key, value), result|
93
+ result[key.to_sym] = value
94
+ end
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ DEFAULT_USERNAME = 'Flipper'
101
+
102
+ DEFAULT_ICON_URL =
103
+ 'https://s3-us-west-2.amazonaws.com/mode.production/' \
104
+ 'flipper-echo/flipper-echo-icon-132.png'
105
+
106
+ # @return [Net::HTTP]
107
+ #
108
+ def http
109
+ @http ||= Net::HTTP.new(uri.host, uri.port).tap do |result|
110
+ result.use_ssl = ssl?
111
+ end
112
+ end
113
+
114
+ # @return [URI]
115
+ #
116
+ def uri
117
+ @uri ||= URI.parse(webhook_url)
118
+ end
119
+
120
+ # @return [true, false]
121
+ #
122
+ def ssl?
123
+ uri.scheme == 'https'
124
+ end
125
+
126
+ # @return [HTTPResponse]
127
+ #
128
+ def post(payload)
129
+ request = Net::HTTP::Post.new(uri.request_uri)
130
+ request.set_content_type('application/json')
131
+ request.body = payload.to_json
132
+
133
+ http.request(request).tap do |response|
134
+ unless response.code == '200'
135
+ $stderr.puts 'Unexpected Slack notifier response: ' \
136
+ "#{response.code}"
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'flipper/echo/stdout/notifier'
4
+
5
+ module Flipper
6
+ module Echo
7
+ # A stdout notifier add-on module
8
+ #
9
+ module Stdout
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ #
3
+ module Flipper
4
+ module Echo
5
+ module Stdout
6
+ # Simple stdout notifier intended as a basis for more advanced custom
7
+ # notifiers
8
+ #
9
+ class Notifier
10
+ # Send notification message to stdout
11
+ #
12
+ # @return [nil]
13
+ #
14
+ def notify(event)
15
+ parts = []
16
+ parts << '[Flipper]'
17
+ parts << "Feature \"#{event.feature.name}\""
18
+ parts << event.action
19
+
20
+ target = target_message(event)
21
+
22
+ parts << "for #{target}" if target
23
+
24
+ $stdout.puts(parts.join(' '))
25
+ end
26
+
27
+ private
28
+
29
+ # @return [String, nil]
30
+ #
31
+ def target_message(event)
32
+ if event.group
33
+ "group #{event.group.name}"
34
+ elsif event.actor
35
+ "actor #{event.actor.flipper_id}"
36
+ elsif event.percentage_of_actors
37
+ "#{event.percentage_of_actors.value}% of actors"
38
+ elsif event.percentage_of_random
39
+ "#{event.percentage_of_random.value}% of random"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -4,6 +4,6 @@ module Flipper
4
4
  module Echo
5
5
  # Current gem version
6
6
  #
7
- VERSION = '0.0.1'
7
+ VERSION = '0.0.2'
8
8
  end
9
9
  end
@@ -30,4 +30,16 @@ describe Flipper::Echo::Configuration do
30
30
  configuration.flipper = flipper
31
31
  end
32
32
  end
33
+
34
+ describe '#notifier=' do
35
+ it 'appends notifier' do
36
+ notifier = double(:notifier)
37
+
38
+ expect(configuration.notifiers).to eq([])
39
+
40
+ configuration.notifier = notifier
41
+
42
+ expect(configuration.notifiers).to eq([notifier])
43
+ end
44
+ end
33
45
  end
@@ -123,7 +123,7 @@ describe Flipper::Echo::Event do
123
123
  it 'calls proc' do
124
124
  procedure = proc {}
125
125
 
126
- allow(event).to receive(:notifier).and_return(procedure)
126
+ allow(event).to receive(:notifiers).and_return([procedure])
127
127
 
128
128
  expect(procedure).to receive(:call)
129
129
 
@@ -137,7 +137,7 @@ describe Flipper::Echo::Event do
137
137
  end
138
138
 
139
139
  it 'calls instance method if it exists' do
140
- allow(event).to receive(:notifier).and_return(notifier)
140
+ allow(event).to receive(:notifiers).and_return([notifier])
141
141
 
142
142
  expect(notifier).to receive(:notify)
143
143
 
@@ -160,18 +160,18 @@ describe Flipper::Echo::Event do
160
160
  end
161
161
  end
162
162
 
163
- describe '#notifier' do
163
+ describe '#notifiers' do
164
164
  let :notifier do
165
165
  double(:notifier)
166
166
  end
167
167
 
168
168
  it 'returns notifier from config' do
169
169
  allow(Flipper::Echo.configuration).to(
170
- receive(:notifier).and_return(notifier))
170
+ receive(:notifiers).and_return([notifier]))
171
171
 
172
172
  event = Flipper::Echo::Event.new(feature, action)
173
173
 
174
- expect(event.send(:notifier)).to eq(notifier)
174
+ expect(event.send(:notifiers)).to eq([notifier])
175
175
  end
176
176
  end
177
177
  end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Flipper::Echo::Slack::Message do
6
+ let :feature do
7
+ double(:feature, name: :experimental)
8
+ end
9
+
10
+ describe '#base' do
11
+ let :event do
12
+ Flipper::Echo::Event.new(feature, :enabled)
13
+ end
14
+
15
+ it 'builds expected string without environment' do
16
+ message = Flipper::Echo::Slack::Message.new(event)
17
+
18
+ expect(message.send(:base)).to eq('Feature `experimental`')
19
+ end
20
+
21
+ it 'builds expected string with environment' do
22
+ message = Flipper::Echo::Slack::Message.new(event)
23
+
24
+ allow(message).to receive(:environment).and_return('staging')
25
+
26
+ expect(message.send(:base)).to eq('[*staging*] Feature `experimental`')
27
+ end
28
+ end
29
+
30
+ describe '#filters' do
31
+ it 'builds group string' do
32
+ event = Flipper::Echo::Event.new(feature, :disabled)
33
+ message = Flipper::Echo::Slack::Message.new(event)
34
+
35
+ group = double(:group, name: 'admins')
36
+ allow(event).to receive(:group).and_return(group)
37
+
38
+ expect(message.send(:filters)).to eq('for group `admins`')
39
+ end
40
+
41
+ it 'builds actor string' do
42
+ event = Flipper::Echo::Event.new(feature, :disabled)
43
+ message = Flipper::Echo::Slack::Message.new(event)
44
+
45
+ actor = double(:actor, flipper_id: 'Actor:1')
46
+ allow(event).to receive(:actor).and_return(actor)
47
+
48
+ expect(message.send(:filters)).to eq('for actor `Actor:1`')
49
+ end
50
+
51
+ it 'builds percentage of actors string' do
52
+ event = Flipper::Echo::Event.new(feature, :disabled)
53
+ message = Flipper::Echo::Slack::Message.new(event)
54
+
55
+ percentage = double(:percentage, value: '10')
56
+ allow(event).to receive(:percentage_of_actors).and_return(percentage)
57
+
58
+ expect(message.send(:filters)).to eq('for 10% of actors')
59
+ end
60
+
61
+ it 'builds percentage of random string' do
62
+ event = Flipper::Echo::Event.new(feature, :disabled)
63
+ message = Flipper::Echo::Slack::Message.new(event)
64
+
65
+ percentage = double(:percentage, value: '10')
66
+ allow(event).to receive(:percentage_of_random).and_return(percentage)
67
+
68
+ expect(message.send(:filters)).to eq('for 10% of random')
69
+ end
70
+ end
71
+
72
+ describe '#environment' do
73
+ it 'returns configured environment' do
74
+ allow(Flipper::Echo.configuration).to(
75
+ receive(:environment).and_return('staging'))
76
+
77
+ message = Flipper::Echo::Slack::Message.new(nil)
78
+
79
+ expect(message.send(:environment)).to eq('staging')
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,126 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Flipper::Echo::Slack::Notifier do
6
+ describe '#channel' do
7
+ it 'is null if options do not include channel' do
8
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
9
+
10
+ expect(notifier.channel).to eq(nil)
11
+ end
12
+
13
+ it 'returns channel from options without prepending' do
14
+ notifier = Flipper::Echo::Slack::Notifier.new(
15
+ 'https://hook', channel: '#custom_channel')
16
+
17
+ expect(notifier.channel).to eq('#custom_channel')
18
+ end
19
+
20
+ it 'returns channel from options with prepending' do
21
+ notifier = Flipper::Echo::Slack::Notifier.new(
22
+ 'https://hook', channel: 'custom_channel')
23
+
24
+ expect(notifier.channel).to eq('#custom_channel')
25
+ end
26
+ end
27
+
28
+ describe '#username' do
29
+ it 'is default if options do not include username' do
30
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
31
+
32
+ expect(notifier.username).to eq('Flipper')
33
+ end
34
+
35
+ it 'returns username from options' do
36
+ notifier = Flipper::Echo::Slack::Notifier.new(
37
+ 'https://hook', username: 'Custom Username')
38
+
39
+ expect(notifier.username).to eq('Custom Username')
40
+ end
41
+ end
42
+
43
+ describe '#icon_url' do
44
+ it 'is default if options do not include url' do
45
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
46
+
47
+ expect(notifier.icon_url).to match(/flipper\-echo\-icon/)
48
+ end
49
+
50
+ it 'returns username from options' do
51
+ notifier = Flipper::Echo::Slack::Notifier.new(
52
+ 'https://hook', icon_url: 'custom-icon')
53
+
54
+ expect(notifier.icon_url).to eq('custom-icon')
55
+ end
56
+ end
57
+
58
+ describe '#notify' do
59
+ let :feature do
60
+ double(:feature, name: :experimental)
61
+ end
62
+
63
+ let :event do
64
+ Flipper::Echo::Event.new(feature, :action)
65
+ end
66
+
67
+ it 'calls notifier with expected options' do
68
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
69
+
70
+ expect(notifier).to receive(:post) do |options|
71
+ expect(options[:text]).to match(/experimental/)
72
+ end
73
+
74
+ notifier.notify(event)
75
+ end
76
+ end
77
+
78
+ describe '#post' do
79
+ it 'posts expected json payload' do
80
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
81
+
82
+ expect(notifier.send(:http)).to receive(:request) do |request|
83
+ expect(request.body).to eq('{"foo":"bar"}')
84
+ expect(request.content_type).to eq('application/json')
85
+
86
+ double(:response, code: '200')
87
+ end
88
+
89
+ notifier.send(:post, foo: 'bar')
90
+ end
91
+
92
+ it 'prints unexpected api response to stderr' do
93
+ notifier = Flipper::Echo::Slack::Notifier.new('https://hook')
94
+
95
+ expect(notifier.send(:http)).to(
96
+ receive(:request).and_return(double(:response, code: '404')))
97
+
98
+ expect { notifier.send(:post, foo: 'bar') }.to output.to_stderr
99
+ end
100
+ end
101
+
102
+ describe '.normalize_channel' do
103
+ it 'is nil for blank name' do
104
+ expect(Flipper::Echo::Slack::Notifier.normalize_channel(nil)).to eq(nil)
105
+ expect(Flipper::Echo::Slack::Notifier.normalize_channel('')).to eq(nil)
106
+ end
107
+
108
+ it 'preserves name including number sign' do
109
+ result = Flipper::Echo::Slack::Notifier.normalize_channel('#channel_name')
110
+
111
+ expect(result).to eq('#channel_name')
112
+ end
113
+
114
+ it 'prepends number sign if necessary' do
115
+ result = Flipper::Echo::Slack::Notifier.normalize_channel('channel_name')
116
+
117
+ expect(result).to eq('#channel_name')
118
+ end
119
+
120
+ it 'preserves at sign' do
121
+ result = Flipper::Echo::Slack::Notifier.normalize_channel('@channel_name')
122
+
123
+ expect(result).to eq('@channel_name')
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+ #
3
+ require 'spec_helper'
4
+
5
+ describe Flipper::Echo::Stdout::Notifier do
6
+ let :feature do
7
+ double(:feature, name: :experimental)
8
+ end
9
+
10
+ let :event do
11
+ Flipper::Echo::Event.new(feature, :enabled)
12
+ end
13
+
14
+ let :notifier do
15
+ Flipper::Echo::Stdout::Notifier.new
16
+ end
17
+
18
+ describe '#notify' do
19
+ it 'sends message to stdout' do
20
+ expect { notifier.notify(event) }.to output.to_stdout
21
+ end
22
+ end
23
+
24
+ describe '#target_message' do
25
+ it 'builds group message' do
26
+ allow(event).to receive(:group).and_return(double(:group, name: :admins))
27
+
28
+ expect(notifier.send(:target_message, event)).to eq('group admins')
29
+ end
30
+
31
+ it 'builds actor message' do
32
+ allow(event).to(
33
+ receive(:actor).and_return(double(:actor, flipper_id: 'id')))
34
+
35
+ expect(notifier.send(:target_message, event)).to eq('actor id')
36
+ end
37
+
38
+ it 'builds percentage of actors message' do
39
+ allow(event).to(
40
+ receive(:percentage_of_actors).and_return(double(:actors, value: 15)))
41
+
42
+ expect(notifier.send(:target_message, event)).to eq('15% of actors')
43
+ end
44
+
45
+ it 'builds percentage of random message' do
46
+ allow(event).to(
47
+ receive(:percentage_of_random).and_return(double(:random, value: 15)))
48
+
49
+ expect(notifier.send(:target_message, event)).to eq('15% of random')
50
+ end
51
+ end
52
+ end
@@ -43,7 +43,7 @@ describe Flipper::Echo do
43
43
  describe '#enable' do
44
44
  it 'enables feature' do
45
45
  expect_any_instance_of(Flipper::Echo::Event).to receive(:notify) do |e|
46
- expect(e.action).to eq(:enable)
46
+ expect(e.action).to eq(:enabled)
47
47
  end
48
48
 
49
49
  adapter.enable(feature, gate, target)
@@ -53,7 +53,7 @@ describe Flipper::Echo do
53
53
  describe '#disable' do
54
54
  it 'disables feature' do
55
55
  expect_any_instance_of(Flipper::Echo::Event).to receive(:notify) do |e|
56
- expect(e.action).to eq(:disable)
56
+ expect(e.action).to eq(:disabled)
57
57
  end
58
58
 
59
59
  adapter.disable(feature, gate, target)
@@ -63,7 +63,7 @@ describe Flipper::Echo do
63
63
  describe '#remove' do
64
64
  it 'removes feature' do
65
65
  expect_any_instance_of(Flipper::Echo::Event).to receive(:notify) do |e|
66
- expect(e.action).to eq(:remove)
66
+ expect(e.action).to eq(:removed)
67
67
  end
68
68
 
69
69
  adapter.remove(feature)
@@ -73,7 +73,7 @@ describe Flipper::Echo do
73
73
  describe '#clear' do
74
74
  it 'clears feature' do
75
75
  expect_any_instance_of(Flipper::Echo::Event).to receive(:notify) do |e|
76
- expect(e.action).to eq(:clear)
76
+ expect(e.action).to eq(:cleared)
77
77
  end
78
78
 
79
79
  adapter.clear(feature)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-echo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Heather Rivers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-13 00:00:00.000000000 Z
11
+ date: 2015-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flipper
@@ -110,10 +110,17 @@ files:
110
110
  - lib/flipper/echo.rb
111
111
  - lib/flipper/echo/configuration.rb
112
112
  - lib/flipper/echo/event.rb
113
+ - lib/flipper/echo/slack.rb
114
+ - lib/flipper/echo/slack/message.rb
115
+ - lib/flipper/echo/slack/notifier.rb
116
+ - lib/flipper/echo/stdout.rb
117
+ - lib/flipper/echo/stdout/notifier.rb
113
118
  - lib/flipper/echo/version.rb
114
119
  - spec/flipper/echo/configuration_spec.rb
115
120
  - spec/flipper/echo/event_spec.rb
116
- - spec/flipper/echo/version_spec.rb
121
+ - spec/flipper/echo/slack/message_spec.rb
122
+ - spec/flipper/echo/slack/notifier_spec.rb
123
+ - spec/flipper/echo/stdout/notifier_spec.rb
117
124
  - spec/flipper/echo_spec.rb
118
125
  - spec/spec_helper.rb
119
126
  - spec/support/adapter_support.rb
@@ -144,7 +151,9 @@ summary: Flipper event notifier
144
151
  test_files:
145
152
  - spec/flipper/echo/configuration_spec.rb
146
153
  - spec/flipper/echo/event_spec.rb
147
- - spec/flipper/echo/version_spec.rb
154
+ - spec/flipper/echo/slack/message_spec.rb
155
+ - spec/flipper/echo/slack/notifier_spec.rb
156
+ - spec/flipper/echo/stdout/notifier_spec.rb
148
157
  - spec/flipper/echo_spec.rb
149
158
  - spec/spec_helper.rb
150
159
  - spec/support/adapter_support.rb
File without changes