superbolt 0.1.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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +266 -0
- data/Rakefile +1 -0
- data/lib/superbolt/adapter/amqp.rb +18 -0
- data/lib/superbolt/adapter/base.rb +28 -0
- data/lib/superbolt/adapter/bunny.rb +16 -0
- data/lib/superbolt/app.rb +73 -0
- data/lib/superbolt/config.rb +30 -0
- data/lib/superbolt/connection/app.rb +21 -0
- data/lib/superbolt/connection/base.rb +40 -0
- data/lib/superbolt/connection/queue.rb +25 -0
- data/lib/superbolt/facade.rb +26 -0
- data/lib/superbolt/incoming_message.rb +26 -0
- data/lib/superbolt/messenger.rb +66 -0
- data/lib/superbolt/processor.rb +33 -0
- data/lib/superbolt/queue.rb +93 -0
- data/lib/superbolt/version.rb +3 -0
- data/lib/superbolt.rb +22 -0
- data/spec/app_spec.rb +98 -0
- data/spec/config_spec.rb +78 -0
- data/spec/connection_spec.rb +21 -0
- data/spec/messenger_spec.rb +77 -0
- data/spec/queue_spec.rb +120 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/superbolt_spec.rb +55 -0
- data/spec/support/queue_helpers.rb +7 -0
- data/superbolt.gemspec +28 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e5ba86e1d694023338deda0d39210951edb45c5b
|
4
|
+
data.tar.gz: 76775a315705a55b7a0e149fc787957800cbc56f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ee50367c87dcf66d69221f20b06aa3d533ffe45b27ddd038d5de41ac00af2c8609ea11c2d54e09cf80c5bacb649dd7877c4bc4043408ad6ec0932bc8688df568
|
7
|
+
data.tar.gz: 07dd4657ad96cf381b7f6e8f107d0c106ae983c274faf7d8324ad4dcb958b593b6e020b1b064607e667b452ad22218271bff451c5b2066698b9c19ec92fb9db4
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 SocialChorus
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
# Superbolt
|
2
|
+
|
3
|
+
Superbolt is an easy intra-app communication system for sending messages
|
4
|
+
between applications. It is backed by RabbitMQ and under the covers it
|
5
|
+
uses both the Bunny gem and the AMQP gem.
|
6
|
+
|
7
|
+
#### Why not just use those gems?
|
8
|
+
|
9
|
+
RabbitMQ is hard. Even though it is a messaging queue, by name, a
|
10
|
+
developer can't pick up one of these great gems and treat the queue
|
11
|
+
like a queue. There is great ceremony in the process. In fact, it is
|
12
|
+
quite easy to pass in the wrong queue arguments, or leave a connection
|
13
|
+
open.
|
14
|
+
|
15
|
+
Superbolt takes the ceremony away and let's developers focus on what is
|
16
|
+
important: reading and sending messages.
|
17
|
+
|
18
|
+
#### How does it make it easier?
|
19
|
+
|
20
|
+
Superbolt has two main components: a queue and an app.
|
21
|
+
|
22
|
+
The queue object acts like a queue. The queue can push, pop and peek.
|
23
|
+
The queue is able to spit out all or a subset of the messages on the
|
24
|
+
queue. It can clear itself. In short it makes inline operations doable.
|
25
|
+
|
26
|
+
The app on the other hand is an EventMachine process that takes over the
|
27
|
+
thread. It continually reads from a single queue until it recieves a
|
28
|
+
signal or message shutting it down. It is smart; it handles exceptions in message
|
29
|
+
processing by moving those problem messages to a related error queue. It also
|
30
|
+
listens on a separate quit queue for a graceful shutdown. A graceful
|
31
|
+
shutdown means no messages are lost.
|
32
|
+
|
33
|
+
#### Simple because there are less RabbitMQ capabilities
|
34
|
+
|
35
|
+
Superbolt makes intra-app communication easier via reducing
|
36
|
+
the types of things that can be done in RabbitMQ. While Superbolt was made to
|
37
|
+
address typical messaging patterns like RPC and work queues, it cuts out
|
38
|
+
RabbitMQ features like exclusive binding, and uses the library itself,
|
39
|
+
and conventions to make sure the right application gets the right
|
40
|
+
message. If all the features of RabbitMQ are needed, then using
|
41
|
+
Superbolt is not appropriate ... hop along.
|
42
|
+
|
43
|
+
#### Conventions
|
44
|
+
|
45
|
+
While Superbolt makes it possible to listen on any queue name
|
46
|
+
as an application, or interact with any queue name as an enumerable-ish queue, it
|
47
|
+
gets its real power from conventions.
|
48
|
+
|
49
|
+
Messages are JSON. Developers can do it differently, but the gains of
|
50
|
+
ease will be lost.
|
51
|
+
|
52
|
+
By convention each application has a name. The name is used along with
|
53
|
+
the application environment in order to communicate. The environment
|
54
|
+
means that test and development messages don't get lumped together. The
|
55
|
+
application name means that each application has to employ some
|
56
|
+
filtering to figure out what end process should handle the messages.
|
57
|
+
|
58
|
+
Per application filtering, is made very easy by Superbolt's message conventions.
|
59
|
+
A Superbolt message has three keys:
|
60
|
+
|
61
|
+
1. origin: the origin application
|
62
|
+
2. event: some identifier/sort key for the message type, will be 'default' by
|
63
|
+
default.
|
64
|
+
3. arguments: additional data to be passed on to the handling process
|
65
|
+
|
66
|
+
Developer's can create a message without having to think to much about
|
67
|
+
these concerns:
|
68
|
+
|
69
|
+
Superbolt.app_name = 'me'
|
70
|
+
Superbolt.message.to('over_there').send!({just: 'do it!'})
|
71
|
+
|
72
|
+
Superbolt.queue('over_there').pop
|
73
|
+
=> {
|
74
|
+
origin: 'me',
|
75
|
+
event: 'default',
|
76
|
+
arguments: {
|
77
|
+
just: 'do it!'
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
## Usage (more)
|
82
|
+
|
83
|
+
###Configuring:
|
84
|
+
|
85
|
+
Superbolt.config = {
|
86
|
+
app_name: 'my_app', # no default
|
87
|
+
env: 'staging', # looks to env for information
|
88
|
+
connection_params: {
|
89
|
+
# can use anything RabbitMQ speaks here
|
90
|
+
host: 'my-rabbitmq-provider.com'
|
91
|
+
} # defaults to what is set in the ENV or localhost
|
92
|
+
}
|
93
|
+
|
94
|
+
#### Connection Configuration
|
95
|
+
|
96
|
+
Out of the box Superbolt will look to the ENV to see if there is a
|
97
|
+
connection key, RABBITMQ\_URL. Developers can customize the connection
|
98
|
+
key that is used. Actual connection params that RabbitMQ Bunny/AMQP use
|
99
|
+
can also be passed in as above. If a connection key is not found in the
|
100
|
+
ENV, localhost will be used. If the application uses these typical
|
101
|
+
conventions, then no connection configuration is required.
|
102
|
+
|
103
|
+
#### App Name
|
104
|
+
|
105
|
+
The application name/identifier is an important default to setup in
|
106
|
+
order to get all the goodness of related to messaging and its
|
107
|
+
conventions.
|
108
|
+
|
109
|
+
If no app name is set up, a littlem or work is required to send a
|
110
|
+
message:
|
111
|
+
|
112
|
+
Superbolt.message
|
113
|
+
.to('over_there')
|
114
|
+
.from('me')
|
115
|
+
.send!({just: 'do it!'})
|
116
|
+
|
117
|
+
Without the #from call, the message will be sent without an origin. In
|
118
|
+
our experience it is typical to process messages differently depending
|
119
|
+
on the sender. Of course, that information can be encoded into the event
|
120
|
+
name, but it is pretty easy to configure an application name so event
|
121
|
+
filtering is easier for the consuming application.
|
122
|
+
|
123
|
+
Superbolt.app_name = 'my_great_app'
|
124
|
+
# - or -
|
125
|
+
Superbolt.config = {
|
126
|
+
app_name = 'my_great_app'
|
127
|
+
}
|
128
|
+
|
129
|
+
### Sending messages
|
130
|
+
|
131
|
+
Superbolt doesn't want to developers worrying about exchanges,
|
132
|
+
durability or connection ceremony. Developers should be able to just
|
133
|
+
send a message. The ease of that sending depends on whether developers
|
134
|
+
are sticking with the Superbolt conventions or not.
|
135
|
+
|
136
|
+
#### The easiest way to message
|
137
|
+
|
138
|
+
The easiest way is to use the Superbolt level helper for sending a
|
139
|
+
message:
|
140
|
+
|
141
|
+
Superbolt.message
|
142
|
+
.re('dorothy')
|
143
|
+
.to('wicked_witch')
|
144
|
+
.send!('On yellow brick road; has friends!')
|
145
|
+
|
146
|
+
This message can be received on a queue
|
147
|
+
'wicked\_witch\_staging', given that the environment is
|
148
|
+
'staging'. When it is popped off it will look like this:
|
149
|
+
|
150
|
+
{
|
151
|
+
origin: 'my_configured_app_name',
|
152
|
+
event: 'dorothy',
|
153
|
+
arguments: 'On yellow brick road; has friends!'
|
154
|
+
}
|
155
|
+
|
156
|
+
#### A more customizable messaging experience
|
157
|
+
|
158
|
+
Messages can also be sent via Superbolt::Queue objects. In this case the
|
159
|
+
message can be anything and the queue name is exactly what is passed in.
|
160
|
+
|
161
|
+
queue = Superbolt::Queue.new('dorothy')
|
162
|
+
queue.push({demand: 'Surrender!'})
|
163
|
+
queue.pop
|
164
|
+
=> {
|
165
|
+
'demand' => 'Surrender!'
|
166
|
+
}
|
167
|
+
|
168
|
+
### Reading messages
|
169
|
+
|
170
|
+
Messages can be read inline or via a standalone app.
|
171
|
+
|
172
|
+
#### Reading Inline
|
173
|
+
|
174
|
+
Reading messages inline is easy and to the point. The Superbolt::Queue
|
175
|
+
object tries to act queue-like instead of like a hard to use external
|
176
|
+
service.
|
177
|
+
|
178
|
+
Popping messages off the queue will remove the message from the queue
|
179
|
+
immediately. If something goes wrong with eth message processing, it is
|
180
|
+
the responsibility of the consuming application to figure out what to
|
181
|
+
do.
|
182
|
+
|
183
|
+
message = queue.pop # This removes the message permanently
|
184
|
+
|
185
|
+
Messages can be read in non-destructive ways as well.
|
186
|
+
|
187
|
+
message = queue.peek
|
188
|
+
# Ponder or process message.
|
189
|
+
# It is still hanging out at the top of the queue.
|
190
|
+
# It can be deleted with a pop,
|
191
|
+
# provided another consumer hasn't already deleted it!
|
192
|
+
|
193
|
+
Because of asynchronicity issues with job processing across several apps
|
194
|
+
or workers, the best usage for non-destructive inline reads is debugging
|
195
|
+
and information gathering.
|
196
|
+
|
197
|
+
queue.all # return all the messages on the queue
|
198
|
+
|
199
|
+
# remove messages meeting the block criteria
|
200
|
+
queue.delete {|m| m['level'] == 'not_important' }
|
201
|
+
|
202
|
+
# peek at messages in a certain range
|
203
|
+
queue.slice(2, 4)
|
204
|
+
|
205
|
+
# get a certain message, non-destructively
|
206
|
+
queue[3]
|
207
|
+
|
208
|
+
#### Reading via a Standalone App
|
209
|
+
|
210
|
+
Reading messages inline is useful, but in general an application wants
|
211
|
+
to read continuously for the latest and greatest intra-app
|
212
|
+
communications.
|
213
|
+
|
214
|
+
Superbolt::App.new('dorothy_inbox').run do |message, logger|
|
215
|
+
HomewardBound.new(message).perform
|
216
|
+
end
|
217
|
+
|
218
|
+
Exceptions raised in the processing block will not exit
|
219
|
+
the Superbolt app. Errors will be logged and the message will be put on
|
220
|
+
an error queue with information about the exception raised. Those
|
221
|
+
messages can be seen by accessing the related error queue:
|
222
|
+
|
223
|
+
error_queue = Superbolt::App.new('dorothy_inbox').error_queue
|
224
|
+
error_queue.all
|
225
|
+
|
226
|
+
The error queue is a Superbolt::Queue and methods like #pop, #delete,
|
227
|
+
and #[] are available to gather more data about the exceptions being
|
228
|
+
raised.
|
229
|
+
|
230
|
+
The app can be shutdown gracefully by sending a quit message to a
|
231
|
+
special queue:
|
232
|
+
|
233
|
+
quit_queue = Superbolt::App.new('dorothy_inbox').quit_queue
|
234
|
+
quit_queue.push(message: 'for a deploy')
|
235
|
+
|
236
|
+
## Installation
|
237
|
+
|
238
|
+
Add this line to your application's Gemfile:
|
239
|
+
|
240
|
+
gem 'superbolt'
|
241
|
+
|
242
|
+
And then execute:
|
243
|
+
|
244
|
+
$ bundle
|
245
|
+
|
246
|
+
Or install it yourself as:
|
247
|
+
|
248
|
+
$ gem install superbolt
|
249
|
+
|
250
|
+
## TODOs:
|
251
|
+
|
252
|
+
* Easy filtering/delegation of messages to classes
|
253
|
+
* Failed messages are put on another queue so that the app is not
|
254
|
+
in a failure loop.
|
255
|
+
* In code YARD stye documentation
|
256
|
+
* CodeClimate
|
257
|
+
* TravisCI continuout integration
|
258
|
+
|
259
|
+
|
260
|
+
## Contributing
|
261
|
+
|
262
|
+
1. Fork it
|
263
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
264
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
265
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
266
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Adapter
|
3
|
+
class AMQP < Base
|
4
|
+
def socket
|
5
|
+
@socket ||= ::AMQP.connect(config.connection_params)
|
6
|
+
end
|
7
|
+
|
8
|
+
def channel
|
9
|
+
@channel ||= ::AMQP::Channel.new(socket)
|
10
|
+
end
|
11
|
+
|
12
|
+
def close(&block)
|
13
|
+
socket.close(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Adapter
|
3
|
+
class Base
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(config=nil)
|
7
|
+
@config = config || Superbolt.config
|
8
|
+
end
|
9
|
+
|
10
|
+
delegate :closed?, :open, :open?,
|
11
|
+
to: :socket
|
12
|
+
|
13
|
+
def close
|
14
|
+
response = socket.close
|
15
|
+
@socket = nil
|
16
|
+
@channel = nil
|
17
|
+
response
|
18
|
+
end
|
19
|
+
|
20
|
+
delegate :queues, :acknowledge, :reject, :queue,
|
21
|
+
to: :channel
|
22
|
+
|
23
|
+
def exchange
|
24
|
+
channel.default_exchange
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Adapter
|
3
|
+
class Bunny < Base
|
4
|
+
def socket
|
5
|
+
return @socket if @socket
|
6
|
+
@socket = ::Bunny.new(config.connection_params)
|
7
|
+
@socket.start
|
8
|
+
@socket
|
9
|
+
end
|
10
|
+
|
11
|
+
def channel
|
12
|
+
@channel ||= socket.create_channel
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Superbolt
|
2
|
+
class App
|
3
|
+
attr_reader :config, :env
|
4
|
+
attr_accessor :logger
|
5
|
+
|
6
|
+
def initialize(name, options)
|
7
|
+
@name = name
|
8
|
+
@env = options[:env]
|
9
|
+
@logger = options[:logger] || Logger.new($stdout)
|
10
|
+
@config = options[:config] || Superbolt.config
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
env ? "#{@name}_#{env}" : @name
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
@connection ||= Connection::App.new(name, config)
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate :close, :closing, :exclusive?, :durable?, :auto_delete?,
|
22
|
+
:writer, :channel, :q,
|
23
|
+
to: :connection
|
24
|
+
|
25
|
+
def queue
|
26
|
+
connection.q
|
27
|
+
end
|
28
|
+
|
29
|
+
def quit_subscriber_queue
|
30
|
+
connection.qq
|
31
|
+
end
|
32
|
+
|
33
|
+
def quit_queue
|
34
|
+
Queue.new("#{connection.name}.quit", connection.config)
|
35
|
+
end
|
36
|
+
|
37
|
+
def error_queue
|
38
|
+
Queue.new("#{connection.name}.error", connection.config)
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(&block)
|
42
|
+
EventMachine.run do
|
43
|
+
queue.subscribe(ack: false) do |metadata, payload|
|
44
|
+
message = IncomingMessage.new(metadata, payload, channel)
|
45
|
+
processor = Processor.new(message, logger, &block)
|
46
|
+
unless processor.perform
|
47
|
+
on_error(message.parse, processor.exception)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
quit_subscriber_queue.subscribe do |message|
|
52
|
+
quit(message)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def on_error(message, error)
|
58
|
+
error_message = message.merge({error: {
|
59
|
+
class: error.class,
|
60
|
+
message: error.message,
|
61
|
+
backtrace: error.backtrace
|
62
|
+
}})
|
63
|
+
error_queue.push(error_message)
|
64
|
+
end
|
65
|
+
|
66
|
+
def quit(message='no message given')
|
67
|
+
logger.info "EXITING Superbolt App listening on queue #{name}: #{message}"
|
68
|
+
close {
|
69
|
+
EventMachine.stop { exit }
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Superbolt
|
2
|
+
class Config
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def connection_params
|
10
|
+
env_params || default
|
11
|
+
end
|
12
|
+
|
13
|
+
def env_connection_key
|
14
|
+
options[:connection_key] || 'RABBITMQ_URL'
|
15
|
+
end
|
16
|
+
|
17
|
+
def env_params
|
18
|
+
ENV[env_connection_key]
|
19
|
+
end
|
20
|
+
|
21
|
+
def default
|
22
|
+
options[:connection_params] || {host: '127.0.0.1'}
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
other.connection_params == connection_params &&
|
27
|
+
other.env_connection_key == env_connection_key
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Connection
|
3
|
+
class App < Base
|
4
|
+
def connection
|
5
|
+
@connection ||= Adapter::AMQP.new(config)
|
6
|
+
end
|
7
|
+
|
8
|
+
def close(&block)
|
9
|
+
connection.close(&block)
|
10
|
+
@connection = nil
|
11
|
+
@q = nil
|
12
|
+
@qq = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def qq
|
16
|
+
@qq ||= connection.queue("#{name}.quit", self.class.default_options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Connection
|
3
|
+
class Base
|
4
|
+
attr_reader :name, :config
|
5
|
+
|
6
|
+
def initialize(name, config=nil)
|
7
|
+
@name = name
|
8
|
+
@config = config || Superbolt.config
|
9
|
+
end
|
10
|
+
|
11
|
+
def connection
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def close
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def q
|
20
|
+
@q ||= connection.queue(name, self.class.default_options)
|
21
|
+
end
|
22
|
+
|
23
|
+
delegate :exclusive?, :durable?, :auto_delete?,
|
24
|
+
to: :q
|
25
|
+
|
26
|
+
def channel
|
27
|
+
connection.channel
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_options
|
31
|
+
{
|
32
|
+
:auto_delete => false,
|
33
|
+
:durable => true,
|
34
|
+
:exclusive => false
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Superbolt
|
2
|
+
module Connection
|
3
|
+
class Queue < Base
|
4
|
+
def connection
|
5
|
+
@connection ||= Adapter::Bunny.new(config)
|
6
|
+
end
|
7
|
+
|
8
|
+
def close
|
9
|
+
connection.close
|
10
|
+
@connection = nil
|
11
|
+
@q = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def closing(&block)
|
15
|
+
response = block.call
|
16
|
+
close
|
17
|
+
response
|
18
|
+
end
|
19
|
+
|
20
|
+
def writer
|
21
|
+
connection.exchange
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Superbolt
|
2
|
+
def self.config=(options)
|
3
|
+
@config = Config.new(options)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.config
|
7
|
+
@config ||= Config.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.queue(name)
|
11
|
+
Superbolt::Queue.new(name, config)
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
attr_writer :env
|
16
|
+
attr_accessor :app_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.env
|
20
|
+
@env || 'development'
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.message(args={})
|
24
|
+
Superbolt::Messenger.new(args)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Superbolt
|
2
|
+
class IncomingMessage
|
3
|
+
attr_reader :payload, :tag, :channel
|
4
|
+
|
5
|
+
def initialize(delivery_info, payload, channel)
|
6
|
+
@payload = payload
|
7
|
+
@tag = delivery_info.delivery_tag
|
8
|
+
@channel = channel
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
JSON.parse(payload)
|
13
|
+
rescue JSON::ParserError
|
14
|
+
payload
|
15
|
+
end
|
16
|
+
|
17
|
+
def reject
|
18
|
+
channel.reject(tag)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ack
|
22
|
+
channel.acknowledge(tag)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Superbolt
|
2
|
+
class Messenger
|
3
|
+
attr_accessor :origin, :name, :event, :arguments, :env
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@name = options.delete(:to)
|
7
|
+
@origin = options.delete(:from) || Superbolt.app_name
|
8
|
+
@event = options.delete(:event) || self.class.defaultevent
|
9
|
+
@env = Superbolt.env
|
10
|
+
@arguments = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
{
|
15
|
+
origin: origin,
|
16
|
+
event: event,
|
17
|
+
arguments: arguments
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
# chainer methods
|
22
|
+
#
|
23
|
+
def to(val)
|
24
|
+
self.name = val
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def from(val)
|
29
|
+
self.origin = val
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def re(val)
|
34
|
+
self.event = val
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def data(val)
|
39
|
+
self.arguments = val
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
# alias
|
44
|
+
# -------
|
45
|
+
|
46
|
+
def send!(args=nil)
|
47
|
+
self.arguments = args if args
|
48
|
+
queue.push(message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def queue
|
52
|
+
unless name
|
53
|
+
raise "no destination app name defined, please pass one in"
|
54
|
+
end
|
55
|
+
Queue.new(destination_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def destination_name
|
59
|
+
"#{name}_#{env}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.defaultevent
|
63
|
+
'default'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|