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 ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ *.swo
3
+ *.swp
4
+ .bundle
5
+ .rvmrc
6
+ Gemfile.lock
7
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in amqp-boilerplate.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,18 @@
1
+ = AMQP Boilterplate
2
+
3
+ == Getting started
4
+
5
+ === Install RabbitMQ
6
+
7
+ === Install the gem
8
+
9
+ ==== Rails 3.x
10
+ gem "amqp-boilerplate", "~> 0.1.0"
11
+
12
+ === Create a producer
13
+
14
+ === Create a consumer
15
+
16
+ == License
17
+
18
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc "Run specs"
5
+ RSpec::Core::RakeTask.new(:spec) { |t| }
6
+
7
+ desc "Run specs"
8
+ task :default => :spec
@@ -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,13 @@
1
+ require 'logger'
2
+
3
+ module AMQP
4
+ module Boilerplate
5
+ module Logging
6
+ attr_writer :logger
7
+
8
+ def logger
9
+ @logger ||= ::Logger.new(STDOUT)
10
+ end
11
+ end
12
+ end
13
+ 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,5 @@
1
+ module AMQP
2
+ module Boilerplate
3
+ VERSION = "0.0.1"
4
+ end
5
+ 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
@@ -0,0 +1,7 @@
1
+ require 'rspec'
2
+
3
+ require 'amqp/boilerplate'
4
+
5
+ RSpec.configure do |c|
6
+ c.mock_with :rspec
7
+ end
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