bunny 0.5.1 → 0.5.2

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.
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{bunny}
3
- s.version = "0.5.1"
3
+ s.version = "0.5.2"
4
4
  s.authors = ["Chris Duncan"]
5
- s.date = %q{2009-08-08}
5
+ s.date = %q{2009-08-17}
6
6
  s.description = %q{Another synchronous Ruby AMQP client}
7
7
  s.email = %q{celldee@gmail.com}
8
8
  s.rubyforge_project = %q{bunny-amqp}
@@ -1,4 +1,4 @@
1
- # simple.rb
1
+ # simple_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple.rb
1
+ # simple_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_ack.rb
1
+ # simple_ack_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_ack.rb
1
+ # simple_ack_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # consumer.rb
1
+ # simple_consumer_08.rb
2
2
 
3
3
  # N.B. To be used in conjunction with simple_publisher.rb
4
4
 
@@ -13,15 +13,18 @@
13
13
  #
14
14
  # Open up two console windows start this program in one of them by typing -
15
15
  #
16
- # ruby simple_consumer.rb
16
+ # ruby simple_consumer_08.rb
17
17
  #
18
18
  # Then switch to the other console window and type -
19
19
  #
20
- # ruby simple_publisher.rb
20
+ # ruby simple_publisher_08.rb
21
21
  #
22
22
  # A message will be printed out by the simple_consumer and it will wait for the next message
23
+ # until the timeout interval is reached.
23
24
  #
24
- # Run simple_publisher 3 more times. After the last run simple_consumer will stop.
25
+ # Run simple_publisher as many times as you like. When you want the program to stop just stop
26
+ # sending messages and the subscribe loop will timeout after 30 seconds, the program will
27
+ # unsubscribe from the queue and close the connection to the server.
25
28
 
26
29
  $:.unshift File.dirname(__FILE__) + '/../lib'
27
30
 
@@ -45,11 +48,14 @@ q.bind(exch, :key => 'fred')
45
48
  i = 1
46
49
 
47
50
  # subscribe to queue
48
- q.subscribe(:consumer_tag => 'testtag1') do |msg|
49
- puts i.to_s + ': ' + msg
51
+ ret = q.subscribe(:consumer_tag => 'testtag1', :timeout => 30) do |msg|
52
+ puts "#{i.to_s}: #{msg}"
50
53
  i+=1
51
- q.unsubscribe(:consumer_tag => 'testtag1') if i == 5
52
54
  end
53
55
 
54
- # close the connection
55
- b.stop
56
+ if ret == :timed_out
57
+ puts '==== simple_consumer_08.rb timed out - closing down ===='
58
+ q.unsubscribe(:consumer_tag => 'testtag1')
59
+ # close the connection
60
+ b.stop
61
+ end
@@ -1,4 +1,4 @@
1
- # consumer.rb
1
+ # simple_consumer_09.rb
2
2
 
3
3
  # N.B. To be used in conjunction with simple_publisher.rb
4
4
 
@@ -13,15 +13,18 @@
13
13
  #
14
14
  # Open up two console windows start this program in one of them by typing -
15
15
  #
16
- # ruby simple_consumer.rb
16
+ # ruby simple_consumer_09.rb
17
17
  #
18
18
  # Then switch to the other console window and type -
19
19
  #
20
- # ruby simple_publisher.rb
20
+ # ruby simple_publisher_09.rb
21
21
  #
22
22
  # A message will be printed out by the simple_consumer and it will wait for the next message
23
+ # until the timeout interval is reached.
23
24
  #
24
- # Run simple_publisher 3 more times. After the last run simple_consumer will stop.
25
+ # Run simple_publisher as many times as you like. When you want the program to stop just stop
26
+ # sending messages and the subscribe loop will timeout after 30 seconds, the program will
27
+ # unsubscribe from the queue and close the connection to the server.
25
28
 
26
29
  $:.unshift File.dirname(__FILE__) + '/../lib'
27
30
 
@@ -45,11 +48,14 @@ q.bind(exch, :key => 'fred')
45
48
  i = 1
46
49
 
47
50
  # subscribe to queue
48
- q.subscribe(:consumer_tag => 'testtag1') do |msg|
49
- puts i.to_s + ': ' + msg
51
+ ret = q.subscribe(:consumer_tag => 'testtag1', :timeout => 30) do |msg|
52
+ puts "#{i.to_s}: #{msg}"
50
53
  i+=1
51
- q.unsubscribe(:consumer_tag => 'testtag1') if i == 5
52
54
  end
53
55
 
54
- # close the connection
55
- b.stop
56
+ if ret == :timed_out
57
+ puts '==== simple_consumer_09.rb timed out - closing down ===='
58
+ q.unsubscribe(:consumer_tag => 'testtag1')
59
+ # close the connection
60
+ b.stop
61
+ end
@@ -1,4 +1,4 @@
1
- # fanout.rb
1
+ # simple_fanout_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # fanout.rb
1
+ # simple_fanout_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_headers.rb
1
+ # simple_headers_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_headers.rb
1
+ # simple_headers_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,6 +1,6 @@
1
- # simple_publisher.rb
1
+ # simple_publisher_08.rb
2
2
 
3
- # N.B. To be used in conjunction with simple_consumer.rb. See simple_consumer.rb for explanation.
3
+ # N.B. To be used in conjunction with simple_consumer_08.rb. See simple_consumer.rb for explanation.
4
4
 
5
5
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
6
6
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_topic.rb
1
+ # simple_topic_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_topic.rb
1
+ # simple_topic_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -12,7 +12,7 @@ module Bunny
12
12
  class ConnectionError < StandardError; end
13
13
  class MessageError < StandardError; end
14
14
 
15
- VERSION = '0.5.1'
15
+ VERSION = '0.5.2'
16
16
 
17
17
  # Returns the Bunny version number
18
18
 
@@ -34,6 +34,9 @@ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_
34
34
  servers, the server may respond to a Connection::Open method with a Connection::Redirect. The insist
35
35
  option, if set to _true_, tells the server that the client is insisting on a connection to the
36
36
  specified server.
37
+ * <tt>:frame_max => maximum frame size in bytes (default = 131072)</tt>
38
+ * <tt>:channel_max => maximum number of channels (default = 0 no maximum)</tt>
39
+ * <tt>:heartbeat => number of seconds (default = 0 no heartbeat)</tt>
37
40
 
38
41
  =end
39
42
 
@@ -50,10 +53,16 @@ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_
50
53
  @status = :not_connected
51
54
  @frame_max = opts[:frame_max] || 131072
52
55
  @channel_max = opts[:channel_max] || 0
56
+ @heartbeat = opts[:heartbeat] || 0
53
57
  @logger = nil
54
58
  create_logger if @logging
59
+ @channels = []
55
60
  # Create channel 0
56
61
  @channel = Bunny::Channel.new(self, true)
62
+ @exchanges = {}
63
+ @queues = {}
64
+ @heartbeat_in = false
65
+ @connecting = false
57
66
  end
58
67
 
59
68
  =begin rdoc
@@ -91,18 +100,6 @@ Exchange
91
100
 
92
101
  === DESCRIPTION:
93
102
 
94
- Returns hash of exchanges declared by Bunny.
95
-
96
- =end
97
-
98
- def exchanges
99
- @exchanges ||= {}
100
- end
101
-
102
- =begin rdoc
103
-
104
- === DESCRIPTION:
105
-
106
103
  Declares a queue to the broker/server. If the queue does not exist, a new one is created
107
104
  using the arguments passed in. If the queue already exists, a reference to it is created, provided
108
105
  that the arguments passed in do not conflict with the existing attributes of the queue. If an error
@@ -141,19 +138,16 @@ Queue
141
138
 
142
139
  return queues[name] if queues.has_key?(name)
143
140
 
144
- queue = Bunny::Queue.new(self, name, opts)
141
+ Bunny::Queue.new(self, name, opts)
145
142
  end
146
-
147
- =begin rdoc
148
-
149
- === DESCRIPTION:
150
-
151
- Returns hash of queues declared by Bunny.
152
-
153
- =end
154
143
 
155
- def queues
156
- @queues ||= {}
144
+ def send_heartbeat
145
+ # Create a new heartbeat frame
146
+ hb = Qrack::Transport::Heartbeat.new('')
147
+ # Channel 0 must be used
148
+ switch_channel(0) if @channel.number > 0
149
+ # Send the heartbeat to server
150
+ send_frame(hb)
157
151
  end
158
152
 
159
153
  def send_frame(*args)
@@ -169,18 +163,72 @@ Returns hash of queues declared by Bunny.
169
163
  nil
170
164
  end
171
165
 
172
- def next_frame
173
- frame = Qrack::Transport::Frame.parse(buffer)
174
- @logger.info("received") { frame } if @logging
175
- frame
166
+ def next_frame(opts = {})
167
+ secs = opts[:timeout] || 0
168
+
169
+ begin
170
+ Timeout::timeout(secs) do
171
+ @frame = Qrack::Transport::Frame.parse(buffer)
172
+ end
173
+ rescue Timeout::Error
174
+ return :timed_out
175
+ end
176
+
177
+ @logger.info("received") { @frame } if @logging
178
+
179
+ raise Bunny::ConnectionError, 'No connection to server' if (@frame.nil? and !connecting?)
180
+
181
+ if @frame.is_a?(Qrack::Transport::Heartbeat)
182
+ @heartbeat_in = true
183
+ next_frame
184
+ end
185
+
186
+ @frame
176
187
  end
177
188
 
178
189
  def next_payload
179
- frame = next_frame
190
+ frame = next_frame
180
191
  frame.payload
181
192
  end
182
193
 
183
194
  alias next_method next_payload
195
+
196
+ =begin rdoc
197
+
198
+ === DESCRIPTION:
199
+
200
+ Checks to see whether or not an undeliverable message has been returned as a result of a publish
201
+ with the <tt>:immediate</tt> or <tt>:mandatory</tt> options.
202
+
203
+ ==== OPTIONS:
204
+
205
+ * <tt>:timeout => number of seconds (default = 0.1) - The method will wait for a return
206
+ message until this timeout interval is reached.
207
+
208
+ ==== RETURNS:
209
+
210
+ <tt>:no_return</tt> if message was not returned before timeout .
211
+ <tt>{:header, :return_details, :payload}</tt> if message is returned. <tt>:return_details</tt> is
212
+ a hash <tt>{:reply_code, :reply_text, :exchange, :routing_key}</tt>.
213
+
214
+ =end
215
+
216
+ def returned_message(opts = {})
217
+ secs = opts[:timeout] || 0.1
218
+ frame = next_frame(:timeout => secs)
219
+
220
+ if frame.is_a?(Symbol)
221
+ return :no_return if frame == :timed_out
222
+ end
223
+
224
+ method = frame.payload
225
+ header = next_payload
226
+ msg = next_payload
227
+ raise Bunny::MessageError, 'unexpected length' if msg.length < header.size
228
+
229
+ # Return the message and related info
230
+ {:header => header, :payload => msg, :return_details => method.arguments}
231
+ end
184
232
 
185
233
  =begin rdoc
186
234
 
@@ -197,12 +245,9 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
197
245
 
198
246
  def close
199
247
  # Close all active channels
200
- channels.each_value do |c|
248
+ channels.each do |c|
201
249
  c.close if c.open?
202
250
  end
203
-
204
- # Set client channel to zero
205
- self.channel = channels[0]
206
251
 
207
252
  # Close connection to AMQP server
208
253
  close_connection
@@ -235,6 +280,8 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
235
280
  =end
236
281
 
237
282
  def start_session
283
+ @connecting = true
284
+
238
285
  loop do
239
286
  # Create/get socket
240
287
  socket
@@ -246,15 +293,17 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
246
293
  break if open_connection == :ok
247
294
  end
248
295
 
249
- # Open a channel
250
- self.channel = get_channel
251
- channel.open
296
+ # Open another channel because channel zero is used for specific purposes
297
+ c = create_channel()
298
+ c.open
252
299
 
253
300
  # Get access ticket
254
301
  request_access
255
302
 
303
+ @connecting = false
304
+
256
305
  # return status
257
- status
306
+ @status = :connected
258
307
  end
259
308
 
260
309
  alias start start_session
@@ -263,7 +312,7 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
263
312
 
264
313
  === DESCRIPTION:
265
314
 
266
- Asks the broker to redeliver all unacknowledged messages on a specifieid channel. Zero or
315
+ Asks the broker to redeliver all unacknowledged messages on a specified channel. Zero or
267
316
  more messages may be redelivered.
268
317
 
269
318
  ==== Options:
@@ -388,23 +437,36 @@ after a rollback.
388
437
  @logging = bool
389
438
  create_logger if @logging
390
439
  end
391
-
392
- def channels
393
- @channels ||= {}
394
- end
395
440
 
396
- def get_channel
397
- channels.each_value do |c|
441
+ def create_channel
442
+ channels.each do |c|
398
443
  return c if (!c.open? and c.number != 0)
399
444
  end
400
445
  # If no channel to re-use instantiate new one
401
446
  Bunny::Channel.new(self)
402
447
  end
403
448
 
449
+ def switch_channel(chann)
450
+ if (0...channels.size).include? chann
451
+ @channel = channels[chann]
452
+ chann
453
+ else
454
+ raise RuntimeError, "Invalid channel number - #{chann}"
455
+ end
456
+ end
457
+
458
+ def connecting?
459
+ connecting
460
+ end
461
+
404
462
  def init_connection
405
463
  write(Qrack::Protocol::HEADER)
406
464
  write([1, 1, Qrack::Protocol::VERSION_MAJOR, Qrack::Protocol::VERSION_MINOR].pack('C4'))
407
- raise Bunny::ProtocolError, 'Connection initiation failed' unless next_method.is_a?(Qrack::Protocol::Connection::Start)
465
+
466
+ frame = next_frame
467
+ if frame.nil? or !frame.payload.is_a?(Qrack::Protocol::Connection::Start)
468
+ raise Bunny::ProtocolError, 'Connection initiation failed'
469
+ end
408
470
  end
409
471
 
410
472
  def open_connection
@@ -417,12 +479,14 @@ after a rollback.
417
479
  )
418
480
  )
419
481
 
420
- method = next_method
421
- raise Bunny::ProtocolError, "Connection failed - user: #{@user}, pass: #{@pass}" if method.nil?
482
+ frame = next_frame
483
+ raise Bunny::ProtocolError, "Connection failed - user: #{@user}, pass: #{@pass}" if frame.nil?
484
+
485
+ method = frame.payload
422
486
 
423
487
  if method.is_a?(Qrack::Protocol::Connection::Tune)
424
488
  send_frame(
425
- Qrack::Protocol::Connection::TuneOk.new( :channel_max => @channel_max, :frame_max => @frame_max, :heartbeat => 0)
489
+ Qrack::Protocol::Connection::TuneOk.new( :channel_max => @channel_max, :frame_max => @frame_max, :heartbeat => @heartbeat)
426
490
  )
427
491
  end
428
492
 
@@ -444,6 +508,9 @@ after a rollback.
444
508
  end
445
509
 
446
510
  def close_connection
511
+ # Set client channel to zero
512
+ switch_channel(0)
513
+
447
514
  send_frame(
448
515
  Qrack::Protocol::Connection::Close.new(:reply_code => 200, :reply_text => 'Goodbye', :class_id => 0, :method_id => 0)
449
516
  )
@@ -477,9 +544,7 @@ after a rollback.
477
544
  def socket
478
545
  return @socket if @socket and (@status == :connected) and not @socket.closed?
479
546
 
480
- begin
481
- @status = :not_connected
482
-
547
+ begin
483
548
  # Attempt to connect.
484
549
  @socket = timeout(CONNECT_TIMEOUT) do
485
550
  TCPSocket.new(host, port)
@@ -488,7 +553,6 @@ after a rollback.
488
553
  if Socket.constants.include? 'TCP_NODELAY'
489
554
  @socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
490
555
  end
491
- @status = :connected
492
556
  rescue => e
493
557
  @status = :not_connected
494
558
  raise Bunny::ServerDownError, e.message