amqp-boilerplate 0.0.1
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.
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/README +18 -0
- data/Rakefile +8 -0
- data/amqp-boilerplate.gemspec +26 -0
- data/lib/amqp-boilerplate.rb +1 -0
- data/lib/amqp/boilerplate.rb +17 -0
- data/lib/amqp/boilerplate/consumer.rb +34 -0
- data/lib/amqp/boilerplate/logging.rb +13 -0
- data/lib/amqp/boilerplate/producer.rb +45 -0
- data/lib/amqp/boilerplate/version.rb +5 -0
- data/spec/amqp/boilerplate/consumer_spec.rb +101 -0
- data/spec/amqp/boilerplate/logging_spec.rb +25 -0
- data/spec/amqp/boilerplate/producer_spec.rb +104 -0
- data/spec/amqp/boilerplate_spec.rb +15 -0
- data/spec/spec_helper.rb +7 -0
- metadata +134 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path("../lib", __FILE__)
|
4
|
+
require 'amqp/boilerplate/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "amqp-boilerplate"
|
8
|
+
s.version = AMQP::Boilerplate::VERSION
|
9
|
+
s.authors = ["Patrick Baselier", "Ludo van den Boom"]
|
10
|
+
s.email = ["patrick@kabisa.nl", "ludo@cubicphuse.nl"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Helper modules for quickly setting up AMQP producers/consumers}
|
13
|
+
s.description = %q{Collection of modules that aid in setting up AMQP producers and consumers.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "amqp-boilerplate"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
# specify any dependencies here; for example:
|
23
|
+
s.add_development_dependency "rake", "~> 0.9"
|
24
|
+
s.add_development_dependency "rspec", "~> 2.6"
|
25
|
+
s.add_runtime_dependency "amqp", "~> 0.8"
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'amqp/boilerplate'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'amqp'
|
2
|
+
|
3
|
+
require 'amqp/boilerplate/version'
|
4
|
+
|
5
|
+
require 'amqp/boilerplate/consumer'
|
6
|
+
require 'amqp/boilerplate/logging'
|
7
|
+
require 'amqp/boilerplate/producer'
|
8
|
+
|
9
|
+
module AMQP
|
10
|
+
module Boilerplate
|
11
|
+
extend Logging
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
yield self if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module AMQP
|
2
|
+
module Boilerplate
|
3
|
+
class Consumer
|
4
|
+
class << self
|
5
|
+
def amqp_queue(name=AMQ::Protocol::EMPTY_STRING, options={})
|
6
|
+
@queue_name = name
|
7
|
+
@queue_options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def amqp_subscription(options={})
|
11
|
+
@subscription_options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
consumer = new
|
16
|
+
|
17
|
+
channel = AMQP.channel
|
18
|
+
channel.on_error(&consumer.method(:handle_channel_error))
|
19
|
+
|
20
|
+
queue = channel.queue(@queue_name, @queue_options)
|
21
|
+
queue.subscribe(@subscription_options, &consumer.method(:handle_message))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_message(metadata, payload)
|
26
|
+
raise NotImplementedError, "The time has come to implement your own consumer class. Good luck!"
|
27
|
+
end
|
28
|
+
|
29
|
+
def handle_channel_error(channel, channel_close)
|
30
|
+
AMQP::Boilerplate.logger.error("[#{self.class}#handle_channel_error] Code = #{channel_close.reply_code}, message = #{channel_close.reply_text}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module AMQP
|
2
|
+
module Boilerplate
|
3
|
+
module Producer
|
4
|
+
def amqp(options={})
|
5
|
+
send :include, InstanceMethods
|
6
|
+
@amqp_boilerplate_options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def amqp_exchange(type=:direct, name=AMQ::Protocol::EMPTY_STRING, options={})
|
10
|
+
@amqp_boilerplate_exchange = [type, name, options]
|
11
|
+
end
|
12
|
+
|
13
|
+
def amqp_message(method_name)
|
14
|
+
@amqp_boilerplate_message = method_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def amqp_boilerplate_options
|
18
|
+
@amqp_boilerplate_options
|
19
|
+
end
|
20
|
+
|
21
|
+
def amqp_boilerplate_exchange
|
22
|
+
@amqp_boilerplate_exchange || amqp_exchange
|
23
|
+
end
|
24
|
+
|
25
|
+
def amqp_boilerplate_message
|
26
|
+
@amqp_boilerplate_message
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
def publish
|
31
|
+
message = send(self.class.amqp_boilerplate_message.to_sym)
|
32
|
+
exchange.publish(message, self.class.amqp_boilerplate_options) do
|
33
|
+
AMQP::Boilerplate.logger.debug "[#{self.class}] Published message:\n#{message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def exchange
|
40
|
+
AMQP::Exchange.new(AMQP.channel, *self.class.amqp_boilerplate_exchange)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyConsumer < AMQP::Boilerplate::Consumer
|
4
|
+
amqp_queue "queue.name.here", :durable => true
|
5
|
+
amqp_subscription :ack => true
|
6
|
+
end
|
7
|
+
|
8
|
+
class FooConsumer < AMQP::Boilerplate::Consumer
|
9
|
+
amqp_queue
|
10
|
+
end
|
11
|
+
|
12
|
+
describe AMQP::Boilerplate::Consumer do
|
13
|
+
before(:each) do
|
14
|
+
@channel = mock(AMQP::Channel)
|
15
|
+
@channel.stub(:on_error).and_return(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#handle_channel_error" do
|
19
|
+
before(:each) do
|
20
|
+
@channel_close = mock(:reply_code => "OK", :reply_text => "Something")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should log the code and message" do
|
24
|
+
AMQP::Boilerplate.logger.should_receive(:error).with("[DummyConsumer#handle_channel_error] Code = #{@channel_close.reply_code}, message = #{@channel_close.reply_text}")
|
25
|
+
|
26
|
+
DummyConsumer.new.handle_channel_error(@channel, @channel_close)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#handle_message" do
|
31
|
+
it "should raise an error (must be implemented by subclasses)" do
|
32
|
+
expect {
|
33
|
+
AMQP::Boilerplate::Consumer.new.handle_message(anything, anything)
|
34
|
+
}.to raise_error(NotImplementedError, "The time has come to implement your own consumer class. Good luck!")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".amqp_queue" do
|
39
|
+
before(:each) do
|
40
|
+
AMQP.stub(:channel).and_return(@channel)
|
41
|
+
@queue = mock(AMQP::Queue)
|
42
|
+
@channel.stub(:queue).and_return(@queue)
|
43
|
+
@queue.stub(:subscribe)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should use a default name for queue" do
|
47
|
+
@channel.should_receive(:queue).with(AMQ::Protocol::EMPTY_STRING, anything).and_return(@queue)
|
48
|
+
|
49
|
+
FooConsumer.start
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should use a empty hash for queue options" do
|
53
|
+
@channel.should_receive(:queue).with(anything, {}).and_return(@queue)
|
54
|
+
|
55
|
+
FooConsumer.start
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ".start" do
|
60
|
+
before(:each) do
|
61
|
+
@consumer = DummyConsumer.new
|
62
|
+
DummyConsumer.stub(:new).and_return(@consumer)
|
63
|
+
|
64
|
+
AMQP.stub(:channel).and_return(@channel)
|
65
|
+
|
66
|
+
@queue = mock(AMQP::Queue)
|
67
|
+
@channel.stub(:queue).and_return(@queue)
|
68
|
+
@queue.stub(:subscribe)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should use a channel" do
|
72
|
+
AMQP.should_receive(:channel).and_return(@channel)
|
73
|
+
DummyConsumer.start
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should instantiate a consumer" do
|
77
|
+
DummyConsumer.should_receive(:new).and_return(@consumer)
|
78
|
+
DummyConsumer.start
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should register a channel error handler" do
|
82
|
+
@channel.should_receive(:on_error)
|
83
|
+
DummyConsumer.start
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should instantiate a queue with the proper queue name" do
|
87
|
+
@channel.should_receive(:queue).with("queue.name.here", anything)
|
88
|
+
DummyConsumer.start
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should instantiate a queue provided with options" do
|
92
|
+
@channel.should_receive(:queue).with(anything, :durable => true)
|
93
|
+
DummyConsumer.start
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should subscribe to the queue" do
|
97
|
+
@queue.should_receive(:subscribe).with(:ack => true)
|
98
|
+
DummyConsumer.start
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AMQP::Boilerplate::Logging do
|
4
|
+
after(:each) do
|
5
|
+
# Reset
|
6
|
+
AMQP::Boilerplate.logger = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#logger" do
|
10
|
+
it "should set the logger to use" do
|
11
|
+
MyLogger = Class.new
|
12
|
+
AMQP::Boilerplate.logger = MyLogger # { |config| config.logger = MyLogger }
|
13
|
+
AMQP::Boilerplate.logger.should == MyLogger
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should default to Ruby Logger" do
|
17
|
+
AMQP::Boilerplate.logger.should be_a(Logger)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should default to Logger writing to STDOUT" do
|
21
|
+
Logger.should_receive(:new).with(STDOUT)
|
22
|
+
AMQP::Boilerplate.logger
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyProducer
|
4
|
+
extend AMQP::Boilerplate::Producer
|
5
|
+
|
6
|
+
amqp :routing_key => "some.routing.key"
|
7
|
+
amqp_exchange :fanout, "amq.fanout", { :durable => true }
|
8
|
+
amqp_message :message
|
9
|
+
|
10
|
+
def message
|
11
|
+
"hello world!"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FooProducer
|
16
|
+
extend AMQP::Boilerplate::Producer
|
17
|
+
|
18
|
+
amqp :routing_key => "another.routing.key"
|
19
|
+
amqp_message :some_method
|
20
|
+
|
21
|
+
def some_method
|
22
|
+
'Foo Bar'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe AMQP::Boilerplate::Producer do
|
27
|
+
before(:each) do
|
28
|
+
AMQP::Boilerplate.stub(:logger).and_return(mock.as_null_object)
|
29
|
+
|
30
|
+
@exchange = mock(AMQP::Exchange)
|
31
|
+
@exchange.stub(:publish).and_yield
|
32
|
+
AMQP::Exchange.stub!(:new).and_return(@exchange)
|
33
|
+
|
34
|
+
@producer = DummyProducer.new
|
35
|
+
end
|
36
|
+
|
37
|
+
describe ".amqp_exchange" do
|
38
|
+
it "should default amqp_exchange attributes" do
|
39
|
+
another_producer = FooProducer.new
|
40
|
+
AMQP::Exchange.should_receive(:new).with(AMQP.channel, :direct, AMQ::Protocol::EMPTY_STRING, {})
|
41
|
+
another_producer.publish
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#publish" do
|
46
|
+
it "should use an exchange" do
|
47
|
+
@producer.should_receive(:exchange).and_return(@exchange)
|
48
|
+
@producer.publish
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should publish to exchange" do
|
52
|
+
@exchange.should_receive(:publish)
|
53
|
+
@producer.publish
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should publish to the exchange using the proper routing_key" do
|
57
|
+
@exchange.should_receive(:publish).with(@producer.message, :routing_key => "some.routing.key")
|
58
|
+
@producer.publish
|
59
|
+
|
60
|
+
another_producer = FooProducer.new
|
61
|
+
@exchange.should_receive(:publish).with(another_producer.some_method, :routing_key => "another.routing.key")
|
62
|
+
another_producer.publish
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should pass options to AMQP::Exchange#publish" do
|
66
|
+
DummyProducer.amqp({ :routing_key => "some.routing.key", :mandatory => true })
|
67
|
+
@exchange.should_receive(:publish).with(@producer.message, :routing_key => "some.routing.key", :mandatory => true)
|
68
|
+
@producer.publish
|
69
|
+
|
70
|
+
DummyProducer.amqp({ :routing_key => "some.routing.key", :mandatory => true, :immediate => true })
|
71
|
+
@exchange.should_receive(:publish).with(@producer.message, :routing_key => "some.routing.key", :mandatory => true, :immediate => true)
|
72
|
+
@producer.publish
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "connecting to exchange" do
|
76
|
+
before(:each) do
|
77
|
+
@channel = mock(AMQP::Channel)
|
78
|
+
AMQP.stub(:channel).and_return(@channel)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should instantiate an exchange" do
|
82
|
+
AMQP::Exchange.should_receive(:new)
|
83
|
+
@producer.publish
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should connect to the exchange" do
|
87
|
+
AMQP::Exchange.should_receive(:new).with(@channel, :fanout, "amq.fanout", { :durable => true })
|
88
|
+
@producer.publish
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should use defaults for exchange configuration" do
|
92
|
+
DummyProducer.amqp_exchange()
|
93
|
+
|
94
|
+
AMQP::Exchange.should_receive(:new).with(@channel, :direct, AMQ::Protocol::EMPTY_STRING, {})
|
95
|
+
@producer.publish
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should log after delivering message" do
|
100
|
+
AMQP::Boilerplate.logger.should_receive(:debug).with("[#{@producer.class}] Published message:\n#{@producer.message}")
|
101
|
+
@producer.publish
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AMQP::Boilerplate do
|
4
|
+
describe ".configure" do
|
5
|
+
after(:each) do
|
6
|
+
AMQP::Boilerplate.logger = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should let us choose what logger to use" do
|
10
|
+
MyFunkyLogger = Class.new
|
11
|
+
AMQP::Boilerplate.configure { |config| config.logger = MyFunkyLogger }
|
12
|
+
AMQP::Boilerplate.logger.should == MyFunkyLogger
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: amqp-boilerplate
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Patrick Baselier
|
14
|
+
- Ludo van den Boom
|
15
|
+
autorequire:
|
16
|
+
bindir: bin
|
17
|
+
cert_chain: []
|
18
|
+
|
19
|
+
date: 2011-09-12 00:00:00 +02:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: rake
|
24
|
+
type: :development
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 25
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
- 9
|
34
|
+
version: "0.9"
|
35
|
+
prerelease: false
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
type: :development
|
40
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 15
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 6
|
49
|
+
version: "2.6"
|
50
|
+
prerelease: false
|
51
|
+
requirement: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: amqp
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 27
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
- 8
|
64
|
+
version: "0.8"
|
65
|
+
prerelease: false
|
66
|
+
requirement: *id003
|
67
|
+
description: Collection of modules that aid in setting up AMQP producers and consumers.
|
68
|
+
email:
|
69
|
+
- patrick@kabisa.nl
|
70
|
+
- ludo@cubicphuse.nl
|
71
|
+
executables: []
|
72
|
+
|
73
|
+
extensions: []
|
74
|
+
|
75
|
+
extra_rdoc_files: []
|
76
|
+
|
77
|
+
files:
|
78
|
+
- .gitignore
|
79
|
+
- .rspec
|
80
|
+
- Gemfile
|
81
|
+
- README
|
82
|
+
- Rakefile
|
83
|
+
- amqp-boilerplate.gemspec
|
84
|
+
- lib/amqp-boilerplate.rb
|
85
|
+
- lib/amqp/boilerplate.rb
|
86
|
+
- lib/amqp/boilerplate/consumer.rb
|
87
|
+
- lib/amqp/boilerplate/logging.rb
|
88
|
+
- lib/amqp/boilerplate/producer.rb
|
89
|
+
- lib/amqp/boilerplate/version.rb
|
90
|
+
- spec/amqp/boilerplate/consumer_spec.rb
|
91
|
+
- spec/amqp/boilerplate/logging_spec.rb
|
92
|
+
- spec/amqp/boilerplate/producer_spec.rb
|
93
|
+
- spec/amqp/boilerplate_spec.rb
|
94
|
+
- spec/spec_helper.rb
|
95
|
+
has_rdoc: true
|
96
|
+
homepage: ""
|
97
|
+
licenses: []
|
98
|
+
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
version: "0"
|
122
|
+
requirements: []
|
123
|
+
|
124
|
+
rubyforge_project: amqp-boilerplate
|
125
|
+
rubygems_version: 1.5.2
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Helper modules for quickly setting up AMQP producers/consumers
|
129
|
+
test_files:
|
130
|
+
- spec/amqp/boilerplate/consumer_spec.rb
|
131
|
+
- spec/amqp/boilerplate/logging_spec.rb
|
132
|
+
- spec/amqp/boilerplate/producer_spec.rb
|
133
|
+
- spec/amqp/boilerplate_spec.rb
|
134
|
+
- spec/spec_helper.rb
|