qup 1.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.
- data/.autotest +41 -0
- data/.gemtest +1 -0
- data/ADAPTER_API.rdoc +97 -0
- data/HISTORY.rdoc +9 -0
- data/Manifest.txt +52 -0
- data/README.rdoc +156 -0
- data/Rakefile +246 -0
- data/lib/qup.rb +48 -0
- data/lib/qup/adapter.rb +57 -0
- data/lib/qup/adapter/kestrel.rb +56 -0
- data/lib/qup/adapter/kestrel/destination.rb +54 -0
- data/lib/qup/adapter/kestrel/queue.rb +101 -0
- data/lib/qup/adapter/kestrel/topic.rb +68 -0
- data/lib/qup/adapter/maildir.rb +57 -0
- data/lib/qup/adapter/maildir/queue.rb +123 -0
- data/lib/qup/adapter/maildir/topic.rb +85 -0
- data/lib/qup/adapter/redis.rb +55 -0
- data/lib/qup/adapter/redis/connection.rb +32 -0
- data/lib/qup/adapter/redis/queue.rb +97 -0
- data/lib/qup/adapter/redis/topic.rb +76 -0
- data/lib/qup/consumer.rb +42 -0
- data/lib/qup/message.rb +18 -0
- data/lib/qup/producer.rb +28 -0
- data/lib/qup/publisher.rb +30 -0
- data/lib/qup/queue_api.rb +124 -0
- data/lib/qup/session.rb +111 -0
- data/lib/qup/subscriber.rb +28 -0
- data/lib/qup/topic_api.rb +92 -0
- data/spec/qup/adapter/kestrel/queue_spec.rb +9 -0
- data/spec/qup/adapter/kestrel/topic_spec.rb +9 -0
- data/spec/qup/adapter/kestrel_context.rb +8 -0
- data/spec/qup/adapter/kestrel_spec.rb +8 -0
- data/spec/qup/adapter/maildir/queue_spec.rb +9 -0
- data/spec/qup/adapter/maildir/topic_spec.rb +9 -0
- data/spec/qup/adapter/maildir_context.rb +10 -0
- data/spec/qup/adapter/maildir_spec.rb +8 -0
- data/spec/qup/adapter/redis/queue_spec.rb +9 -0
- data/spec/qup/adapter/redis/topic_spec.rb +9 -0
- data/spec/qup/adapter/redis_context.rb +6 -0
- data/spec/qup/adapter/redis_spec.rb +8 -0
- data/spec/qup/adapter_spec.rb +28 -0
- data/spec/qup/consumer_spec.rb +40 -0
- data/spec/qup/message_spec.rb +13 -0
- data/spec/qup/producer_spec.rb +18 -0
- data/spec/qup/queue_api_spec.rb +21 -0
- data/spec/qup/session_spec.rb +81 -0
- data/spec/qup/shared_adapter_examples.rb +29 -0
- data/spec/qup/shared_queue_examples.rb +71 -0
- data/spec/qup/shared_topic_examples.rb +57 -0
- data/spec/qup/topic_api_spec.rb +21 -0
- data/spec/qup_spec.rb +37 -0
- data/spec/spec_helper.rb +26 -0
- metadata +281 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_queue_examples'
|
3
|
+
require 'qup/adapter/kestrel_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Kestrel::Queue', :kestrel => true do
|
6
|
+
include_context "Qup::Adapter::Kestrel"
|
7
|
+
include_context "Qup::Queue"
|
8
|
+
it_behaves_like Qup::QueueAPI
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_topic_examples'
|
3
|
+
require 'qup/adapter/kestrel_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Kestrel::Topic', :kestrel => true do
|
6
|
+
include_context "Qup::Adapter::Kestrel"
|
7
|
+
include_context "Qup::Topic"
|
8
|
+
it_behaves_like Qup::TopicAPI
|
9
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# The Shared Context that all the Kestrel tests need to run.
|
2
|
+
# It is assumed that there is a Kestrel server running on localhost port 22133
|
3
|
+
shared_context "Qup::Adapter::Kestrel" do
|
4
|
+
let( :uri ) { URI.parse( "kestrel://localhost:22133/" ) }
|
5
|
+
let( :adapter ) { ::Qup::Adapter::Kestrel.new( uri ) }
|
6
|
+
end
|
7
|
+
|
8
|
+
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_queue_examples'
|
3
|
+
require 'qup/adapter/maildir_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Maildir::Queue', :maildir => true do
|
6
|
+
include_context "Qup::Adapter::Maildir"
|
7
|
+
include_context "Qup::Queue"
|
8
|
+
it_behaves_like Qup::QueueAPI
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_topic_examples'
|
3
|
+
require 'qup/adapter/maildir_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Maildir::Topic', :maildir => true do
|
6
|
+
include_context "Qup::Adapter::Maildir"
|
7
|
+
include_context "Qup::Topic"
|
8
|
+
it_behaves_like Qup::TopicAPI
|
9
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# The common context needed for all the Maildir Adapter tests.
|
2
|
+
#
|
3
|
+
# All the maildir tests will be run in the 'path' below
|
4
|
+
shared_context 'Qup::Adapter::Maildir' do
|
5
|
+
let( :path ) { temp_dir( "qup-maildir" ) }
|
6
|
+
let( :uri ) { URI.parse( "maildir://#{path}" ) }
|
7
|
+
|
8
|
+
# Needed to support the Shared Examples
|
9
|
+
let( :adapter ) { ::Qup::Adapter::Maildir.new( uri ) }
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_queue_examples'
|
3
|
+
require 'qup/adapter/redis_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Redis::Queue', :redis => true do
|
6
|
+
include_context "Qup::Adapter::Redis"
|
7
|
+
include_context "Qup::Queue"
|
8
|
+
it_behaves_like Qup::QueueAPI
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'qup/shared_topic_examples'
|
3
|
+
require 'qup/adapter/redis_context'
|
4
|
+
|
5
|
+
describe 'Qup::Adapter::Redis::Topic', :redis => true do
|
6
|
+
include_context "Qup::Adapter::Redis"
|
7
|
+
include_context "Qup::Topic"
|
8
|
+
it_behaves_like Qup::TopicAPI
|
9
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# The shared context that all the Redis tests need to run.
|
2
|
+
# It is assumed that there is a Redis server running on localhost port 6379
|
3
|
+
shared_context "Qup::Adapter::Redis" do
|
4
|
+
let( :uri ) { URI.parse( "redis://localhost:6379/" ) }
|
5
|
+
let( :adapter ) { ::Qup::Adapter::Redis.new( uri ) }
|
6
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Qup::AdapterTest < Qup::Adapter
|
4
|
+
register :quptest
|
5
|
+
end
|
6
|
+
|
7
|
+
describe 'Adapter Registration' do
|
8
|
+
it 'registers an adapter' do
|
9
|
+
Qup::Adapters['quptest'].should eq Qup::AdapterTest
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "Not Implementing the Adapter API" do
|
14
|
+
let( :api ) { Qup::AdapterTest.new }
|
15
|
+
|
16
|
+
%w[ close closed? ].each do |method|
|
17
|
+
it "##{method} kaboom!" do
|
18
|
+
lambda { api.send( method ) }.should raise_error( NotImplementedError, "please implement '#{method}'" )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
%w[ queue topic ].each do |method|
|
23
|
+
it "##{method} kaboom!" do
|
24
|
+
lambda { api.send( method, 'foo' ) }.should raise_error( NotImplementedError, "please implement '#{method}'" )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Qup::Consumer do
|
4
|
+
|
5
|
+
let( :path ) { temp_dir( "qup-consumer" ) }
|
6
|
+
let( :queue ) { ::Qup::Adapter::Maildir::Queue.new( path, 'bar' ) }
|
7
|
+
let( :producer ) { queue.producer }
|
8
|
+
let( :consumer ) { queue.consumer }
|
9
|
+
|
10
|
+
before do
|
11
|
+
producer.produce( 'consumption' )
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
FileUtils.rm_rf( path )
|
16
|
+
end
|
17
|
+
|
18
|
+
it "consumes an item from the queue" do
|
19
|
+
msg = consumer.consume
|
20
|
+
msg.data.should eq 'consumption'
|
21
|
+
queue.acknowledge msg
|
22
|
+
queue.depth.should eq 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "acknowledges messages it has consumed" do
|
26
|
+
msg = consumer.consume
|
27
|
+
msg.data.should eq 'consumption'
|
28
|
+
queue.depth.should eq 1
|
29
|
+
consumer.acknowledge( msg )
|
30
|
+
queue.depth.should eq 0
|
31
|
+
end
|
32
|
+
|
33
|
+
it "consumes auto-acknowledges msgs in a block" do
|
34
|
+
consumer.consume do |msg|
|
35
|
+
msg.data.should eq 'consumption'
|
36
|
+
end
|
37
|
+
queue.depth.should eq 0
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Qup::Message do
|
4
|
+
let( :message ) { Qup::Message.new( "my unique key", "some data" ) }
|
5
|
+
|
6
|
+
it "has a key" do
|
7
|
+
message.key.should == 'my unique key'
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has data' do
|
11
|
+
message.data.should == 'some data'
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Qup::Producer do
|
4
|
+
|
5
|
+
let( :path ) { temp_dir( "qup-producer" ) }
|
6
|
+
let( :queue ) { ::Qup::Adapter::Maildir::Queue.new( path, 'baz' ) }
|
7
|
+
let( :producer ) { queue.producer }
|
8
|
+
|
9
|
+
after do
|
10
|
+
FileUtils.rm_rf( path )
|
11
|
+
end
|
12
|
+
|
13
|
+
it "produces items onto the queue" do
|
14
|
+
queue.depth.should eq 0
|
15
|
+
producer.produce( 'production' )
|
16
|
+
queue.depth.should eq 1
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Qup::QueueAPITest
|
4
|
+
include Qup::QueueAPI
|
5
|
+
end
|
6
|
+
|
7
|
+
describe "Not Implementing the Queue API" do
|
8
|
+
let( :api ) { Qup::QueueAPITest.new }
|
9
|
+
|
10
|
+
%w[ name depth flush destroy consume ].each do |method|
|
11
|
+
it "##{method} kaboom!" do
|
12
|
+
lambda { api.send( method ) }.should raise_error( NotImplementedError, "please implement '#{method}'" )
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
%w[ produce acknowledge ].each do |method|
|
17
|
+
it "##{method} kaboom!" do
|
18
|
+
lambda { api.send( method, nil ) }.should raise_error( NotImplementedError, "please implement '#{method}'" )
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Qup::Session do
|
4
|
+
|
5
|
+
let( :path ) { temp_dir( "qup-session" ) }
|
6
|
+
let( :uri ) { "maildir://#{path}" }
|
7
|
+
let( :session ) { ::Qup::Session.new( uri ) }
|
8
|
+
|
9
|
+
after do
|
10
|
+
FileUtils.rm_rf( path )
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a uri" do
|
14
|
+
session.uri.to_s.should == "maildir:#{path}"
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can be closed' do
|
18
|
+
session.closed?.should be_false
|
19
|
+
session.close
|
20
|
+
session.closed?.should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#open' do
|
24
|
+
it 'returns a new session' do
|
25
|
+
s = Qup::Session.open( uri )
|
26
|
+
s.closed?.should be_false
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'yields a new session' do
|
30
|
+
Qup::Session.open( uri ) do |s|
|
31
|
+
s.closed?.should be_false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'closes a session at the end of the block' do
|
36
|
+
save_s = nil
|
37
|
+
Qup::Session.open( uri ) do |s|
|
38
|
+
s.closed?.should be_false
|
39
|
+
save_s = s
|
40
|
+
end
|
41
|
+
save_s.closed?.should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#queue' do
|
46
|
+
it "can return a Queue" do
|
47
|
+
q = session.queue( 'foo' )
|
48
|
+
q.name.should == 'foo'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can yield a Queue' do
|
52
|
+
session.queue( 'foo' ) do |q|
|
53
|
+
q.name.should == 'foo'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises an error if accessing a closed Session' do
|
58
|
+
session.close
|
59
|
+
lambda { session.queue( 'boom' ) }.should raise_error( Qup::Session::ClosedError, /Session (.*) is closed/ )
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
describe '#topic' do
|
65
|
+
it "can return a Topic" do
|
66
|
+
t = session.topic('t')
|
67
|
+
t.name.should == 't'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can yiled a Topic' do
|
71
|
+
session.topic('t') do |t|
|
72
|
+
t.name.should == 't'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises an error if accessing a closed Session' do
|
77
|
+
session.close
|
78
|
+
lambda { session.topic( 'boom' ) }.should raise_error( Qup::Session::ClosedError, /Session (.*) is closed/ )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# The Adapter share context requires that the context is include in define:
|
4
|
+
#
|
5
|
+
# let( :adapter )
|
6
|
+
#
|
7
|
+
shared_examples Qup::Adapter do
|
8
|
+
it 'is registered as an adapter' do
|
9
|
+
Qup::Adapters[uri.scheme].should eq adapter.class
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can be closed' do
|
13
|
+
adapter.closed?.should be_false
|
14
|
+
adapter.close
|
15
|
+
adapter.closed?.should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can create a QueueAPI-like object' do
|
19
|
+
q = adapter.queue( 'q' )
|
20
|
+
q.should be_kind_of( Qup::QueueAPI )
|
21
|
+
q.name.should eq 'q'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can create a QueueAPI-like object' do
|
25
|
+
t = adapter.topic( 't' )
|
26
|
+
t.should be_kind_of( Qup::TopicAPI )
|
27
|
+
t.name.should eq 't'
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# The Queue share context requires that the context is include in define:
|
4
|
+
#
|
5
|
+
# let( :adapter )
|
6
|
+
#
|
7
|
+
shared_context "Qup::Queue" do
|
8
|
+
|
9
|
+
let( :queue ) { adapter.queue( 'foo' ) }
|
10
|
+
|
11
|
+
after do
|
12
|
+
queue.destroy
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
shared_examples Qup::QueueAPI do
|
18
|
+
|
19
|
+
it "has a name" do
|
20
|
+
queue.name.should eq 'foo'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "#produce" do
|
24
|
+
queue.depth.should eq 0
|
25
|
+
queue.produce( "a new message" )
|
26
|
+
queue.depth.should eq 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#flush" do
|
30
|
+
10.times { |x| queue.produce( "message #{x}" ) }
|
31
|
+
queue.depth.should eq 10
|
32
|
+
queue.flush
|
33
|
+
queue.depth.should eq 0
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#consume' do
|
37
|
+
before do
|
38
|
+
queue.produce( "consumeable message" )
|
39
|
+
queue.depth.should eq 1
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'normally' do
|
43
|
+
msg = queue.consume
|
44
|
+
msg.data.should eq "consumeable message"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'with block it auto acknowledges' do
|
48
|
+
queue.consume do |msg|
|
49
|
+
msg.data.should eq 'consumeable message'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#acknowledge" do
|
55
|
+
it "acks a message" do
|
56
|
+
queue.produce( "acknowledgeable message" )
|
57
|
+
queue.depth.should eq 1
|
58
|
+
|
59
|
+
msg = queue.consume
|
60
|
+
msg.data.should eq "acknowledgeable message"
|
61
|
+
|
62
|
+
queue.acknowledge( msg )
|
63
|
+
queue.depth.should eq 0
|
64
|
+
end
|
65
|
+
|
66
|
+
it "raises an error if you attempt to to acknowledge an unconsumed message" do
|
67
|
+
msg = queue.produce( 'unconsumed' )
|
68
|
+
lambda { queue.acknowledge( msg ) }.should raise_error(Qup::Error)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# The Queue share context requires that the context is include in define:
|
4
|
+
#
|
5
|
+
# let( :adapter )
|
6
|
+
#
|
7
|
+
shared_context "Qup::Topic" do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@topic = adapter.topic( 'topic' )
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
@topic.destroy
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
shared_examples Qup::TopicAPI do
|
20
|
+
|
21
|
+
it "has a name" do
|
22
|
+
@topic.name.should == 'topic'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "creates publisher" do
|
26
|
+
p = @topic.publisher
|
27
|
+
p.topic.should eq @topic
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
describe "subscribers" do
|
32
|
+
before do
|
33
|
+
@subs = []
|
34
|
+
3.times do |x|
|
35
|
+
@subs << @topic.subscriber( "sub-#{x}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
after do
|
40
|
+
@subs.each { |s| s.unsubscribe }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "are counted" do
|
44
|
+
@topic.subscriber_count.should eq 3
|
45
|
+
end
|
46
|
+
|
47
|
+
it "each receives a copy of the message" do
|
48
|
+
p = @topic.publisher
|
49
|
+
p.publish( "hi all" )
|
50
|
+
|
51
|
+
@subs.each do |sub|
|
52
|
+
msg = sub.consume
|
53
|
+
msg.data.should eq 'hi all'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|