bunny 0.1.0 → 0.1.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/README.markdown +13 -22
- data/examples/fanout.rb +44 -0
- data/examples/simple.rb +1 -6
- data/examples/simple_ack.rb +33 -0
- data/examples/simple_consumer.rb +36 -0
- data/examples/simple_publisher.rb +33 -0
- data/lib/amqp.rb +5 -1
- data/lib/bunny/exchange.rb +3 -0
- data/lib/bunny/queue.rb +68 -5
- data/spec/exchange_spec.rb +2 -1
- data/spec/queue_spec.rb +6 -5
- metadata +6 -2
data/README.markdown
CHANGED
@@ -4,30 +4,23 @@ Google Group: [bunny-amqp](http://groups.google.com/group/bunny-amqp)
|
|
4
4
|
|
5
5
|
Mailing List: [bunny-amqp-devel](http://rubyforge.org/mailman/listinfo/bunny-amqp-devel)
|
6
6
|
|
7
|
-
Rubyforge: [bunny-amqp](http://bunny-amqp
|
7
|
+
Rubyforge: [bunny-amqp](http://rubyforge.org/projects/bunny-amqp)
|
8
8
|
|
9
9
|
## Announcements
|
10
10
|
|
11
|
-
|
11
|
+
Bunny v0.1.1 has been released. It contains the following changes -
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
b = Bunny.new
|
18
|
-
exch = b.exchange('my_exchange', :type => :fanout)
|
19
|
-
|
20
|
-
If you do not specify a :type option then a default of :direct is used.
|
21
|
-
|
22
|
-
The old way was -
|
23
|
-
|
24
|
-
b = Bunny.new
|
25
|
-
exch = b.exchange(:fanout, 'my_exchange')
|
13
|
+
* Queue#delete method returns ‘QUEUE DELETED’, Exchange#delete method returns ‘EXCHANGE DELETED’, Queue#bind returns ‘BIND SUCCEEDED’, Queue#unbind returns ‘UNBIND SUCCEEDED’
|
14
|
+
* Queue#subscribe method available (see example bunny/examples/simple_consumer.rb)
|
15
|
+
* Queue#status now returns a hash {:message_count, :consumer_count}
|
16
|
+
* Queue#ack works after a Queue#subscribe or Queue#pop if :ack => true was specified
|
26
17
|
|
27
18
|
## About
|
28
19
|
|
29
20
|
*Bunny* is an [AMQP](http://www.amqp.org) (Advanced Message Queuing Protocol) client, written in Ruby, that is intended to allow you to interact with AMQP-compliant message brokers/servers such as [RabbitMQ](http://www.rabbitmq.com) in a synchronous fashion.
|
30
21
|
|
22
|
+
It is based on a great deal of fabulous code from [amqp](http://github.com/tmm1/amqp) by Aman Gupta and [Carrot](http://github.com/famoseagle/carrot) by Amos Elliston.
|
23
|
+
|
31
24
|
You can use *Bunny* to -
|
32
25
|
|
33
26
|
* Create and delete exchanges
|
@@ -43,12 +36,7 @@ You can use *Bunny* to -
|
|
43
36
|
b = Bunny.new(:logging => true)
|
44
37
|
|
45
38
|
# start a communication session with the amqp server
|
46
|
-
|
47
|
-
b.start
|
48
|
-
rescue Exception => e
|
49
|
-
puts 'ERROR - Could not start a session: ' + e
|
50
|
-
exit
|
51
|
-
end
|
39
|
+
b.start
|
52
40
|
|
53
41
|
# declare a queue
|
54
42
|
q = b.queue('test1')
|
@@ -110,12 +98,15 @@ Queue#message_count
|
|
110
98
|
### Return queue consumer count
|
111
99
|
Queue#consumer_count
|
112
100
|
|
113
|
-
### Return queue status (
|
101
|
+
### Return queue status (hash {:message count, :consumer_count})
|
114
102
|
Queue#status
|
115
103
|
|
116
104
|
### Delete a queue from the target server
|
117
105
|
Queue#delete({_options_})
|
118
106
|
|
107
|
+
### Acknowledge receipt of a message
|
108
|
+
Queue#ack
|
109
|
+
|
119
110
|
## Acknowledgements
|
120
111
|
|
121
112
|
This project has borrowed heavily from the following two projects and owes their respective creators and collaborators a whole lot of gratitude:
|
data/examples/fanout.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# fanout.rb
|
2
|
+
|
3
|
+
# Assumes that target message broker/server has a user called 'guest' with a password 'guest'
|
4
|
+
# and that it is running on 'localhost'.
|
5
|
+
|
6
|
+
# If this is not the case, please change the 'Bunny.new' call below to include
|
7
|
+
# the relevant arguments e.g. b = Bunny.new(:user => 'john', :pass => 'doe', :host => 'foobar')
|
8
|
+
|
9
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
10
|
+
|
11
|
+
require 'bunny'
|
12
|
+
|
13
|
+
b = Bunny.new(:logging => true)
|
14
|
+
|
15
|
+
# start a communication session with the amqp server
|
16
|
+
begin
|
17
|
+
b.start
|
18
|
+
rescue Exception => e
|
19
|
+
puts 'ERROR - Could not start a session: ' + e
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
|
23
|
+
# declare queues
|
24
|
+
q1 = b.queue('test_fan1')
|
25
|
+
q2 = b.queue('test_fan2')
|
26
|
+
|
27
|
+
# create a fanout exchange
|
28
|
+
exch = b.exchange('test_fan', :type => :fanout)
|
29
|
+
|
30
|
+
# bind the queues to the exchange
|
31
|
+
q1.bind(exch)
|
32
|
+
q2.bind(exch)
|
33
|
+
|
34
|
+
# publish a message to the exchange
|
35
|
+
exch.publish('This message will be fanned out')
|
36
|
+
|
37
|
+
# get message from the queues
|
38
|
+
msg = q1.pop
|
39
|
+
puts 'This is the message from q1: ' + msg + "\n\n"
|
40
|
+
msg = q2.pop
|
41
|
+
puts 'This is the message from q2: ' + msg + "\n\n"
|
42
|
+
|
43
|
+
# close the client connection
|
44
|
+
b.stop
|
data/examples/simple.rb
CHANGED
@@ -13,12 +13,7 @@ require 'bunny'
|
|
13
13
|
b = Bunny.new(:logging => true)
|
14
14
|
|
15
15
|
# start a communication session with the amqp server
|
16
|
-
|
17
|
-
b.start
|
18
|
-
rescue Exception => e
|
19
|
-
puts 'ERROR - Could not start a session: ' + e
|
20
|
-
exit
|
21
|
-
end
|
16
|
+
b.start
|
22
17
|
|
23
18
|
# declare a queue
|
24
19
|
q = b.queue('test1')
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# simple_ack.rb
|
2
|
+
|
3
|
+
# Assumes that target message broker/server has a user called 'guest' with a password 'guest'
|
4
|
+
# and that it is running on 'localhost'.
|
5
|
+
|
6
|
+
# If this is not the case, please change the 'Bunny.new' call below to include
|
7
|
+
# the relevant arguments e.g. b = Bunny.new(:user => 'john', :pass => 'doe', :host => 'foobar')
|
8
|
+
|
9
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
10
|
+
|
11
|
+
require 'bunny'
|
12
|
+
|
13
|
+
b = Bunny.new(:logging => true)
|
14
|
+
|
15
|
+
# start a communication session with the amqp server
|
16
|
+
b.start
|
17
|
+
|
18
|
+
# declare a queue
|
19
|
+
q = b.queue('test1')
|
20
|
+
|
21
|
+
# publish a message to the queue
|
22
|
+
q.publish('Testing acknowledgements')
|
23
|
+
|
24
|
+
# get message from the queue
|
25
|
+
msg = q.pop(:ack => true)
|
26
|
+
|
27
|
+
# acknowledge receipt of message
|
28
|
+
q.ack
|
29
|
+
|
30
|
+
puts 'This is the message: ' + msg + "\n\n"
|
31
|
+
|
32
|
+
# close the client connection
|
33
|
+
b.stop
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# consumer.rb
|
2
|
+
|
3
|
+
# N.B. To be used in conjunction with publisher.rb - RUN THIS BEFORE simple_publisher.rb
|
4
|
+
|
5
|
+
# Assumes that target message broker/server has a user called 'guest' with a password 'guest'
|
6
|
+
# and that it is running on 'localhost'.
|
7
|
+
|
8
|
+
# If this is not the case, please change the 'Bunny.new' call below to include
|
9
|
+
# the relevant arguments e.g. b = Bunny.new(:user => 'john', :pass => 'doe', :host => 'foobar')
|
10
|
+
|
11
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
12
|
+
|
13
|
+
require 'bunny'
|
14
|
+
|
15
|
+
b = Bunny.new(:logging => true)
|
16
|
+
|
17
|
+
# start a communication session with the amqp server
|
18
|
+
b.start
|
19
|
+
|
20
|
+
# create/get queue
|
21
|
+
q = b.queue('po_box')
|
22
|
+
|
23
|
+
# create/get exchange
|
24
|
+
exch = b.exchange('sorting_room')
|
25
|
+
|
26
|
+
# bind queue to exchange
|
27
|
+
q.bind(exch, :key => 'fred')
|
28
|
+
|
29
|
+
# subscribe to queue
|
30
|
+
msg = q.subscribe(:consumer_tag => 'testtag1')
|
31
|
+
|
32
|
+
# output received message
|
33
|
+
puts msg
|
34
|
+
|
35
|
+
# close the connection
|
36
|
+
b.stop
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# simple_publisher.rb
|
2
|
+
|
3
|
+
# N.B. To be used in conjunction with simple_consumer.rb
|
4
|
+
|
5
|
+
# Assumes that target message broker/server has a user called 'guest' with a password 'guest'
|
6
|
+
# and that it is running on 'localhost'.
|
7
|
+
|
8
|
+
# If this is not the case, please change the 'Bunny.new' call below to include
|
9
|
+
# the relevant arguments e.g. b = Bunny.new(:user => 'john', :pass => 'doe', :host => 'foobar')
|
10
|
+
|
11
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
12
|
+
|
13
|
+
require 'bunny'
|
14
|
+
|
15
|
+
b = Bunny.new(:logging => true)
|
16
|
+
|
17
|
+
# start a communication session with the amqp server
|
18
|
+
b.start
|
19
|
+
|
20
|
+
# create/get queue
|
21
|
+
q = b.queue('po_box')
|
22
|
+
|
23
|
+
# create/get exchange
|
24
|
+
exch = b.exchange('sorting_room')
|
25
|
+
|
26
|
+
# bind queue to exchange
|
27
|
+
q.bind(exch, :key => 'fred')
|
28
|
+
|
29
|
+
# publish message to exchange
|
30
|
+
exch.publish('This is a message from the publisher', :key => 'fred')
|
31
|
+
|
32
|
+
# message should now be picked up by the consumer so we can stop
|
33
|
+
b.stop
|
data/lib/amqp.rb
CHANGED
@@ -3,10 +3,14 @@ module AMQP
|
|
3
3
|
require "amqp/#{file}"
|
4
4
|
end
|
5
5
|
|
6
|
-
#
|
6
|
+
# return messages
|
7
7
|
CONNECTED = 'CONNECTED'
|
8
8
|
NOT_CONNECTED = 'NOT CONNECTED'
|
9
9
|
QUEUE_EMPTY = 'QUEUE EMPTY'
|
10
|
+
QUEUE_DELETED = 'QUEUE DELETED'
|
11
|
+
EXCHANGE_DELETED = 'EXCHANGE DELETED'
|
12
|
+
BIND_SUCCEEDED = 'BIND SUCCEEDED'
|
13
|
+
UNBIND_SUCCEEDED = 'UNBIND SUCCEEDED'
|
10
14
|
|
11
15
|
# specific error definitions
|
12
16
|
class ProtocolError < StandardError; end
|
data/lib/bunny/exchange.rb
CHANGED
data/lib/bunny/queue.rb
CHANGED
@@ -24,15 +24,28 @@ class Bunny
|
|
24
24
|
|
25
25
|
raise ProtocolError, "Error declaring queue #{name}" unless client.next_method.is_a?(Protocol::Queue::DeclareOk)
|
26
26
|
end
|
27
|
+
|
28
|
+
def ack
|
29
|
+
client.send_frame(
|
30
|
+
Protocol::Basic::Ack.new(:delivery_tag => delivery_tag)
|
31
|
+
)
|
32
|
+
|
33
|
+
# reset delivery tag
|
34
|
+
self.delivery_tag = nil
|
35
|
+
end
|
27
36
|
|
28
37
|
def pop(opts = {})
|
38
|
+
|
29
39
|
# do we want the message header?
|
30
40
|
hdr = opts.delete(:header)
|
31
41
|
|
42
|
+
# do we want to have to provide an acknowledgement?
|
43
|
+
ack = opts.delete(:ack)
|
44
|
+
|
32
45
|
client.send_frame(
|
33
46
|
Protocol::Basic::Get.new({ :queue => name,
|
34
47
|
:consumer_tag => name,
|
35
|
-
:no_ack => !
|
48
|
+
:no_ack => !ack,
|
36
49
|
:nowait => true }.merge(opts))
|
37
50
|
)
|
38
51
|
|
@@ -44,6 +57,9 @@ class Bunny
|
|
44
57
|
raise ProtocolError, "Error getting message from queue #{name}"
|
45
58
|
end
|
46
59
|
|
60
|
+
# get delivery tag to use for acknowledge
|
61
|
+
self.delivery_tag = method.delivery_tag if ack
|
62
|
+
|
47
63
|
header = client.next_payload
|
48
64
|
msg = client.next_payload
|
49
65
|
raise MessageError, 'unexpected length' if msg.length < header.size
|
@@ -57,20 +73,58 @@ class Bunny
|
|
57
73
|
end
|
58
74
|
|
59
75
|
def message_count
|
60
|
-
status
|
76
|
+
s = status
|
77
|
+
s[:message_count]
|
61
78
|
end
|
62
79
|
|
63
80
|
def consumer_count
|
64
|
-
status
|
81
|
+
s = status
|
82
|
+
s[:consumer_count]
|
65
83
|
end
|
66
84
|
|
67
|
-
def status(opts = {}
|
85
|
+
def status(opts = {})
|
68
86
|
client.send_frame(
|
69
87
|
Protocol::Queue::Declare.new({ :queue => name, :passive => true }.merge(opts))
|
70
88
|
)
|
71
89
|
method = client.next_method
|
72
|
-
|
90
|
+
{:message_count => method.message_count, :consumer_count => method.consumer_count}
|
73
91
|
end
|
92
|
+
|
93
|
+
def subscribe(opts = {})
|
94
|
+
consumer_tag = opts[:consumer_tag] || name
|
95
|
+
|
96
|
+
# ignore the :nowait option if passed, otherwise program will not wait for a
|
97
|
+
# message to get to the server causing an error
|
98
|
+
opts.delete(:nowait)
|
99
|
+
|
100
|
+
# do we want the message header?
|
101
|
+
hdr = opts.delete(:header)
|
102
|
+
|
103
|
+
# do we want to have to provide an acknowledgement?
|
104
|
+
ack = opts.delete(:ack)
|
105
|
+
|
106
|
+
client.send_frame(
|
107
|
+
Protocol::Basic::Consume.new({ :queue => name,
|
108
|
+
:consumer_tag => consumer_tag,
|
109
|
+
:no_ack => !ack,
|
110
|
+
:nowait => false }.merge(opts))
|
111
|
+
)
|
112
|
+
|
113
|
+
raise ProtocolError,
|
114
|
+
"Error subscribing to queue #{name}" unless
|
115
|
+
client.next_method.is_a?(Protocol::Basic::ConsumeOk)
|
116
|
+
|
117
|
+
method = client.next_method
|
118
|
+
|
119
|
+
# get delivery tag to use for acknowledge
|
120
|
+
self.delivery_tag = method.delivery_tag if ack
|
121
|
+
|
122
|
+
header = client.next_payload
|
123
|
+
msg = client.next_payload
|
124
|
+
raise MessageError, 'unexpected length' if msg.length < header.size
|
125
|
+
|
126
|
+
hdr ? {:header => header, :payload => msg} : msg
|
127
|
+
end
|
74
128
|
|
75
129
|
def bind(exchange, opts = {})
|
76
130
|
exchange = exchange.respond_to?(:name) ? exchange.name : exchange
|
@@ -90,6 +144,9 @@ class Bunny
|
|
90
144
|
raise ProtocolError,
|
91
145
|
"Error binding queue #{name}" unless
|
92
146
|
client.next_method.is_a?(Protocol::Queue::BindOk)
|
147
|
+
|
148
|
+
# return message
|
149
|
+
BIND_SUCCEEDED
|
93
150
|
end
|
94
151
|
|
95
152
|
def unbind(exchange, opts = {})
|
@@ -107,6 +164,9 @@ class Bunny
|
|
107
164
|
raise ProtocolError,
|
108
165
|
"Error unbinding queue #{name}" unless
|
109
166
|
client.next_method.is_a?(Protocol::Queue::UnbindOk)
|
167
|
+
|
168
|
+
# return message
|
169
|
+
UNBIND_SUCCEEDED
|
110
170
|
end
|
111
171
|
|
112
172
|
def delete(opts = {})
|
@@ -123,6 +183,9 @@ class Bunny
|
|
123
183
|
client.next_method.is_a?(Protocol::Queue::DeleteOk)
|
124
184
|
|
125
185
|
client.queues.delete(name)
|
186
|
+
|
187
|
+
# return confirmation
|
188
|
+
QUEUE_DELETED
|
126
189
|
end
|
127
190
|
|
128
191
|
private
|
data/spec/exchange_spec.rb
CHANGED
data/spec/queue_spec.rb
CHANGED
@@ -22,25 +22,25 @@ describe Bunny::Queue do
|
|
22
22
|
it "should ignore the :nowait option when binding to an exchange" do
|
23
23
|
exch = @b.exchange('direct_exch')
|
24
24
|
q = @b.queue('test0')
|
25
|
-
q.bind(exch, :nowait => true)
|
25
|
+
q.bind(exch, :nowait => true).should == 'BIND SUCCEEDED'
|
26
26
|
end
|
27
27
|
|
28
28
|
it "should be able to bind to an exchange" do
|
29
29
|
exch = @b.exchange('direct_exch')
|
30
30
|
q = @b.queue('test1')
|
31
|
-
q.bind(exch)
|
31
|
+
q.bind(exch).should == 'BIND SUCCEEDED'
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should ignore the :nowait option when unbinding from an exchange" do
|
35
35
|
exch = @b.exchange('direct_exch')
|
36
36
|
q = @b.queue('test0')
|
37
|
-
q.unbind(exch, :nowait => true)
|
37
|
+
q.unbind(exch, :nowait => true).should == 'UNBIND SUCCEEDED'
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should be able to unbind from an exchange" do
|
41
41
|
exch = @b.exchange('direct_exch')
|
42
42
|
q = @b.queue('test1')
|
43
|
-
q.unbind(exch)
|
43
|
+
q.unbind(exch).should == 'UNBIND SUCCEEDED'
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should be able to publish a message" do
|
@@ -76,7 +76,8 @@ describe Bunny::Queue do
|
|
76
76
|
|
77
77
|
it "should be able to be deleted" do
|
78
78
|
q = @b.queue('test1')
|
79
|
-
q.delete
|
79
|
+
res = q.delete
|
80
|
+
res.should == 'QUEUE DELETED'
|
80
81
|
@b.queues.has_key?('test1').should be false
|
81
82
|
end
|
82
83
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bunny
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Duncan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-27 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -36,6 +36,10 @@ files:
|
|
36
36
|
- lib/amqp/spec.rb
|
37
37
|
- lib/bunny.rb
|
38
38
|
- examples/simple.rb
|
39
|
+
- examples/fanout.rb
|
40
|
+
- examples/simple_consumer.rb
|
41
|
+
- examples/simple_publisher.rb
|
42
|
+
- examples/simple_ack.rb
|
39
43
|
- spec/bunny_spec.rb
|
40
44
|
- spec/exchange_spec.rb
|
41
45
|
- spec/queue_spec.rb
|