bunny 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|