qup 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.autotest +41 -0
  2. data/.gemtest +1 -0
  3. data/ADAPTER_API.rdoc +97 -0
  4. data/HISTORY.rdoc +9 -0
  5. data/Manifest.txt +52 -0
  6. data/README.rdoc +156 -0
  7. data/Rakefile +246 -0
  8. data/lib/qup.rb +48 -0
  9. data/lib/qup/adapter.rb +57 -0
  10. data/lib/qup/adapter/kestrel.rb +56 -0
  11. data/lib/qup/adapter/kestrel/destination.rb +54 -0
  12. data/lib/qup/adapter/kestrel/queue.rb +101 -0
  13. data/lib/qup/adapter/kestrel/topic.rb +68 -0
  14. data/lib/qup/adapter/maildir.rb +57 -0
  15. data/lib/qup/adapter/maildir/queue.rb +123 -0
  16. data/lib/qup/adapter/maildir/topic.rb +85 -0
  17. data/lib/qup/adapter/redis.rb +55 -0
  18. data/lib/qup/adapter/redis/connection.rb +32 -0
  19. data/lib/qup/adapter/redis/queue.rb +97 -0
  20. data/lib/qup/adapter/redis/topic.rb +76 -0
  21. data/lib/qup/consumer.rb +42 -0
  22. data/lib/qup/message.rb +18 -0
  23. data/lib/qup/producer.rb +28 -0
  24. data/lib/qup/publisher.rb +30 -0
  25. data/lib/qup/queue_api.rb +124 -0
  26. data/lib/qup/session.rb +111 -0
  27. data/lib/qup/subscriber.rb +28 -0
  28. data/lib/qup/topic_api.rb +92 -0
  29. data/spec/qup/adapter/kestrel/queue_spec.rb +9 -0
  30. data/spec/qup/adapter/kestrel/topic_spec.rb +9 -0
  31. data/spec/qup/adapter/kestrel_context.rb +8 -0
  32. data/spec/qup/adapter/kestrel_spec.rb +8 -0
  33. data/spec/qup/adapter/maildir/queue_spec.rb +9 -0
  34. data/spec/qup/adapter/maildir/topic_spec.rb +9 -0
  35. data/spec/qup/adapter/maildir_context.rb +10 -0
  36. data/spec/qup/adapter/maildir_spec.rb +8 -0
  37. data/spec/qup/adapter/redis/queue_spec.rb +9 -0
  38. data/spec/qup/adapter/redis/topic_spec.rb +9 -0
  39. data/spec/qup/adapter/redis_context.rb +6 -0
  40. data/spec/qup/adapter/redis_spec.rb +8 -0
  41. data/spec/qup/adapter_spec.rb +28 -0
  42. data/spec/qup/consumer_spec.rb +40 -0
  43. data/spec/qup/message_spec.rb +13 -0
  44. data/spec/qup/producer_spec.rb +18 -0
  45. data/spec/qup/queue_api_spec.rb +21 -0
  46. data/spec/qup/session_spec.rb +81 -0
  47. data/spec/qup/shared_adapter_examples.rb +29 -0
  48. data/spec/qup/shared_queue_examples.rb +71 -0
  49. data/spec/qup/shared_topic_examples.rb +57 -0
  50. data/spec/qup/topic_api_spec.rb +21 -0
  51. data/spec/qup_spec.rb +37 -0
  52. data/spec/spec_helper.rb +26 -0
  53. 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,8 @@
1
+ require 'spec_helper'
2
+ require 'qup/shared_adapter_examples'
3
+ require 'qup/adapter/kestrel_context'
4
+
5
+ describe 'Qup::Adapter::Kestrel', :kestrel => true do
6
+ include_context "Qup::Adapter::Kestrel"
7
+ it_behaves_like Qup::Adapter
8
+ end
@@ -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,8 @@
1
+ require 'spec_helper'
2
+ require 'qup/shared_adapter_examples'
3
+ require 'qup/adapter/maildir_context'
4
+
5
+ describe 'Qup::Adapter::Maildir', :maildir => true do
6
+ include_context "Qup::Adapter::Maildir"
7
+ it_behaves_like ::Qup::Adapter
8
+ 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,8 @@
1
+ require 'spec_helper'
2
+ require 'qup/shared_adapter_examples'
3
+ require 'qup/adapter/redis_context'
4
+
5
+ describe 'Qup::Adapter::Redis', :redis => true do
6
+ include_context "Qup::Adapter::Redis"
7
+ it_behaves_like Qup::Adapter
8
+ 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