superbolt 0.6.0 → 0.7.0

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: 2c78aea5b8060d528b43b74ac5c6939e137bad63
4
- data.tar.gz: 9a660f7536bd590f3f2aad70f1bcf16b10a67b2c
3
+ metadata.gz: 7f7e42b8136e680635a7c00c37cb0a4bf1bc9aec
4
+ data.tar.gz: d43dded8b39232b7e9ed7123e1556461417a3db1
5
5
  SHA512:
6
- metadata.gz: 3c06a247acb6b856c9c72078ffab1da9869df256de2eef70ce65b48b981dfd19b80b84d015cc51207b733984f99c7deed41cc8d2f3fb5c1d2c6368aa41f88cfe
7
- data.tar.gz: e33cbf5336606dec0cb52e420b49a00c0d278721ed348e55abefc1ea6b2f08be918122bcac7adaed28cf10bebf1c77eed764b2bf2cb33a057415c806a4502deb
6
+ metadata.gz: ee3084deda5525a9ba5d20fcce3f0781431cf6cf7732556f54850cf38de4ce8cab949803c02519deec0ede961e050b0a490cead8af781facf03c9a5851697947
7
+ data.tar.gz: b78d27807ed2368663ff7253c1d8cbcd051557bf435ea41bce170d326f71cad19688ec97ecd3705f68e7368f090d21977349dd9aae78ff2a0864909d665a54ff
data/README.md CHANGED
@@ -21,14 +21,14 @@ important: reading and sending messages.
21
21
 
22
22
  Superbolt has two main components: a queue and an app.
23
23
 
24
- The queue object acts like a queue. The queue can push, pop and peek.
25
- The queue is able to spit out all or a subset of the messages on the
24
+ The queue object acts like a queue. The queue can push, pop and peek.
25
+ The queue is able to spit out all or a subset of the messages on the
26
26
  queue. It can clear itself. In short it makes inline operations doable.
27
27
 
28
- The app on the other hand is an EventMachine process that takes over the
28
+ The app on the other hand is long running process that takes over the
29
29
  thread. It continually reads from a single queue until it recieves a
30
- signal or message shutting it down. It is smart; it handles exceptions in message
31
- processing by moving those problem messages to a related error queue. It also
30
+ signal shutting it down. It is smart; it is capable of sending exception
31
+ notifications exceptions in message processing to Airbrake. It also
32
32
  listens on a separate quit queue for a graceful shutdown. A graceful
33
33
  shutdown means no messages are lost.
34
34
 
@@ -101,12 +101,12 @@ key that is used. Actual connection params that RabbitMQ Bunny/AMQP use
101
101
  can also be passed in as above. If a connection key is not found in the
102
102
  ENV, localhost will be used. If the application uses these typical
103
103
  conventions, then no connection configuration is required.
104
-
104
+
105
105
  #### App Name
106
106
 
107
107
  The application name/identifier is an important default to setup in
108
108
  order to get all the goodness of related to messaging and its
109
- conventions.
109
+ conventions.
110
110
 
111
111
  If no app name is set up, a littlem or work is required to send a
112
112
  message:
@@ -133,7 +133,7 @@ filtering is easier for the consuming application.
133
133
  Superbolt doesn't want to developers worrying about exchanges,
134
134
  durability or connection ceremony. Developers should be able to just
135
135
  send a message. The ease of that sending depends on whether developers
136
- are sticking with the Superbolt conventions or not.
136
+ are sticking with the Superbolt conventions or not.
137
137
 
138
138
  #### The easiest way to message
139
139
 
@@ -154,7 +154,7 @@ This message can be received on a queue
154
154
  event: 'dorothy',
155
155
  arguments: 'On yellow brick road; has friends!'
156
156
  }
157
-
157
+
158
158
  #### A more customizable messaging experience
159
159
 
160
160
  Messages can also be sent via Superbolt::Queue objects. In this case the
@@ -162,20 +162,20 @@ message can be anything and the queue name is exactly what is passed in.
162
162
 
163
163
  queue = Superbolt::Queue.new('dorothy')
164
164
  queue.push({demand: 'Surrender!'})
165
- queue.pop
165
+ queue.pop
166
166
  => {
167
167
  'demand' => 'Surrender!'
168
168
  }
169
169
 
170
170
  ### Reading messages
171
171
 
172
- Messages can be read inline or via a standalone app.
172
+ Messages can be read inline or via a standalone app.
173
173
 
174
174
  #### Reading Inline
175
175
 
176
176
  Reading messages inline is easy and to the point. The Superbolt::Queue
177
177
  object tries to act queue-like instead of like a hard to use external
178
- service.
178
+ service.
179
179
 
180
180
  Popping messages off the queue will remove the message from the queue
181
181
  immediately. If something goes wrong with eth message processing, it is
@@ -184,12 +184,12 @@ do.
184
184
 
185
185
  message = queue.pop # This removes the message permanently
186
186
 
187
- Messages can be read in non-destructive ways as well.
187
+ Messages can be read in non-destructive ways as well.
188
188
 
189
189
  message = queue.peek
190
190
  # Ponder or process message.
191
191
  # It is still hanging out at the top of the queue.
192
- # It can be deleted with a pop,
192
+ # It can be deleted with a pop,
193
193
  # provided another consumer hasn't already deleted it!
194
194
 
195
195
  Because of asynchronicity issues with job processing across several apps
@@ -202,7 +202,7 @@ and information gathering.
202
202
  queue.delete {|m| m['level'] == 'not_important' }
203
203
 
204
204
  # peek at messages in a certain range
205
- queue.slice(2, 4)
205
+ queue.slice(2, 4)
206
206
 
207
207
  # get a certain message, non-destructively
208
208
  queue[3]
@@ -218,16 +218,9 @@ communications.
218
218
  end
219
219
 
220
220
  Exceptions raised in the processing block will not exit
221
- the Superbolt app. Errors will be logged and the message will be put on
222
- an error queue with information about the exception raised. Those
223
- messages can be seen by accessing the related error queue:
224
-
225
- error_queue = Superbolt::App.new('dorothy_inbox').error_queue
226
- error_queue.all
227
-
228
- The error queue is a Superbolt::Queue and methods like #pop, #delete,
229
- and #[] are available to gather more data about the exceptions being
230
- raised.
221
+ the Superbolt app. Errors will be logged and the notification will be sent
222
+ to the error notifier (none by default, can be airbrake) with information
223
+ about the exception raised.
231
224
 
232
225
  The app can be shutdown gracefully by sending a quit message to a
233
226
  special queue:
@@ -235,6 +228,19 @@ special queue:
235
228
  quit_queue = Superbolt::App.new('dorothy_inbox').quit_queue
236
229
  quit_queue.push(message: 'for a deploy')
237
230
 
231
+ ## Error reporting
232
+
233
+ `Superbolt::App` can hook into Airbrake by your command:
234
+
235
+ Superbolt.error_notifier = :airbrake
236
+
237
+ Superbolt::App.new('dorothy_inbox') do
238
+ do_unsafe_stuff
239
+ end
240
+
241
+ Note that Superbolt does not have [the Airbrake gem](https://github.com/airbrake/airbrake)
242
+ amongst it's dependencies, so it is up to you to add it to your project.
243
+
238
244
  ## Installation
239
245
 
240
246
  Add this line to your application's Gemfile:
data/lib/superbolt.rb CHANGED
@@ -11,6 +11,9 @@ require 'active_support/inflector'
11
11
  require "superbolt/version"
12
12
  require "superbolt/config"
13
13
 
14
+ require "superbolt/error_notifier/airbrake"
15
+ require "superbolt/error_notifier/none"
16
+
14
17
  require "superbolt/adapter/base"
15
18
  require "superbolt/adapter/bunny"
16
19
  require "superbolt/adapter/amqp"
data/lib/superbolt/app.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  module Superbolt
2
2
  class App
3
- attr_reader :config, :env, :runner_type
3
+ attr_reader :config, :env, :runner_type, :error_notifier_type
4
4
  attr_accessor :logger
5
5
 
6
6
  def initialize(name, options={})
7
- @name = name
8
- @env = options[:env] || Superbolt.env
9
- @logger = options[:logger] || Logger.new($stdout)
10
- @config = options[:config] || Superbolt.config
11
- @runner_type = options[:runner] || config.runner || :default
7
+ @name = name
8
+ @env = options[:env] || Superbolt.env
9
+ @logger = options[:logger] || Logger.new($stdout)
10
+ @config = options[:config] || Superbolt.config
11
+ @runner_type = options[:runner] || config.runner || :default
12
+ @error_notifier_type = options[:error_notifier] || Superbolt.error_notifier
12
13
  end
13
14
 
14
15
  def name
@@ -20,10 +21,6 @@ module Superbolt
20
21
  Queue.new("#{connection.name}.quit", connection.config)
21
22
  end
22
23
 
23
- def error_queue
24
- Queue.new("#{connection.name}.error", connection.config)
25
- end
26
-
27
24
  def connection
28
25
  @connection ||= Connection::Queue.new(name, config)
29
26
  end
@@ -41,7 +38,7 @@ module Superbolt
41
38
  end
42
39
 
43
40
  def run(&block)
44
- @consumer = runner_class.new(queue, error_queue, logger, block).run
41
+ runner_class.new(queue, error_notifier, logger, block).run
45
42
  # quit_subscriber_queue.subscribe do |message|
46
43
  # (message)
47
44
  # end
@@ -65,9 +62,27 @@ module Superbolt
65
62
  runner_map[:ack_one]
66
63
  end
67
64
 
65
+ def error_notifier
66
+ @error_notifier ||= error_notifier_class.new(logger)
67
+ end
68
+
69
+ def error_notifier_class
70
+ error_notifier_map[error_notifier_type] || default_error_notifier
71
+ end
72
+
73
+ def error_notifier_map
74
+ {
75
+ airbrake: ErrorNotifier::Airbrake,
76
+ none: ErrorNotifier::None
77
+ }
78
+ end
79
+
80
+ def default_error_notifier
81
+ error_notifier_map[:none]
82
+ end
83
+
68
84
  def quit(message='no message given')
69
85
  logger.info "EXITING Superbolt App listening on queue #{name}: #{message}"
70
- q.channel.consumers.first[0]
71
86
  q.channel.basic_cancel q.channel.consumers.first[0]
72
87
  close
73
88
  end
@@ -27,6 +27,10 @@ module Superbolt
27
27
  options[:runner]
28
28
  end
29
29
 
30
+ def error_notifier
31
+ options[:error_notifier]
32
+ end
33
+
30
34
  def env_params
31
35
  ENV[env_connection_key]
32
36
  end
@@ -0,0 +1,17 @@
1
+ module Superbolt
2
+ module ErrorNotifier
3
+ class Airbrake
4
+ def initialize(logger)
5
+ @logger = logger
6
+ end
7
+
8
+ def error!(exception, superbolt_message = nil)
9
+ if defined? ::Airbrake
10
+ ::Airbrake.notify_or_ignore(exception, parameters: superbolt_message)
11
+ else
12
+ @logger.warn("You have configured Superbolt to send errors to Airbrake, but Airbrake is not available or is not configured!")
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Superbolt
2
+ module ErrorNotifier
3
+ class None
4
+ def initialize(*); end
5
+ def error!(*); end
6
+ end
7
+ end
8
+ end
9
+
@@ -1,9 +1,10 @@
1
1
  module Superbolt
2
-
2
+
3
3
  def self.config=(options)
4
4
  @config = Config.new({
5
5
  env: env,
6
6
  app_name: app_name,
7
+ error_notifier: error_notifier,
7
8
  file_matcher: file_matcher
8
9
  }.merge(options))
9
10
  end
@@ -17,7 +18,7 @@ module Superbolt
17
18
  end
18
19
 
19
20
  class << self
20
- attr_writer :env, :file_matcher
21
+ attr_writer :env, :file_matcher, :error_notifier
21
22
  attr_accessor :app_name
22
23
  end
23
24
 
@@ -29,6 +30,10 @@ module Superbolt
29
30
  @file_matcher || /_file$/
30
31
  end
31
32
 
33
+ def self.error_notifier
34
+ @error_notifier || :none
35
+ end
36
+
32
37
  def self.message(args={})
33
38
  Superbolt::Messenger.new(args)
34
39
  end
@@ -29,4 +29,4 @@ module Superbolt
29
29
  hash
30
30
  end
31
31
  end
32
- end
32
+ end
@@ -1,11 +1,11 @@
1
1
  module Superbolt
2
2
  module Runner
3
3
  class Base
4
- attr_reader :queue, :error_queue, :logger, :block
4
+ attr_reader :queue, :error_notifier, :logger, :block
5
5
 
6
- def initialize(queue, error_queue, logger, block)
6
+ def initialize(queue, error_notifier, logger, block)
7
7
  @queue = queue
8
- @error_queue = error_queue
8
+ @error_notifier = error_notifier
9
9
  @logger = logger
10
10
  @block = block
11
11
  end
@@ -15,13 +15,7 @@ module Superbolt
15
15
  end
16
16
 
17
17
  def on_error(message, error)
18
- error_message = message.merge({error: {
19
- class: error.class,
20
- message: error.message,
21
- backtrace: error.backtrace,
22
- errored_at: Time.now
23
- }})
24
- error_queue.push(error_message)
18
+ error_notifier.error!(error, message)
25
19
  end
26
20
  end
27
21
  end
@@ -14,10 +14,11 @@ module Superbolt
14
14
  queue.subscribe(ack: ack, block: true) do |delivery_info, metadata, payload|
15
15
  message = Superbolt::IncomingMessage.new(delivery_info, payload, channel)
16
16
  processor = processor_class.new(message, logger, &block)
17
- processor.perform
18
- # unless processor.perform
19
- # on_error(message.parse, processor.exception)
20
- # end
17
+ success = processor.perform
18
+
19
+ unless success
20
+ on_error(message.parse, processor.exception)
21
+ end
21
22
 
22
23
  message.ack if ack
23
24
  end
@@ -1,3 +1,3 @@
1
1
  module Superbolt
2
- VERSION = "0.6.0" #possibly broken for file transfer :(
2
+ VERSION = "0.7.0" #possibly broken for file transfer :(
3
3
  end
data/spec/app_spec.rb CHANGED
@@ -62,6 +62,7 @@ describe Superbolt::App do
62
62
  it 'removes messages from the queue on successful completion' do
63
63
  queue.push({first: 1})
64
64
  queue.push({last: 2})
65
+ messages = []
65
66
 
66
67
  app.run do |message, logger|
67
68
  messages << message
@@ -94,16 +95,44 @@ describe Superbolt::App do
94
95
  message_received.should be_true
95
96
  end
96
97
 
97
- it "moves the message to an error queue if an exception is raised" do
98
- pending "we will probably delete this test in favor of airbrake"
99
- queue.push({oh: 'noes'})
98
+ context 'notifying errors' do
99
+ let(:message) { { 'oh' => 'noes' } }
100
+ let(:the_error) { RuntimeError.new('something went wrong') }
101
+
102
+ context 'airbrake' do
103
+ before { Superbolt.error_notifier = :airbrake }
104
+ after { Superbolt.error_notifier = nil }
105
+
106
+ it 'uses ErrorNotifiers::Airbrake' do
107
+ app.error_notifier.should be_an_instance_of(Superbolt::ErrorNotifier::Airbrake)
108
+ app.error_notifier.should_receive(:error!).with(the_error, message)
109
+ messages = []
100
110
 
101
- app.run do |message|
102
- # quit_queue.push({message: 'halt thyself'})
103
- raise "something went wrong"
111
+ queue.push(message)
112
+ queue.push(message)
113
+
114
+ app.run do |message|
115
+ messages << message
116
+ messages.length > 1 ? app.quit : raise(the_error)
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'default' do
122
+ it 'does not fail' do
123
+ app.error_notifier.should be_an_instance_of(Superbolt::ErrorNotifier::None)
124
+ app.error_notifier.should_receive(:error!).with(the_error, message)
125
+ messages = []
126
+
127
+ queue.push(message)
128
+ queue.push(message)
129
+
130
+ app.run do |message|
131
+ messages << message
132
+ messages.length > 1 ? app.quit : raise(the_error)
133
+ end
134
+ end
104
135
  end
105
- queue.size.should == 0
106
- error_queue.size.should == 1
107
136
  end
108
137
  end
109
138
 
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ describe Superbolt::ErrorNotifier::Airbrake do
5
+ subject(:notifier) { described_class.new(logger) }
6
+
7
+ let(:logger) { Logger.new(out) }
8
+ let(:out) { StringIO.new('') }
9
+
10
+ let(:error) { RuntimeError.new('ouch!') }
11
+ let(:message) { { the_superbolt: 'message' } }
12
+
13
+ context 'when the app has Airbrake installed' do
14
+ before do
15
+ stub_const("::Airbrake", double(:Airbrake, notify_or_ignore: nil))
16
+ end
17
+
18
+ it 'logs an error to Airbrake when there was one' do
19
+ expect(Airbrake).to receive(:notify_or_ignore).with(error, parameters: message)
20
+ notifier.error!(error, message)
21
+ end
22
+
23
+ it 'can live without the message' do
24
+ expect(Airbrake).to receive(:notify_or_ignore).with(error, parameters: nil)
25
+ notifier.error!(error)
26
+ end
27
+ end
28
+
29
+ context 'when Airbrake is not there' do
30
+ it 'will not break' do
31
+ expect(defined? Airbrake).to be_nil
32
+ notifier.error!(error, message)
33
+ # no errors
34
+ end
35
+
36
+ it 'will log a warning' do
37
+ notifier.error!(error, message)
38
+ expect(out.string).to include('Airbrake is not available or is not configured!')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe Superbolt::ErrorNotifier::None do
4
+ subject(:notifier) { described_class.new(double(:Logger)) }
5
+
6
+ let(:error) { RuntimeError.new('ouch!') }
7
+ let(:message) { { the_superbolt: 'message' } }
8
+
9
+ it 'does not fail' do
10
+ notifier.error!(error)
11
+ notifier.error!(error, message)
12
+ end
13
+ end
14
+
@@ -28,7 +28,7 @@ module PG
28
28
  end
29
29
 
30
30
  describe "Superbolt::Runner::Pg" do
31
- let(:runner) { Superbolt::Runner::Pg.new('berkin', double('error', push: true), 'whatever', 'some block') }
31
+ let(:runner) { Superbolt::Runner::Pg.new('berkin', Superbolt::ErrorNotifier::None.new, 'whatever', 'some block') }
32
32
  let(:connection) { double('connection', reset!: true) }
33
33
 
34
34
  before do
@@ -6,20 +6,24 @@ describe Superbolt, 'the facade' do
6
6
  @config = nil
7
7
  end
8
8
  end
9
-
9
+
10
10
  describe 'total configuration' do
11
11
  before do
12
12
  Superbolt.app_name = 'bossanova'
13
13
  Superbolt.env = 'production'
14
+ Superbolt.error_notifier = :airbrake
14
15
  Superbolt.config = {
15
16
  connection_key: 'SOME_RABBITMQ_URL'
16
17
  }
17
18
  end
18
19
 
20
+ after { Superbolt.error_notifier = nil }
21
+
19
22
  it "should retain the configuration information" do
20
23
  Superbolt.config.app_name.should == 'bossanova'
21
24
  Superbolt.config.env.should == 'production'
22
25
  Superbolt.config.env_connection_key.should == 'SOME_RABBITMQ_URL'
26
+ Superbolt.config.error_notifier.should == :airbrake
23
27
  end
24
28
  end
25
29
 
data/superbolt.gemspec CHANGED
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_development_dependency "bundler", "~> 1.3"
28
28
  spec.add_development_dependency "rake"
29
- spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "rspec", "= 2.14.1"
30
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: superbolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - socialchorus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-19 00:00:00.000000000 Z
11
+ date: 2014-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -112,16 +112,16 @@ dependencies:
112
112
  name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - '>='
115
+ - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: 2.14.1
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: 2.14.1
125
125
  description: Superbolt is comprised of a standalone app, and a queue-like queue for
126
126
  sending messages between services and applications.
127
127
  email:
@@ -144,6 +144,8 @@ files:
144
144
  - lib/superbolt/connection/app.rb
145
145
  - lib/superbolt/connection/base.rb
146
146
  - lib/superbolt/connection/queue.rb
147
+ - lib/superbolt/error_notifier/airbrake.rb
148
+ - lib/superbolt/error_notifier/none.rb
147
149
  - lib/superbolt/facade.rb
148
150
  - lib/superbolt/file_manager.rb
149
151
  - lib/superbolt/file_packer.rb
@@ -171,6 +173,8 @@ files:
171
173
  - spec/file_packer_spec.rb
172
174
  - spec/file_unpacker_spec.rb
173
175
  - spec/incoming_message_spec.rb
176
+ - spec/lib/superbolt/error_notifier/airbrake_spec.rb
177
+ - spec/lib/superbolt/error_notifier/none_spec.rb
174
178
  - spec/lib/superbolt/runner/pg_spec.rb
175
179
  - spec/messenger_spec.rb
176
180
  - spec/queue_spec.rb
@@ -212,6 +216,8 @@ test_files:
212
216
  - spec/file_packer_spec.rb
213
217
  - spec/file_unpacker_spec.rb
214
218
  - spec/incoming_message_spec.rb
219
+ - spec/lib/superbolt/error_notifier/airbrake_spec.rb
220
+ - spec/lib/superbolt/error_notifier/none_spec.rb
215
221
  - spec/lib/superbolt/runner/pg_spec.rb
216
222
  - spec/messenger_spec.rb
217
223
  - spec/queue_spec.rb