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.
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