amqp 0.8.0.rc12 → 0.8.0.rc13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.travis.yml +1 -1
  2. data/.yardopts +2 -1
  3. data/CONTRIBUTORS +29 -22
  4. data/Gemfile +2 -1
  5. data/README.md +241 -0
  6. data/amqp.gemspec +7 -5
  7. data/bin/set_test_suite_realms_up.sh +6 -6
  8. data/docs/08Migration.textile +3 -1
  9. data/docs/Bindings.textile +3 -1
  10. data/docs/Clustering.textile +4 -0
  11. data/docs/ConnectingToTheBroker.textile +108 -86
  12. data/docs/ConnectionEncryptionWithTLS.textile +3 -1
  13. data/docs/DocumentationGuidesIndex.textile +24 -2
  14. data/docs/Durability.textile +22 -1
  15. data/docs/ErrorHandling.textile +21 -1
  16. data/docs/Exchanges.textile +181 -9
  17. data/docs/GettingStarted.textile +65 -167
  18. data/docs/Queues.textile +400 -355
  19. data/docs/RabbitMQVersions.textile +34 -3
  20. data/docs/Routing.textile +4 -1
  21. data/docs/RunningTests.textile +116 -0
  22. data/docs/Troubleshooting.textile +131 -0
  23. data/docs/VendorSpecificExtensions.textile +20 -0
  24. data/examples/channels/qos_aka_prefetch.rb +3 -3
  25. data/examples/channels/qos_aka_prefetch_without_callback.rb +2 -2
  26. data/examples/error_handling/channel_level_exception.rb +1 -1
  27. data/examples/error_handling/channel_level_exception_with_multiple_channels_involved.rb +1 -0
  28. data/examples/error_handling/connection_level_exception.rb +26 -0
  29. data/examples/error_handling/global_channel_level_exception_handler.rb +3 -3
  30. data/examples/exchanges/autodeletion_of_exchanges.rb +37 -0
  31. data/examples/extensions/rabbitmq/per_queue_message_ttl.rb +3 -3
  32. data/examples/extensions/rabbitmq/publisher_confirmations_with_transient_messages.rb +2 -2
  33. data/examples/guides/getting_started/{03_babblr.rb → 03_blabbr.rb} +0 -0
  34. data/examples/guides/queues/01a_declaring_a_server_named_queue_using_queue_constructor.rb +18 -0
  35. data/examples/guides/queues/01b_declaring_a_queue_using_queue_constructor.rb +18 -0
  36. data/examples/guides/queues/02a_declaring_a_durable_shared_queue.rb +18 -0
  37. data/examples/guides/queues/02b_declaring_a_durable_shared_queue.rb +18 -0
  38. data/examples/guides/queues/03a_declaring_a_temporary_exclusive_queue.rb +19 -0
  39. data/examples/guides/queues/03b_declaring_a_temporary_exclusive_queue.rb +18 -0
  40. data/examples/guides/queues/{05_binding_a_queue_using_exchange_instance.rb → 04_bind_a_queue_using_exchange_instance.rb} +2 -2
  41. data/examples/guides/queues/{06_biding_a_queue_using_exchange_name_string.rb → 05_bind_a_queue_using_exchange_name.rb} +2 -2
  42. data/examples/guides/queues/{07_subscribing_to_receive_messages.rb → 06_subscribe_to_receive_messages.rb} +1 -1
  43. data/examples/guides/queues/{08_poll_for_messages.rb → 07_fetch_a_message_from_the_queue.rb} +1 -1
  44. data/examples/guides/queues/{09_unsubscribing_a_consumer.rb → 08_unsubscribing_a_consumer.rb} +0 -0
  45. data/examples/guides/queues/{10_unbinding_from_exchange.rb → 09_unbinding_from_exchange.rb} +2 -2
  46. data/examples/guides/queues/{11_purge_a_queue.rb → 10_purge_a_queue.rb} +2 -2
  47. data/examples/guides/queues/{12_deleting_a_queue.rb → 11_deleting_a_queue.rb} +2 -2
  48. data/examples/hello_world.rb +1 -1
  49. data/examples/hello_world_with_an_empty_string.rb +33 -0
  50. data/examples/issues/amq_client_issue_7.rb +31 -0
  51. data/examples/issues/amq_protocol_issue_14.rb +46 -0
  52. data/examples/issues/issue_75.rb +23 -0
  53. data/examples/issues/issue_79.rb +35 -0
  54. data/examples/issues/issue_80.rb +40 -0
  55. data/examples/publishing/{publishing_and_immediately_stopping_event_loop.rb → publishing_a_one_off_message.rb} +9 -12
  56. data/examples/publishing/publishing_callback.rb +52 -0
  57. data/examples/publishing/returned_messages.rb +3 -3
  58. data/examples/queues/accessing_message_metadata.rb +60 -0
  59. data/examples/queues/queue_status.rb +0 -7
  60. data/examples/queues/rejecting_messages_without_requeueuing.rb +47 -0
  61. data/examples/queues/using_explicit_acknowledgements.rb +96 -0
  62. data/examples/routing/headers_routing.rb +54 -0
  63. data/lib/amqp/channel.rb +245 -40
  64. data/lib/amqp/client.rb +23 -11
  65. data/lib/amqp/exchange.rb +58 -41
  66. data/lib/amqp/queue.rb +66 -13
  67. data/lib/amqp/version.rb +1 -1
  68. data/spec/integration/authentication_spec.rb +5 -5
  69. data/spec/integration/basic_get_spec.rb +1 -1
  70. data/spec/integration/channel_close_spec.rb +10 -3
  71. data/spec/integration/queue_declaration_spec.rb +26 -5
  72. data/spec/integration/topic_subscription_spec.rb +1 -1
  73. data/spec/unit/amqp/client_spec.rb +7 -54
  74. data/tasks.rb +1 -8
  75. metadata +64 -23
  76. data/README.textile +0 -229
@@ -40,32 +40,42 @@ module AMQP
40
40
  end
41
41
  end
42
42
 
43
- # Parses AMQP connection string and returns it's components as a hash.
43
+ # Parses AMQP connection URI and returns its components as a hash.
44
44
  #
45
45
  # h2. vhost naming schemes
46
46
  #
47
- # AMQP 0.9.1 spec does not define what vhost naming scheme should be. RabbitMQ and Apache Qpid use different schemes
48
- # (Qpid said to have two) but the bottom line is: even though some brokers use / as the default vhost, it can be *any string*.
49
- # Host (and optional port) part must be separated from vhost (path component) with a slash character (/).
47
+ # It is convenient to be able to specify the AMQP connection
48
+ # parameters as a URI string, and various "amqp" URI schemes
49
+ # exist. Unfortunately, there is no standard for these URIs, so
50
+ # while the schemes share the basic idea, they differ in some
51
+ # details. This implementation aims to encourage URIs that work
52
+ # as widely as possible.
50
53
  #
51
- # This method will also unescape path part of the URI.
54
+ # The URI scheme should be "amqp", or "amqps" if SSL is required.
55
+ #
56
+ # The host, port, username and password are represented in the
57
+ # authority component of the URI in the same way as in http URIs.
58
+ #
59
+ # The vhost is obtained from the first segment of the path, with the
60
+ # leading slash removed. The path should contain only a single
61
+ # segment (i.e, the only slash in it should be the leading one).
62
+ # If the vhost is to include slashes or other reserved URI
63
+ # characters, these should be percent-escaped.
52
64
  #
53
65
  # @example How vhost is parsed
54
66
  #
55
67
  # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com") # => vhost is nil, so default (/) will be used
56
68
  # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com/") # => vhost is an empty string
57
- # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com//") # => vhost is /
58
- # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com//vault") # => vhost is /vault
59
69
  # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com/%2Fvault") # => vhost is /vault
60
70
  # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com/production") # => vhost is production
61
71
  # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com/a.b.c") # => vhost is a.b.c
62
- # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com///a/b/c/d") # => vhost is //a/b/c/d
72
+ # AMQP::Client.parse_connection_uri("amqp://dev.rabbitmq.com/foo/bar") # => ArgumentError
63
73
  #
64
74
  #
65
75
  # @param [String] connection_string AMQP connection URI, à la JDBC connection string. For example: amqp://bus.megacorp.internal:5877.
66
76
  # @return [Hash] Connection parameters (:username, :password, :vhost, :host, :port, :ssl)
67
77
  #
68
- # @raise [ArgumentError] When connection URI schema is not amqp or amqps.
78
+ # @raise [ArgumentError] When connection URI schema is not amqp or amqps, or the path contains multiple segments
69
79
  #
70
80
  # @see http://bit.ly/ks8MXK Connecting to The Broker documentation guide
71
81
  # @api public
@@ -75,14 +85,16 @@ module AMQP
75
85
 
76
86
  opts = {}
77
87
 
78
-
79
88
  opts[:scheme] = uri.scheme
80
89
  opts[:user] = URI.unescape(uri.user) if uri.user
81
90
  opts[:pass] = URI.unescape(uri.password) if uri.password
82
- opts[:vhost] = URI.unescape($1) if uri.path =~ %r{^/(.*)}
83
91
  opts[:host] = uri.host if uri.host
84
92
  opts[:port] = uri.port || AMQP_PORTS[uri.scheme]
85
93
  opts[:ssl] = uri.scheme == AMQPS
94
+ if uri.path =~ %r{^/(.*)}
95
+ raise ArgumentError.new("#{uri} has multiple-segment path; please percent-encode any slashes in the vhost name (e.g. /production => %2Fproduction). Learn more at http://bit.ly/amqp-gem-and-connection-uris") if $1.index('/')
96
+ opts[:vhost] = URI.unescape($1)
97
+ end
86
98
 
87
99
  opts
88
100
  end
@@ -366,8 +366,10 @@ module AMQP
366
366
 
367
367
  # Publishes message to the exchange. The message will be routed to queues by the exchange
368
368
  # and distributed to any active consumers. Routing logic is determined by exchange type and
369
- # configuration as well as message attributes (like :routing_key).
369
+ # configuration as well as message attributes (like :routing_key or message headers).
370
370
  #
371
+ # Published data is opaque and not modified by Ruby amqp gem in any way. Serialization of data with JSON, Thrift, BSON
372
+ # or similar libraries before publishing is very common.
371
373
  #
372
374
  #
373
375
  # h2. Data serialization
@@ -375,14 +377,48 @@ module AMQP
375
377
  # Note that this method calls #to_s on payload argument value. You are encouraged to take care of
376
378
  # data serialization before publishing (using JSON, Thrift, Protocol Buffers or other serialization library).
377
379
  # Note that because AMQP is a binary protocol, text formats like JSON lose lose their strong point of being easy
378
- # to inspect data as it travels across network.
380
+ # to inspect data as it travels across network. For the same reason BSON may be a good fit.
379
381
  #
380
382
  #
383
+ # h2. Publishing and message persistence
384
+ #
385
+ # In cases when you application cannot afford to lose a message, AMQP 0.9.1 has several features to offer:
386
+ #
387
+ # * Persistent messages
388
+ # * Messages acknowledgements
389
+ # * Transactions
390
+ # * (a RabbitMQ-specific extension) Publisher confirms
391
+ #
392
+ # This is a broad topic and we dedicate a separate guide, {file:docs/Durability.textile Durability and message persistence}, to it.
393
+ #
394
+ #
395
+ # h2. Publishing callback and guarantees it DOES NOT offer
396
+ #
397
+ # Exact moment when message is published is not determined and depends on many factors, including machine's networking stack configuration,
398
+ # so (optional) block this method takes is scheduled for next event loop tick, and data is staged for delivery for current event loop
399
+ # tick. For most applications, this is good enough. The only way to guarantee a message was delivered in a distributed system is to
400
+ # ask a peer to send you a message back. RabbitMQ
401
+ #
402
+ # @note Optional callback this method takes DOES NOT OFFER ANY GUARANTEES ABOUT DATA DELIVERY and must not be used as a "delivery callback".
403
+ # The only way to guarantee delivery in distributed environment is to use an acknowledgement mechanism, such as AMQP transactions
404
+ # or lightweight "publisher confirms" RabbitMQ extension supported by amqp gem. See {file:docs/Durability.textile Durability and message persistence}
405
+ # and {file:docs/Exchanges.textile Working With Exchanges} guides for details.
406
+ #
381
407
  #
382
408
  # h2. Event loop blocking
383
409
  #
384
- # To minimize blocking of EventMachine event loop, this method performs network I/O on the next event loop tick. This means
385
- # that shutting down the event loop immediately after {Exchange#publish} returns will result in message never being sent.
410
+ # When intermixing publishing of many messages with other workload that may take some time, even loop blocking may affect the performance.
411
+ # There are several ways to avoid it:
412
+ #
413
+ # * Run EventMachine in a separate thread.
414
+ # * Use EventMachine.next_tick.
415
+ # * Use EventMachine.defer to offload operation to EventMachine thread pool.
416
+ #
417
+ # TBD: this subject is worth a separate guide
418
+ #
419
+ #
420
+ # h2. Sending one-off messages
421
+ #
386
422
  # If you need to send a one-off message and then stop the event loop, pass a block to {Exchange#publish} that will be executed
387
423
  # after message is pushed down the network stack, and use {AMQP::Session#disconnect} to properly tear down AMQP connection
388
424
  # (see example under Examples section below).
@@ -393,18 +429,6 @@ module AMQP
393
429
  # end
394
430
  #
395
431
  #
396
- # h2. Publishing and persistence
397
- #
398
- # In cases when you application cannot afford to lose a message, AMQP 0.9.1 has several features to offer:
399
- #
400
- # * Persistent messages
401
- # * Messages acknowledgements
402
- # * Transactions
403
- # * (a RabbitMQ-specific extension) Publisher confirms
404
- #
405
- # This is a broad topic and we dedicate a separate guide, {file:docs/Durability.textile Durability and message persistence}, to it.
406
- #
407
- #
408
432
  #
409
433
  # @param [#to_s] payload Message payload (content). Note that this method calls #to_s on payload argument value.
410
434
  # You are encouraged to take care of data serialization before publishing (using JSON, Thrift,
@@ -446,48 +470,41 @@ module AMQP
446
470
  #
447
471
  # @return [Exchange] self
448
472
  #
449
- # @note Please make sure you read {file:docs/Durability.textile Durability guide} that covers exchanges durability vs. messages
473
+ # @note Please make sure you read {file:docs/Durability.textile Durability an message persistence} guide that covers exchanges durability vs. messages
450
474
  # persistence.
451
475
  # @api public
452
476
  def publish(payload, options = {}, &block)
453
- EM.next_tick do
454
- opts = @default_publish_options.merge(options)
477
+ opts = @default_publish_options.merge(options)
455
478
 
456
- @channel.once_open do
457
- super(payload.to_s, opts[:key] || opts[:routing_key] || @default_routing_key, @default_headers.merge(options), opts[:mandatory], opts[:immediate])
479
+ @channel.once_open do
480
+ super(payload.to_s, opts[:key] || opts[:routing_key] || @default_routing_key, @default_headers.merge(options), opts[:mandatory], opts[:immediate])
458
481
 
459
- # don't pass block to AMQ::Client::Exchange#publish because it will be executed
460
- # immediately and we want to do it later. See ruby-amqp/amqp/#67 MK.
461
- block.call if block
462
- end
482
+ # don't pass block to AMQ::Client::Exchange#publish because it will be executed
483
+ # immediately and we want to do it later. See ruby-amqp/amqp/#67 MK.
484
+ EventMachine.next_tick(&block) if block
463
485
  end
464
486
 
465
487
  self
466
488
  end
467
489
 
468
490
 
469
- # This method deletes an exchange. When an exchange is deleted all queue
470
- # bindings on the exchange are cancelled.
491
+ # This method deletes an exchange. When an exchange is deleted all queue bindings on the exchange are deleted, too.
492
+ # Further attempts to publish messages to a deleted exchange will result in a channel-level exception.
471
493
  #
472
- # Further attempts to publish messages to a deleted exchange will raise
473
- # an AMQP::Channel::Error due to a channel close exception.
494
+ # @example Deleting an exchange
474
495
  #
475
- # exchange = AMQP::Channel.direct('name', :routing_key => 'foo.bar')
496
+ # exchange = AMQP::Channel.direct("search.indexing")
476
497
  # exchange.delete
477
498
  #
478
- # == Options
479
- # * :nowait => true | false (default true)
480
- # If set, the server will not respond to the method. The client should
481
- # not wait for a reply method. If the server could not complete the
482
- # method it will raise a channel or connection exception.
483
- #
484
- # exchange.delete(:nowait => false)
499
+ # @option opts [Boolean] :nowait (false) If set, the server will not respond to the method. The client should
500
+ # not wait for a reply method. If the server could not complete the
501
+ # method it will raise a channel or connection exception.
485
502
  #
486
- # * :if_unused => true | false (default false)
487
- # If set, the server will only delete the exchange if it has no queue
488
- # bindings. If the exchange has queue bindings the server does not
489
- # delete it but raises a channel exception instead (AMQP::Error).
503
+ # @option opts [Boolean] :if_unused (false) If set, the server will only delete the exchange if it has no queue
504
+ # bindings. If the exchange has queue bindings the server does not
505
+ # delete it but raises a channel exception instead.
490
506
  #
507
+ # @return [NilClass] nil
491
508
  # @api public
492
509
  def delete(opts = {}, &block)
493
510
  @channel.once_open do
@@ -170,11 +170,16 @@ module AMQP
170
170
  #
171
171
  # @api public
172
172
  def initialize(channel, name = AMQ::Protocol::EMPTY_STRING, opts = {}, &block)
173
+ raise ArgumentError.new("queue name must not be nil; if you want broker to generate queue name for you, pass an empty string") if name.nil?
174
+
173
175
  @channel = channel
174
176
  name = AMQ::Protocol::EMPTY_STRING if name.nil?
175
177
  @name = name unless name.empty?
176
178
  @server_named = name.empty?
177
179
  @opts = self.class.add_default_options(name, opts, block)
180
+
181
+ raise ArgumentError.new("server-named queues (name = '') declaration with :nowait => true makes no sense. If you are not sure what that means, simply drop :nowait => true from opts.") if @server_named && @opts[:nowait]
182
+
178
183
  @bindings = Hash.new
179
184
 
180
185
  # a deferrable that we use to delay operations until this queue is actually declared.
@@ -254,6 +259,10 @@ module AMQP
254
259
  # name is empty, the routing key will be the current queue for the
255
260
  # channel, which is the last declared queue.
256
261
  #
262
+ # @option opts [Hash] :arguments (nil) A hash of optional arguments with the declaration. Headers exchange type uses these metadata
263
+ # attributes for routing matching.
264
+ # In addition, brokers may implement AMQP extensions using x-prefixed declaration arguments.
265
+ #
257
266
  # @option opts [Boolean] :nowait (true) If set, the server will not respond to the method. The client should
258
267
  # not wait for a reply method. If the server could not complete the
259
268
  # method it will raise a channel or connection exception.
@@ -472,7 +481,7 @@ module AMQP
472
481
  #
473
482
  # @example Use of callback with a single argument
474
483
  #
475
- # EM.run do
484
+ # EventMachine.run do
476
485
  # exchange = AMQP::Channel.direct("foo queue")
477
486
  # EM.add_periodic_timer(1) do
478
487
  # exchange.publish("random number #{rand(1000)}")
@@ -488,19 +497,55 @@ module AMQP
488
497
  #
489
498
  # @example Use of callback with two arguments
490
499
  #
491
- # EM.run do
492
- # exchange = AMQP::Channel.direct("foo queue")
493
- # EM.add_periodic_timer(1) do
494
- # exchange.publish("random number #{rand(1000)}")
500
+ # EventMachine.run do
501
+ # connection = AMQP.connect(:host => '127.0.0.1')
502
+ # puts "Connected to AMQP broker. Running #{AMQP::VERSION} version of the gem..."
503
+ #
504
+ # channel = AMQP::Channel.new(connection)
505
+ # queue = channel.queue("amqpgem.examples.hello_world", :auto_delete => true)
506
+ # exchange = channel.direct("amq.direct")
507
+ #
508
+ # queue.bind(exchange)
509
+ #
510
+ # channel.on_error do |ch, channel_close|
511
+ # puts channel_close.reply_text
512
+ # connection.close { EventMachine.stop }
495
513
  # end
496
514
  #
497
- # # note that #bind is never called; it is implicit because
498
- # # the exchange and queue names match
499
- # queue = AMQP::Channel.queue('foo queue')
500
- # queue.subscribe do |header, body|
501
- # p header
502
- # puts "received payload [#{body}]"
515
+ # queue.subscribe do |metadata, payload|
516
+ # puts "metadata.routing_key : #{metadata.routing_key}"
517
+ # puts "metadata.content_type: #{metadata.content_type}"
518
+ # puts "metadata.priority : #{metadata.priority}"
519
+ # puts "metadata.headers : #{metadata.headers.inspect}"
520
+ # puts "metadata.timestamp : #{metadata.timestamp.inspect}"
521
+ # puts "metadata.type : #{metadata.type}"
522
+ # puts "metadata.delivery_tag: #{metadata.delivery_tag}"
523
+ # puts "metadata.redelivered : #{metadata.redelivered}"
524
+ #
525
+ # puts "metadata.app_id : #{metadata.app_id}"
526
+ # puts "metadata.exchange : #{metadata.exchange}"
527
+ # puts
528
+ # puts "Received a message: #{payload}. Disconnecting..."
529
+ #
530
+ # connection.close {
531
+ # EventMachine.stop { exit }
532
+ # }
503
533
  # end
534
+ #
535
+ # exchange.publish("Hello, world!",
536
+ # :app_id => "amqpgem.example",
537
+ # :priority => 8,
538
+ # :type => "kinda.checkin",
539
+ # # headers table keys can be anything
540
+ # :headers => {
541
+ # :coordinates => {
542
+ # :latitude => 59.35,
543
+ # :longitude => 18.066667
544
+ # },
545
+ # :participants => 11,
546
+ # :venue => "Stockholm"
547
+ # },
548
+ # :timestamp => Time.now.to_i)
504
549
  # end
505
550
  #
506
551
  #
@@ -516,10 +561,15 @@ module AMQP
516
561
  # method it will raise a channel or connection exception.
517
562
  #
518
563
  # @option opts [#call] :confirm (nil) If set, this proc will be called when the server confirms subscription
519
- # to the queue with a ConsumeOk message. Setting this option will
564
+ # to the queue with a basic.consume-ok message. Setting this option will
520
565
  # automatically set :nowait => false. This is required for the server
521
566
  # to send a confirmation.
522
567
  #
568
+ # @option opts [Boolean] :exclusive (false) Request exclusive consumer access, meaning only this consumer can access the queue.
569
+ # This is useful when you want a long-lived shared queue to be temporarily accessible by just
570
+ # one application (or thread, or process). If application exclusive consumer is part of crashes
571
+ # or loses network connection to the broker, channel is closed and exclusive consumer is thus cancelled.
572
+ #
523
573
  #
524
574
  # @yield [headers, payload] When block only takes one argument, yields payload to it. In case of two arguments, yields headers and payload.
525
575
  # @yieldparam [AMQP::Header] headers Headers (metadata) associated with this message (for example, routing key).
@@ -527,6 +577,9 @@ module AMQP
527
577
  #
528
578
  # @return [Queue] Self
529
579
  # @api public
580
+ #
581
+ # @see file:docs/Queues.textile Documentation guide on queues
582
+ # @see #unsubscribe
530
583
  def subscribe(opts = {}, &block)
531
584
  raise Error, 'already subscribed to the queue' if @consumer_tag
532
585
 
@@ -662,7 +715,7 @@ module AMQP
662
715
 
663
716
  # @private
664
717
  def self.add_default_options(name, opts, block)
665
- { :queue => name, :nowait => block.nil? }.merge(opts)
718
+ { :queue => name, :nowait => (block.nil? && !name.empty?) }.merge(opts)
666
719
  end
667
720
 
668
721
  private
@@ -6,5 +6,5 @@ module AMQP
6
6
  #
7
7
  # @see AMQ::Protocol::VERSION
8
8
  # @return [String] AMQP gem version
9
- VERSION = '0.8.0.rc12'
9
+ VERSION = '0.8.0.rc13'
10
10
  end
@@ -46,11 +46,11 @@ describe "Authentication attempt" do
46
46
  #
47
47
 
48
48
  # assuming there is an account amqp_gem with password of "amqp_gem_password" that has
49
- # access to /amqp_gem_testbed
49
+ # access to amqp_gem_testbed
50
50
  context "when amqp_gem/amqp_gem_testbed has access to amqp_gem_testbed" do
51
51
  context "and provided credentials are correct" do
52
52
  it "succeeds" do
53
- connection = AMQP.connect(AMQP_OPTS.merge(:username => "amqp_gem", :password => "amqp_gem_password", :vhost => "/amqp_gem_testbed"))
53
+ connection = AMQP.connect(AMQP_OPTS.merge(:username => "amqp_gem", :password => "amqp_gem_password", :vhost => "amqp_gem_testbed"))
54
54
 
55
55
  done(0.4) {
56
56
  connection.should be_connected
@@ -70,7 +70,7 @@ describe "Authentication attempt" do
70
70
  callback_has_fired = true
71
71
  done
72
72
  }
73
- connection = AMQP.connect(:username => "amqp_gem", :password => Time.now.to_i.to_s, :vhost => "/amqp_gem_testbed", :on_possible_authentication_failure => handler)
73
+ connection = AMQP.connect(:username => "amqp_gem", :password => Time.now.to_i.to_s, :vhost => "amqp_gem_testbed", :on_possible_authentication_failure => handler)
74
74
  end # it
75
75
  end
76
76
 
@@ -100,11 +100,11 @@ describe "Authentication attempt" do
100
100
  #
101
101
 
102
102
  # assuming there is an account amqp_gem with password of "amqp_gem_password" that has
103
- # access to /amqp_gem_testbed
103
+ # access to amqp_gem_testbed
104
104
  context "when amqp_gem/amqp_gem_testbed has access to amqp_gem_testbed" do
105
105
  context "and provided credentials are correct" do
106
106
  it "succeeds" do
107
- connection = AMQP.connect "amqp://amqp_gem:amqp_gem_password@localhost/%2Famqp_gem_testbed"
107
+ connection = AMQP.connect "amqp://amqp_gem:amqp_gem_password@localhost/amqp_gem_testbed"
108
108
 
109
109
  done(0.3) {
110
110
  connection.should be_connected
@@ -87,7 +87,7 @@ describe AMQP::Queue, "#pop" do
87
87
  # Queue.Get doesn't qualify for subscription, hence, manual deletion is required
88
88
  @queue.delete
89
89
  }
90
- done(1.5) {
90
+ done(2.5) {
91
91
  number_of_received_messages.should == expected_number_of_messages
92
92
  }
93
93
  end # it
@@ -6,10 +6,17 @@ describe AMQP::Channel, "#close(&callback)" do
6
6
  include EventedSpec::AMQPSpec
7
7
 
8
8
  it "takes a callback which will run when we get back Channel.Close-Ok" do
9
- channel = AMQP::Channel.new do |*args|
10
- channel.close do |channel, method|
11
- done
9
+ @events = []
10
+
11
+ AMQP::Channel.new do |ch|
12
+ @events << :open_ok
13
+ ch.close do |channel, close_ok|
14
+ @events << :close_ok
12
15
  end
13
16
  end
17
+
18
+ done(0.3) {
19
+ @events.should == [:open_ok, :close_ok]
20
+ }
14
21
  end
15
22
  end
@@ -43,12 +43,33 @@ describe AMQP do
43
43
  end # context
44
44
 
45
45
  context "when queue name is passed on as an empty string" do
46
- it "uses server-assigned queue name" do
47
- @channel.queue("") do |queue, *args|
48
- queue.name.should_not be_empty
49
- queue.delete
50
- done(0.3)
46
+ context "and :nowait isn't used" do
47
+ it "uses server-assigned queue name" do
48
+ @channel.queue("") do |queue, *args|
49
+ queue.name.should_not be_empty
50
+ queue.delete
51
+ done(0.3)
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ context "and :nowait is used" do
58
+ it "raises ArgumentError" do
59
+ expect { AMQP::Queue.new(@channel, "", :nowait => true) }.to raise_error(ArgumentError, /makes no sense/)
60
+ expect { @channel.queue("", :nowait => true) }.to raise_error(ArgumentError, /makes no sense/)
61
+
62
+ done
51
63
  end
64
+ end # context
65
+ end
66
+
67
+ context "when queue name is nil" do
68
+ it "raises ArgumentError" do
69
+ expect { AMQP::Queue.new(@channel, nil) }.to raise_error(ArgumentError, /queue name must not be nil/)
70
+ expect { @channel.queue(nil) }.to raise_error(ArgumentError, /queue name must not be nil/)
71
+
72
+ done
52
73
  end
53
74
  end # context
54
75