brightbox-warren 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/LICENCE +11 -0
- data/Manifest +25 -0
- data/Rakefile +23 -0
- data/examples/authed/receiver.rb +23 -0
- data/examples/authed/secret.rb +1 -0
- data/examples/authed/sender.rb +38 -0
- data/examples/simple/mass_sender.rb +21 -0
- data/examples/simple/receiver.rb +17 -0
- data/examples/simple/sender.rb +33 -0
- data/lib/warren/connection.rb +37 -0
- data/lib/warren/message_filter.rb +70 -0
- data/lib/warren/message_filters/shared_secret.rb +70 -0
- data/lib/warren/message_filters/yaml.rb +20 -0
- data/lib/warren/queue.rb +85 -0
- data/lib/warren.rb +24 -0
- data/readme.rdoc +19 -0
- data/spec/hash_extend.rb +9 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/warren/connection_spec.rb +70 -0
- data/spec/warren/message_filter_spec.rb +84 -0
- data/spec/warren/queue_spec.rb +93 -0
- data/tasks/rdoc.rake +7 -0
- data/tasks/rspec.rake +24 -0
- data/warren.gemspec +34 -0
- metadata +100 -0
data/CHANGELOG
ADDED
data/LICENCE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Released under the MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2008 Brightbox Systems Ltd
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
10
|
+
|
11
|
+
See http://www.brightbox.co.uk/ for contact details.
|
data/Manifest
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
examples/authed/receiver.rb
|
3
|
+
examples/authed/secret.rb
|
4
|
+
examples/authed/sender.rb
|
5
|
+
examples/simple/mass_sender.rb
|
6
|
+
examples/simple/receiver.rb
|
7
|
+
examples/simple/sender.rb
|
8
|
+
lib/warren/connection.rb
|
9
|
+
lib/warren/message_filter.rb
|
10
|
+
lib/warren/message_filters/shared_secret.rb
|
11
|
+
lib/warren/message_filters/yaml.rb
|
12
|
+
lib/warren/queue.rb
|
13
|
+
lib/warren.rb
|
14
|
+
LICENCE
|
15
|
+
Manifest
|
16
|
+
Rakefile
|
17
|
+
readme.rdoc
|
18
|
+
spec/hash_extend.rb
|
19
|
+
spec/spec.opts
|
20
|
+
spec/spec_helper.rb
|
21
|
+
spec/warren/connection_spec.rb
|
22
|
+
spec/warren/message_filter_spec.rb
|
23
|
+
spec/warren/queue_spec.rb
|
24
|
+
tasks/rdoc.rake
|
25
|
+
tasks/rspec.rake
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
# Load in external rakefiles
|
5
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each do | rake_file |
|
6
|
+
load rake_file
|
7
|
+
end
|
8
|
+
|
9
|
+
# Gem stuff
|
10
|
+
require 'echoe'
|
11
|
+
Echoe.new('warren') do | gem |
|
12
|
+
gem.author = ["Caius Durling", "David Smalley"]
|
13
|
+
gem.email = 'support@brightbox.co.uk'
|
14
|
+
gem.summary = 'Library for pushing messages onto and off RabbitMQ queues'
|
15
|
+
gem.url = 'http://github.com/brightbox/warren'
|
16
|
+
gem.dependencies = [["amqp", '>= 0.6.0']]
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Generates the manifest and the gemspec"
|
20
|
+
task :build => [:manifest, :build_gemspec] do
|
21
|
+
puts "Built!"
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../lib/warren")
|
3
|
+
|
4
|
+
Signal.trap("INT") { AMQP.stop { EM.stop } }
|
5
|
+
Signal.trap("TERM") { AMQP.stop { EM.stop } }
|
6
|
+
|
7
|
+
# Listen to the main queue
|
8
|
+
q = "main"
|
9
|
+
puts "Listening to the #{q} queue."
|
10
|
+
|
11
|
+
# Setup the connection directly this time
|
12
|
+
Warren::Queue.connection = {:user => "caius", :pass => "caius", :vhost => "/"}
|
13
|
+
|
14
|
+
# Set the secret key
|
15
|
+
require File.expand_path(File.dirname(__FILE__) + "/secret")
|
16
|
+
Warren::MessageFilter::SharedSecret.key = SUPER_SECRET_KEY
|
17
|
+
# And add the filter
|
18
|
+
Warren::MessageFilter << Warren::MessageFilter::SharedSecret
|
19
|
+
|
20
|
+
# And attach a block for new messages to fire
|
21
|
+
Warren::Queue.subscribe(q) do |msg|
|
22
|
+
p [Time.now, msg]
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
SUPER_SECRET_KEY = "This is my super secret string"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../lib/warren")
|
3
|
+
|
4
|
+
Signal.trap("INT") { exit! }
|
5
|
+
Signal.trap("TERM") { exit! }
|
6
|
+
|
7
|
+
# Setup our own connection before generating the queue object
|
8
|
+
conn = Warren::Connection.new(
|
9
|
+
:user => "caius",
|
10
|
+
:pass => "caius",
|
11
|
+
:vhost => "/",
|
12
|
+
:default_queue => "main"
|
13
|
+
)
|
14
|
+
# Set the connection for the queue
|
15
|
+
Warren::Queue.connection = conn
|
16
|
+
# Generate some data to send
|
17
|
+
data = {
|
18
|
+
:people => [
|
19
|
+
:fred => {
|
20
|
+
:age => 25,
|
21
|
+
:location => "Leeds"
|
22
|
+
},
|
23
|
+
:george => {
|
24
|
+
:age => 32,
|
25
|
+
:location => "London"
|
26
|
+
}
|
27
|
+
]
|
28
|
+
}
|
29
|
+
|
30
|
+
# Set the secret key
|
31
|
+
require File.expand_path(File.dirname(__FILE__) + "/secret")
|
32
|
+
Warren::MessageFilter::SharedSecret.key = SUPER_SECRET_KEY
|
33
|
+
|
34
|
+
# And add the filter
|
35
|
+
Warren::MessageFilter << Warren::MessageFilter::SharedSecret
|
36
|
+
|
37
|
+
# Push a message onto the queue
|
38
|
+
p Warren::Queue.publish(:default, data )
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/warren")
|
3
|
+
|
4
|
+
Signal.trap("INT") { exit! }
|
5
|
+
Signal.trap("TERM") { exit! }
|
6
|
+
|
7
|
+
# Setup our own connection before generating the queue object
|
8
|
+
conn = Warren::Connection.new({
|
9
|
+
:user => "caius",
|
10
|
+
:pass => "caius",
|
11
|
+
:vhost => "/",
|
12
|
+
:default_queue => "main"
|
13
|
+
})
|
14
|
+
# Set the connection for the queue
|
15
|
+
Warren::Queue.connection = conn
|
16
|
+
|
17
|
+
1000.times do | i |
|
18
|
+
puts i
|
19
|
+
sleep 0.1
|
20
|
+
Warren::Queue.publish(:default, "Message no #{i}") { puts "sent ##{i}" }
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/warren")
|
3
|
+
|
4
|
+
Signal.trap("INT") { AMQP.stop { EM.stop } }
|
5
|
+
Signal.trap("TERM") { AMQP.stop { EM.stop } }
|
6
|
+
|
7
|
+
# Listen to the main queue
|
8
|
+
q = "main"
|
9
|
+
puts "Listening to the #{q} queue."
|
10
|
+
|
11
|
+
# Setup the connection directly this time
|
12
|
+
Warren::Queue.connection = {:user => "caius", :pass => "caius", :vhost => "/"}
|
13
|
+
|
14
|
+
# And attach a block for new messages to fire
|
15
|
+
Warren::Queue.subscribe(q) do |msg|
|
16
|
+
p [Time.now, msg]
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/warren")
|
3
|
+
|
4
|
+
Signal.trap("INT") { exit! }
|
5
|
+
Signal.trap("TERM") { exit! }
|
6
|
+
|
7
|
+
# Setup our own connection before generating the queue object
|
8
|
+
conn = Warren::Connection.new(
|
9
|
+
:user => "caius",
|
10
|
+
:pass => "caius",
|
11
|
+
:vhost => "/",
|
12
|
+
:default_queue => "main"
|
13
|
+
)
|
14
|
+
# Set the connection for the queue
|
15
|
+
Warren::Queue.connection = conn
|
16
|
+
# Generate some data to send
|
17
|
+
data = {
|
18
|
+
:people => [
|
19
|
+
:fred => {
|
20
|
+
:age => 25,
|
21
|
+
:location => "Leeds"
|
22
|
+
},
|
23
|
+
:george => {
|
24
|
+
:age => 32,
|
25
|
+
:location => "London"
|
26
|
+
}
|
27
|
+
]
|
28
|
+
}
|
29
|
+
# Push a message onto the queue
|
30
|
+
p Warren::Queue.publish(:default, data )
|
31
|
+
|
32
|
+
# And then push a message onto the queue, returning "foo"
|
33
|
+
p Warren::Queue.publish(:default, data) { "foo" }
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Warren::Connection
|
2
|
+
|
3
|
+
# Creates a new connection with the options passed in.
|
4
|
+
# Requires at least a :user, :pass and :vhost else will raise
|
5
|
+
# InvalidConnectionDetails.
|
6
|
+
def initialize opts = {}
|
7
|
+
# Check they've passed in the stuff without a default on it
|
8
|
+
unless opts.has_key?(:user) && opts.has_key?(:pass) && opts.has_key?(:vhost)
|
9
|
+
raise InvalidConnectionDetails, "Missing a username, password or vhost."
|
10
|
+
end
|
11
|
+
@opts = opts
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the default queue name or returns InvalidConnectionDetails
|
15
|
+
# if no default queue is defined
|
16
|
+
def queue_name
|
17
|
+
raise InvalidConnectionDetails, "Missing a default queue name." unless @opts.has_key?(:default_queue)
|
18
|
+
@opts[:default_queue]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a hash of the connection options
|
22
|
+
def options
|
23
|
+
{
|
24
|
+
:user => @opts[:user],
|
25
|
+
:pass => @opts[:pass],
|
26
|
+
:vhost => @opts[:vhost],
|
27
|
+
:host => (@opts[:host] || "localhost"),
|
28
|
+
:port => (@opts[:port] || ::AMQP::PORT.to_i),
|
29
|
+
:logging => (@opts[:logging] || false)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Raised if connection details are missing or invalid
|
34
|
+
# Check the error message for more details
|
35
|
+
class InvalidConnectionDetails < Exception
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/message_filters/yaml")
|
2
|
+
|
3
|
+
module Warren
|
4
|
+
# Handles filtering messages going onto/coming off the queue
|
5
|
+
class MessageFilter
|
6
|
+
# Array of filters to be run on the message before its
|
7
|
+
# pushed to rabbit.
|
8
|
+
#
|
9
|
+
# NB: These get called in reverse order from the array -
|
10
|
+
# the last filter to be added gets called first.
|
11
|
+
@@filters = [Warren::MessageFilter::Yaml]
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Adds a filter to the list
|
15
|
+
#
|
16
|
+
# A valid filter is just a class that defines
|
17
|
+
# <tt>self.pack</tt> and <tt>self.unpack</tt>
|
18
|
+
# methods, which both accept a single argument,
|
19
|
+
# act upon it, and return the output.
|
20
|
+
#
|
21
|
+
# Example filter class (See also message_filters/*.rb)
|
22
|
+
#
|
23
|
+
# class Foo
|
24
|
+
# def self.pack msg
|
25
|
+
# msg.reverse # Assumes msg responds to reverse
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def self.unpack msg
|
29
|
+
# msg.reverse # Does the opposite of Foo#pack
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
def << filter
|
34
|
+
@@filters << filter
|
35
|
+
end
|
36
|
+
alias :add_filter :<<
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns current array of filters
|
40
|
+
def self.filters
|
41
|
+
@@filters
|
42
|
+
end
|
43
|
+
|
44
|
+
# Resets the filters to default
|
45
|
+
def self.reset_filters
|
46
|
+
@@filters = [Warren::MessageFilter::Yaml]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Runs the raw message through all the filters
|
50
|
+
# and returns the filtered version
|
51
|
+
def self.pack msg
|
52
|
+
@@filters.reverse.each do |f|
|
53
|
+
# puts "Packing with #{f}"
|
54
|
+
msg = f.send(:pack, msg)
|
55
|
+
end
|
56
|
+
msg
|
57
|
+
end
|
58
|
+
|
59
|
+
# Runs the filtered message through all the
|
60
|
+
# filters and returns the raw version
|
61
|
+
def self.unpack msg
|
62
|
+
@@filters.each do |f|
|
63
|
+
# puts "Unpacking with #{f}"
|
64
|
+
msg = f.unpack(msg)
|
65
|
+
end
|
66
|
+
msg
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
begin
|
2
|
+
require "hmac-sha2"
|
3
|
+
rescue LoadError => e
|
4
|
+
puts "Error loading the `ruby-hmac` gem."
|
5
|
+
exit!
|
6
|
+
end
|
7
|
+
|
8
|
+
module Warren
|
9
|
+
class MessageFilter
|
10
|
+
# Hashes the message using a secret salt, stores the hash
|
11
|
+
# in the message and then checks its the same when pulled
|
12
|
+
# off the other end.
|
13
|
+
#
|
14
|
+
# Basic trust implementation to make sure the message
|
15
|
+
# hasn't been tampered with in transit and came from
|
16
|
+
# an "authorised" app.
|
17
|
+
#
|
18
|
+
# Make sure both the publisher and subscriber use the same
|
19
|
+
# key else you'll get KeyValidationError error raised.
|
20
|
+
#
|
21
|
+
class SharedSecret
|
22
|
+
# Raised when no key (salt) is provided
|
23
|
+
class NoKeyError < Exception; end
|
24
|
+
# Raised when there is a key mismatch error
|
25
|
+
class KeyValidationError < Exception; end
|
26
|
+
|
27
|
+
# Sets the key to use
|
28
|
+
def self.key= key
|
29
|
+
@@key = key
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns the current key
|
33
|
+
# Raises NoKeyError if no key has been assigned yet
|
34
|
+
def self.key
|
35
|
+
raise NoKeyError if @@key.nil?
|
36
|
+
@@key
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the hashed message
|
40
|
+
#
|
41
|
+
# Expects that msg#to_s returns a string
|
42
|
+
# to hash against.
|
43
|
+
#
|
44
|
+
def self.secret msg
|
45
|
+
HMAC::SHA256.hexdigest(self.key, msg.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Called when the message is being packed for
|
49
|
+
# transit. Returns a hash.
|
50
|
+
def self.pack msg
|
51
|
+
# Make sure its a hash
|
52
|
+
msg = {:secret_msg => msg} unless msg.is_a? Hash
|
53
|
+
# And add our secret into the hash
|
54
|
+
msg[:secret] = self.secret(msg.to_s)
|
55
|
+
msg
|
56
|
+
end
|
57
|
+
|
58
|
+
# Called when unpacking the message from transit.
|
59
|
+
# Returns the original object.
|
60
|
+
def self.unpack msg
|
61
|
+
# Check the secret exists in the msg and matches the secret_string
|
62
|
+
raise KeyValidationError unless msg.delete(:secret) == self.secret(msg)
|
63
|
+
# see if its a hash we created, it'll only contain the key "secret_msg" if it is
|
64
|
+
msg = msg[:secret_msg] if msg.keys == [:secret_msg]
|
65
|
+
msg
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Warren
|
4
|
+
class MessageFilter
|
5
|
+
# Packs the message into a YAML string
|
6
|
+
# for transferring safely across the wire
|
7
|
+
class Yaml
|
8
|
+
|
9
|
+
# Returns a YAML string
|
10
|
+
def self.pack msg
|
11
|
+
YAML.dump(msg)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns original message
|
15
|
+
def self.unpack msg
|
16
|
+
YAML.load(msg)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/warren/queue.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
class Warren::Queue
|
2
|
+
@@connection = nil
|
3
|
+
|
4
|
+
# Raised if no connection has been defined yet.
|
5
|
+
class NoConnectionDetails < Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
# Raised if a block is expected by the method but none is given.
|
9
|
+
class NoBlockGiven < Exception
|
10
|
+
end
|
11
|
+
|
12
|
+
# Sets the connection details
|
13
|
+
def self.connection= params
|
14
|
+
@@connection = params.is_a?(Warren::Connection) ? params : Warren::Connection.new(params)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the current connection details
|
18
|
+
# Raises NoConnectionDetails if no connection details have been
|
19
|
+
# assigned yet.
|
20
|
+
def self.connection
|
21
|
+
if @@connection.nil?
|
22
|
+
raise NoConnectionDetails, "You need to set the connection details."
|
23
|
+
end
|
24
|
+
@@connection
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Sends a message to a queue. If successfully sent it returns
|
29
|
+
# true, unless callback block is passed (see below)
|
30
|
+
#
|
31
|
+
# Warren::Queue.publish(:queue_name, {:foo => "name"})
|
32
|
+
#
|
33
|
+
# Can also pass a block which is fired after the message
|
34
|
+
# is sent. If a block is passed, then the return value of the block
|
35
|
+
# is returned from this method.
|
36
|
+
#
|
37
|
+
# Warren::Queue.publish(:queue_name, {:foo => "name"}) { puts "foo" }
|
38
|
+
#
|
39
|
+
def self.publish queue_name, payload, &blk
|
40
|
+
queue_name = self.connection.queue_name if queue_name == :default
|
41
|
+
# Create a message object if it isn't one already
|
42
|
+
msg = Warren::MessageFilter.pack(payload)
|
43
|
+
|
44
|
+
do_connect(true, blk) do
|
45
|
+
queue = MQ::Queue.new(MQ.new, queue_name)
|
46
|
+
queue.publish msg.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Subscribes to a queue and runs the block
|
53
|
+
# for each message received
|
54
|
+
#
|
55
|
+
# Warren::Queue.subscribe("example") {|msg| puts msg }
|
56
|
+
#
|
57
|
+
# Expects a block and raises NoBlockGiven if no block is given.
|
58
|
+
#
|
59
|
+
def self.subscribe queue_name, &block
|
60
|
+
raise NoBlockGiven unless block_given?
|
61
|
+
queue_name = self.connection.queue_name if queue_name == :default
|
62
|
+
# todo: check if its a valid queue?
|
63
|
+
do_connect(false) do
|
64
|
+
queue = MQ::Queue.new(MQ.new, queue_name)
|
65
|
+
queue.subscribe do |msg|
|
66
|
+
msg = Warren::MessageFilter.unpack(msg)
|
67
|
+
block.call(msg)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Connects and does the stuff its told to!
|
76
|
+
def self.do_connect should_stop = true, callback = nil, &block
|
77
|
+
AMQP.start(self.connection.options) do
|
78
|
+
block.call
|
79
|
+
AMQP.stop { EM.stop_event_loop } if should_stop
|
80
|
+
end
|
81
|
+
# Returns the block return value or true
|
82
|
+
callback.nil? ? true : callback.call
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/warren.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "rubygems"
|
3
|
+
require "mq"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Library for pushing messages onto RabbitMQ queues,
|
7
|
+
# and receiving them at the other end.
|
8
|
+
#
|
9
|
+
# It handles authentication + filtering messages with custom
|
10
|
+
# classes if needed.
|
11
|
+
#
|
12
|
+
# Start with Warren::Queue for details and see also
|
13
|
+
# examples/
|
14
|
+
#
|
15
|
+
module Warren
|
16
|
+
@@foo = ""
|
17
|
+
end
|
18
|
+
|
19
|
+
WARREN_ROOT = File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
20
|
+
|
21
|
+
# Require everything in the lib folder
|
22
|
+
Dir["#{WARREN_ROOT}/lib/warren/**/*.rb"].each do |file|
|
23
|
+
require file
|
24
|
+
end
|
data/readme.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= Warren
|
2
|
+
|
3
|
+
Library for pushing messages onto RabbitMQ queues, and receiving them at the other end.
|
4
|
+
|
5
|
+
It handles authentication + filtering messages with custom classes if needed.
|
6
|
+
|
7
|
+
Start with Warren::Queue for details and see also examples/
|
8
|
+
|
9
|
+
== Released under the MIT Licence
|
10
|
+
|
11
|
+
Copyright (c) 2008 Brightbox Systems Ltd
|
12
|
+
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
14
|
+
|
15
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
18
|
+
|
19
|
+
See http://www.brightbox.co.uk/ for contact details.
|
data/spec/hash_extend.rb
ADDED
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Warren::Connection do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@c = Warren::Connection.new(details)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should require a username" do
|
10
|
+
lambda {
|
11
|
+
Warren::Connection.new(details.except(:user))
|
12
|
+
}.should raise_error(Warren::Connection::InvalidConnectionDetails)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should require a password" do
|
16
|
+
lambda {
|
17
|
+
Warren::Connection.new(details.except(:pass))
|
18
|
+
}.should raise_error(Warren::Connection::InvalidConnectionDetails)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should require a queue name if none specified" do
|
22
|
+
lambda {
|
23
|
+
conn = Warren::Connection.new(details)
|
24
|
+
conn.queue_name
|
25
|
+
}.should raise_error(Warren::Connection::InvalidConnectionDetails)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return a default host" do
|
29
|
+
@c.options[:host].should == "localhost"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return a given host" do
|
33
|
+
c = Warren::Connection.new(details.merge({:host => "caius"}))
|
34
|
+
c.options[:host].should == "caius"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return a default port" do
|
38
|
+
@c.options[:port].should == 5672
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return a given port" do
|
42
|
+
c = Warren::Connection.new(details.merge({:port => 1}))
|
43
|
+
c.options[:port].should == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return a default logging param" do
|
47
|
+
@c.options[:logging].should == false
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should return a given logging param" do
|
51
|
+
c = Warren::Connection.new(details.merge({:logging => true}))
|
52
|
+
c.options[:logging].should == true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return the given queue name" do
|
56
|
+
conn = Warren::Connection.new(details.merge({:default_queue => "queue"}))
|
57
|
+
conn.queue_name.should == "queue"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def details
|
63
|
+
{
|
64
|
+
:user => "user",
|
65
|
+
:pass => "pass",
|
66
|
+
:vhost => "main",
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
# Needed for some tests later on
|
4
|
+
class Foo
|
5
|
+
def self.pack msg
|
6
|
+
msg
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.unpack msg
|
10
|
+
msg
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Warren::MessageFilter do
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
Warren::MessageFilter.reset_filters
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "Managing Filters" do
|
21
|
+
it "should have YAML as a filter by default" do
|
22
|
+
fs = Warren::MessageFilter.filters
|
23
|
+
|
24
|
+
fs.should have(1).element
|
25
|
+
fs.first.should == Warren::MessageFilter::Yaml
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should add additional filters to the stack" do
|
29
|
+
Warren::MessageFilter.should respond_to(:<<)
|
30
|
+
Warren::MessageFilter << Foo
|
31
|
+
fs = Warren::MessageFilter.filters
|
32
|
+
|
33
|
+
fs.should have(2).elements
|
34
|
+
fs.first.should == Warren::MessageFilter::Yaml
|
35
|
+
fs.last.should == Foo
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "calling filters to send message" do
|
40
|
+
|
41
|
+
it "should YAML by default" do
|
42
|
+
@msg = "message"
|
43
|
+
Warren::MessageFilter::Yaml.should_receive(:pack).with(@msg).and_return("yamled")
|
44
|
+
|
45
|
+
Warren::MessageFilter.pack(@msg)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should call each filter in turn when packing" do
|
49
|
+
@msg = "message"
|
50
|
+
|
51
|
+
Foo.should_receive(:pack).with(@msg).and_return("fooed")
|
52
|
+
Warren::MessageFilter::Yaml.should_receive(:pack).with("fooed").and_return("yamled")
|
53
|
+
|
54
|
+
Warren::MessageFilter << Foo
|
55
|
+
|
56
|
+
Warren::MessageFilter.pack(@msg).should == "yamled"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "calling filters to unpack message" do
|
62
|
+
it "should un-YAML by default" do
|
63
|
+
@msg = "yamled"
|
64
|
+
|
65
|
+
Warren::MessageFilter::Yaml.should_receive(:unpack).with("yamled").and_return("message")
|
66
|
+
|
67
|
+
Warren::MessageFilter.unpack(@msg).should == "message"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should run all unpack filters" do
|
71
|
+
@msg = "yamled message"
|
72
|
+
|
73
|
+
Warren::MessageFilter::Yaml.should_receive(:unpack).with("yamled message").and_return("fooed")
|
74
|
+
Foo.should_receive(:unpack).with("fooed").and_return("message")
|
75
|
+
|
76
|
+
Warren::MessageFilter << Foo
|
77
|
+
Warren::MessageFilter.filters.should == [Warren::MessageFilter::Yaml, Foo]
|
78
|
+
|
79
|
+
Warren::MessageFilter.unpack(@msg).should == "message"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Warren::Queue do
|
4
|
+
|
5
|
+
it "should require raise exception with no connection details" do
|
6
|
+
lambda {
|
7
|
+
Warren::Queue.connection
|
8
|
+
}.should raise_error(Warren::Queue::NoConnectionDetails)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should require connection details to publish" do
|
12
|
+
lambda {
|
13
|
+
Warren::Queue.publish("", "")
|
14
|
+
}.should raise_error(Warren::Queue::NoConnectionDetails)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should require connection details to subscribe" do
|
18
|
+
lambda {
|
19
|
+
Warren::Queue.subscribe("") { true }
|
20
|
+
}.should raise_error(Warren::Queue::NoConnectionDetails)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should set connection details" do
|
24
|
+
conn = new_connection
|
25
|
+
|
26
|
+
Warren::Queue.connection = conn
|
27
|
+
Warren::Queue.connection.should == conn
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should publish to a queue" do
|
31
|
+
Warren::Queue.connection = new_connection
|
32
|
+
|
33
|
+
Warren::Queue.should_receive(:do_connect).with(true, nil).and_return(true)
|
34
|
+
Warren::Queue.publish("queue", "payload")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should publish to a default queue" do
|
38
|
+
Warren::Queue.connection = new_connection(:default_queue => "queue")
|
39
|
+
|
40
|
+
Warren::Queue.should_receive(:do_connect).with(true, nil).and_return(true)
|
41
|
+
Warren::Queue.publish(:default, "payload")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should publish to a queue with a block" do
|
45
|
+
Warren::Queue.connection = new_connection
|
46
|
+
|
47
|
+
blk = Proc.new { true }
|
48
|
+
|
49
|
+
Warren::Queue.should_receive(:do_connect).with(true, blk).and_return(true)
|
50
|
+
Warren::Queue.publish("queue", "payload", &blk)
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "subscribing" do
|
54
|
+
|
55
|
+
it "should require a block to be passed" do
|
56
|
+
Warren::Queue.connection = new_connection
|
57
|
+
|
58
|
+
lambda {
|
59
|
+
Warren::Queue.subscribe("queue")
|
60
|
+
}.should raise_error(Warren::Queue::NoBlockGiven)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should subscribe to a queue" do
|
64
|
+
Warren::Queue.connection = new_connection
|
65
|
+
|
66
|
+
Warren::Queue.should_receive(:do_connect).with(false).and_return(true)
|
67
|
+
Warren::Queue.subscribe("queue") { true }
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should subscribe to the default queue" do
|
71
|
+
Warren::Queue.connection = new_connection(:default_queue => "queue")
|
72
|
+
|
73
|
+
Warren::Queue.should_receive(:do_connect).with(false).and_return(true)
|
74
|
+
Warren::Queue.subscribe(:default) { true }
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def new_connection opts = {}
|
82
|
+
Warren::Connection.new(details.merge(opts))
|
83
|
+
end
|
84
|
+
|
85
|
+
def details
|
86
|
+
{
|
87
|
+
:user => "user",
|
88
|
+
:pass => "pass",
|
89
|
+
:vhost => "main",
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/tasks/rdoc.rake
ADDED
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Borrowed from http://github.com/rsim/ruby-plsql/tree/master/tasks/rspec.rake
|
2
|
+
# Github++
|
3
|
+
begin
|
4
|
+
require "spec"
|
5
|
+
rescue LoadError
|
6
|
+
require "rubygems"
|
7
|
+
require "spec"
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require "spec/rake/spectask"
|
12
|
+
rescue LoadError
|
13
|
+
puts <<-EOS
|
14
|
+
To use rspec for testing you must install rspec gem:
|
15
|
+
[sudo] gem install rspec
|
16
|
+
EOS
|
17
|
+
exit(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "Run the specs under spec/*"
|
21
|
+
Spec::Rake::SpecTask.new do |t|
|
22
|
+
t.spec_opts = ["--options", "spec/spec.opts"]
|
23
|
+
t.spec_files = FileList["spec/**/*_spec.rb"]
|
24
|
+
end
|
data/warren.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{warren}
|
5
|
+
s.version = "0.5"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Caius Durling, David Smalley"]
|
9
|
+
s.date = %q{2009-03-13}
|
10
|
+
s.description = %q{Library for pushing messages onto and off RabbitMQ queues}
|
11
|
+
s.email = %q{support@brightbox.co.uk}
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/warren/connection.rb", "lib/warren/message_filter.rb", "lib/warren/message_filters/shared_secret.rb", "lib/warren/message_filters/yaml.rb", "lib/warren/queue.rb", "lib/warren.rb", "tasks/rdoc.rake", "tasks/rspec.rake"]
|
13
|
+
s.files = ["CHANGELOG", "examples/authed/receiver.rb", "examples/authed/secret.rb", "examples/authed/sender.rb", "examples/simple/mass_sender.rb", "examples/simple/receiver.rb", "examples/simple/sender.rb", "lib/warren/connection.rb", "lib/warren/message_filter.rb", "lib/warren/message_filters/shared_secret.rb", "lib/warren/message_filters/yaml.rb", "lib/warren/queue.rb", "lib/warren.rb", "LICENCE", "Manifest", "Rakefile", "readme.rdoc", "spec/hash_extend.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/warren/connection_spec.rb", "spec/warren/message_filter_spec.rb", "spec/warren/queue_spec.rb", "tasks/rdoc.rake", "tasks/rspec.rake", "warren.gemspec"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/brightbox/warren}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Warren", "--main", "readme.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{warren}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Library for pushing messages onto and off RabbitMQ queues}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_runtime_dependency(%q<amqp>, [">= 0.6.0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<amqp>, [">= 0.6.0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<amqp>, [">= 0.6.0"])
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: brightbox-warren
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.5"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Caius Durling, David Smalley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-13 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: amqp
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.6.0
|
24
|
+
version:
|
25
|
+
description: Library for pushing messages onto and off RabbitMQ queues
|
26
|
+
email: support@brightbox.co.uk
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- CHANGELOG
|
33
|
+
- lib/warren/connection.rb
|
34
|
+
- lib/warren/message_filter.rb
|
35
|
+
- lib/warren/message_filters/shared_secret.rb
|
36
|
+
- lib/warren/message_filters/yaml.rb
|
37
|
+
- lib/warren/queue.rb
|
38
|
+
- lib/warren.rb
|
39
|
+
- tasks/rdoc.rake
|
40
|
+
- tasks/rspec.rake
|
41
|
+
files:
|
42
|
+
- CHANGELOG
|
43
|
+
- examples/authed/receiver.rb
|
44
|
+
- examples/authed/secret.rb
|
45
|
+
- examples/authed/sender.rb
|
46
|
+
- examples/simple/mass_sender.rb
|
47
|
+
- examples/simple/receiver.rb
|
48
|
+
- examples/simple/sender.rb
|
49
|
+
- lib/warren/connection.rb
|
50
|
+
- lib/warren/message_filter.rb
|
51
|
+
- lib/warren/message_filters/shared_secret.rb
|
52
|
+
- lib/warren/message_filters/yaml.rb
|
53
|
+
- lib/warren/queue.rb
|
54
|
+
- lib/warren.rb
|
55
|
+
- LICENCE
|
56
|
+
- Manifest
|
57
|
+
- Rakefile
|
58
|
+
- readme.rdoc
|
59
|
+
- spec/hash_extend.rb
|
60
|
+
- spec/spec.opts
|
61
|
+
- spec/spec_helper.rb
|
62
|
+
- spec/warren/connection_spec.rb
|
63
|
+
- spec/warren/message_filter_spec.rb
|
64
|
+
- spec/warren/queue_spec.rb
|
65
|
+
- tasks/rdoc.rake
|
66
|
+
- tasks/rspec.rake
|
67
|
+
- warren.gemspec
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/brightbox/warren
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options:
|
72
|
+
- --line-numbers
|
73
|
+
- --inline-source
|
74
|
+
- --title
|
75
|
+
- Warren
|
76
|
+
- --main
|
77
|
+
- readme.rdoc
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: "0"
|
85
|
+
version:
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "1.2"
|
91
|
+
version:
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: warren
|
95
|
+
rubygems_version: 1.2.0
|
96
|
+
signing_key:
|
97
|
+
specification_version: 2
|
98
|
+
summary: Library for pushing messages onto and off RabbitMQ queues
|
99
|
+
test_files: []
|
100
|
+
|