bunny 0.9.0.pre3 → 0.9.0.pre4

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/.gitignore CHANGED
@@ -16,3 +16,5 @@ bin/*
16
16
  vendor/*
17
17
  playground/*
18
18
  *.org
19
+ repl-*
20
+ debug/*
data/ChangeLog.md CHANGED
@@ -1,6 +1,61 @@
1
1
  ## Changes between Bunny 0.9.0.pre3 and 0.9.0.pre4
2
2
 
3
- No changes yet.
3
+ ### Heartbeats Support Fixes
4
+
5
+ Heartbeats are now correctly sent at safe intervals (half of the configured
6
+ interval). In addition, setting `:heartbeat => 0` (or `nil`) will disable
7
+ heartbeats, just like in Bunny 0.8 and [amqp gem](http://rubyamqp.info).
8
+
9
+ Default `:heartbeat` value is now `600` (seconds), the same as RabbitMQ 3.0
10
+ default.
11
+
12
+
13
+ ### Eliminate Race Conditions When Registering Consumers
14
+
15
+ Fixes a potential race condition between `basic.consume-ok` handler and
16
+ delivery handler when a consumer is registered for a queue that has
17
+ messages in it.
18
+
19
+ GH issue: #78.
20
+
21
+ ### Support for Alternative Authentication Mechanisms
22
+
23
+ Bunny now supports two authentication mechanisms and can be extended
24
+ to support more. The supported methods are `"PLAIN"` (username
25
+ and password) and `"EXTERNAL"` (typically uses TLS, UNIX sockets or
26
+ another mechanism that does not rely on username/challenge pairs).
27
+
28
+ To use the `"EXTERNAL"` method, pass `:auth_mechanism => "EXTERNAL"` to
29
+ `Bunny.new`:
30
+
31
+ ``` ruby
32
+ # uses the EXTERNAL authentication mechanism
33
+ conn = Bunny.new(:auth_method => "EXTERNAL")
34
+ conn.start
35
+ ```
36
+
37
+ ### Bunny::Consumer#cancel
38
+
39
+ A new high-level API method: `Bunny::Consumer#cancel`, can be used to
40
+ cancel a consumer. `Bunny::Queue#subscribe` will now return consumer
41
+ instances when the `:block` option is passed in as `false`.
42
+
43
+
44
+ ### Bunny::Exchange#delete Behavior Change
45
+
46
+ `Bunny::Exchange#delete` will no longer delete pre-declared exchanges
47
+ that cannot be declared by Bunny (`amq.*` and the default exchange).
48
+
49
+
50
+ ### Bunny::DeliveryInfo#redelivered?
51
+
52
+ `Bunny::DeliveryInfo#redelivered?` is a new method that is an alias
53
+ to `Bunny::DeliveryInfo#redelivered` but follows the Ruby community convention
54
+ about predicate method names.
55
+
56
+ ### Corrected Bunny::DeliveryInfo#delivery_tag Name
57
+
58
+ `Bunny::DeliveryInfo#delivery_tag` had a typo which is now fixed.
4
59
 
5
60
 
6
61
  ## Changes between Bunny 0.9.0.pre2 and 0.9.0.pre3
data/README.md CHANGED
@@ -1,79 +1,145 @@
1
1
  # About Bunny
2
2
 
3
- Bunny is a synchronous RabbitMQ client that focuses on ease of use.
3
+ Bunny is a synchronous RabbitMQ client that focuses on ease of use. With the next
4
+ 0.9 release (currently in master), it is feature complete, supports all RabbitMQ 3.0
5
+ features and is free of many limitations of earlier versions.
4
6
 
5
7
 
6
8
  ## Supported Ruby Versions
7
9
 
8
- It supports Ruby 1.9.3, 1.9.2, 1.8.7, Rubinius 2 and JRuby.
10
+ Bunny 0.9 and more recent versions support Ruby 1.9.3, 1.9.2, JRuby 1.7, Rubinius 2.0 and 1.8.7.
9
11
 
10
12
 
11
13
  ## Supported RabbitMQ Versions
12
14
 
13
- Bunny versions `< 0.7.x` support RabbitMQ 1.x and 2.x. Bunny `0.8.x` and later versions only
14
- supports RabbitMQ 2.x.
15
+ Bunny `0.8.x` and later versions only support RabbitMQ 2.x and 3.x.
15
16
 
17
+ Bunny versions `0.7.x` and earlier support RabbitMQ 1.x and 2.x.
16
18
 
17
- ## Important: Bunny is about to undergo a lot of internal changes
19
+
20
+ ## Changes in Bunny 0.9
18
21
 
19
22
  Bunny is a very old library with **a lot** of missing functionality. It also implements an older version of the spec
20
- and may or may not work with future RabbitMQ versions. As such, Bunny is about to undergo serious internal changes.
21
- We will make our best to keep them as backwards compatible as possible but within reason.
23
+ and may or may not work with future RabbitMQ versions. As such, Bunny needed serious internal changes.
24
+ We (the maintainers) make our best to keep the new version as backwards compatible as possible but within reason.
22
25
 
23
26
  See [this announcement](https://groups.google.com/forum/?fromgroups#!topic/ruby-amqp/crNVGEuHm68) to learn more.
24
27
 
25
- In the meantime, consider using [Hot Bunnies](http://github.com/ruby-amqp/hot_bunnies) (JRuby-only) or [amqp gem](http://rubyamqp.info) instead.
26
28
 
29
+ ## Installation & Bundler Dependency
30
+
31
+ To install Bunny 0.9.x with RubyGems:
32
+
33
+ ```
34
+ gem install bunny --pre
35
+ ```
27
36
 
28
- ## Quick Start for Bunny 0.7.x and 0.8.x
37
+ To use Bunny 0.9.x in a project managed with Bundler:
38
+
39
+ ``` ruby
40
+ gem "bunny", ">= 0.9.0.pre3" # optionally: , :git => "git://github.com/ruby-amqp/bunny.git", :branch => "master"
41
+ ```
42
+
43
+
44
+ ## Quick Start for Bunny 0.9.x
45
+
46
+ Below is a small snippet that demonstrates how to publish
47
+ and synchronously consume ("pull API") messages with Bunny 0.9.
48
+
49
+ For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html).
29
50
 
30
51
  ``` ruby
31
52
  require "bunny"
32
53
 
33
- b = Bunny.new(:logging => true)
54
+ # Start a communication session with RabbitMQ
55
+ conn = Bunny.new
56
+ conn.start
34
57
 
35
- # start a communication session with the amqp server
36
- b.start
58
+ # open a channel
59
+ ch = conn.create_channel
37
60
 
38
61
  # declare a queue
39
- q = b.queue("test1")
62
+ q = ch.queue("test1")
40
63
 
41
64
  # declare default direct exchange which is bound to all queues
42
- e = b.exchange("")
65
+ e = ch.default_exchange
43
66
 
44
67
  # publish a message to the exchange which then gets routed to the queue
45
- e.publish("Hello, everybody!", :key => 'test1')
68
+ e.publish("Hello, everybody!", :routing_key => 'test1')
46
69
 
47
- # get message from the queue
48
- msg = q.pop[:payload]
70
+ # fetch a message from the queue
71
+ delivery_info, metadata, payload = q.pop
49
72
 
50
- puts "This is the message: " + msg + "\n\n"
73
+ puts "This is the message: #{payload}"
51
74
 
52
75
  # close the connection
53
- b.stop
76
+ conn.stop
54
77
  ```
55
78
 
56
- ... or just:
57
79
 
58
- ```
59
- require "bunny"
80
+ ## Community & Getting Help
60
81
 
61
- # Create a direct queue named "my_testq"
62
- Bunny.run { |c| c.queue("my_testq") }
63
- ```
82
+ ### Getting Started
64
83
 
65
- ## Community & Getting Help
84
+ For a 15 minute tutorial using more practical examples, see [Getting Started with RabbitMQ and Ruby using Bunny](http://rubybunny.info/articles/getting_started.html).
85
+
86
+ ### Guides
87
+
88
+ Other documentation guides are available at [rubybunny.info](http://rubybunny.info).
89
+
90
+ ### API Reference
91
+
92
+ [Bunny API Reference](http://rubydoc.info/github/ruby-amqp/bunny/master/frames) is available at rubydoc.info.
93
+
94
+ ## Getting Help
95
+
96
+ ### Mailing List
97
+
98
+ [Bunny a mailing list](groups.google.com/group/ruby-amqp). We encourage you
99
+ to also join the [rabbitmq-discuss](https://lists.rabbitmq.com/cgi-bin/mailman/listinfo/rabbitmq-discuss) mailing list. Feel free to ask any questions that you may have.
100
+
101
+
102
+ ### IRC
103
+
104
+ For more immediate help, please join `#rabbitmq` on `irc.freenode.net`.
105
+
106
+
107
+ ### News & Announcements on Twitter
108
+
109
+ To subscribe for announcements of releases, important changes and so on, please follow [@rubyamqp](https://twitter.com/#!/rubyamqp) on Twitter.
110
+
111
+
112
+ ### Reporting Issues
113
+
114
+ If you find a bug, poor default, missing feature or find any part of the API inconvenient, please [file an issue](http://github.com/ruby-amqp/bunny/issues) on GitHub.
115
+ When filing an issue, please specify which Bunny and RabbitMQ versions you are using, provide recent RabbitMQ log file contents if possible,
116
+ and try to explain what behavior you expected and why. Bonus points for contributing failing test cases.
117
+
118
+
119
+ ## Other Ruby RabbitMQ Clients
120
+
121
+ Other widely used Ruby RabbitMQ clients are [Hot Bunnies](http://github.com/ruby-amqp/hot_bunnies) (JRuby-only) and [amqp gem](http://rubyamqp.info).
122
+ Both are mature libraries and require RabbitMQ 2.x or 3.x.
123
+
124
+
125
+ ## Contributing
126
+
127
+ First, clone the repository and run
128
+
129
+ bundle install --binstubs
130
+
131
+ and then run tests with
66
132
 
67
- Please use [Ruby RabbitMQ clients Google Group](http://groups.google.com/group/ruby-amqp) for any questions you may
68
- have.
133
+ ./bin/rspec -cfs spec
69
134
 
70
- For news and updates, [follow @rubyamqp](http://twitter.com/rubyamqp) on Twitter.
135
+ After that create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
136
+ on GitHub.
71
137
 
72
138
 
73
139
 
74
140
  ## Other Resources
75
141
 
76
- * [AMQP 0.9.1 model explained](): introductory explanation of the AMQP v0.9.1 specification with particular reference to RabbitMQ.
142
+ * [AMQP 0.9.1 model explained](http://www.rabbitmq.com/tutorials/amqp-concepts.html): introductory explanation of the AMQP v0.9.1 specification with particular reference to RabbitMQ.
77
143
 
78
144
 
79
145
  ## Links
data/bunny.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  map { |mail| Base64.decode64(mail) }
30
30
 
31
31
  # Dependencies
32
- s.add_dependency "amq-protocol", ">= 1.0.0"
32
+ s.add_dependency "amq-protocol", ">= 1.0.1"
33
33
 
34
34
  # Files.
35
35
  s.has_rdoc = true
@@ -9,9 +9,9 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
9
9
  require 'bunny'
10
10
 
11
11
 
12
- b = Bunny.new(:heartbeat_interval => 2)
13
- b.start
12
+ conn = Bunny.new(:heartbeat_interval => 2)
13
+ conn.start
14
14
 
15
- c = b.create_channel
15
+ c = conn.create_channel
16
16
 
17
17
  sleep 10
@@ -8,6 +8,6 @@ $:.unshift(File.expand_path("../../../lib", __FILE__))
8
8
 
9
9
  require 'bunny'
10
10
 
11
- b = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
12
- b.start
11
+ conn = Bunny.new("amqp://guest:guest@aksjhdkajshdkj.example82737.com")
12
+ conn.start
13
13
 
@@ -9,13 +9,12 @@ conn.start
9
9
 
10
10
  ch = conn.create_channel
11
11
  q = ch.queue("bunny.examples.hello_world", :auto_delete => true)
12
- x = ch.default_exchange
13
12
 
14
13
  q.subscribe do |delivery_info, properties, payload|
15
14
  puts "Received #{payload}"
16
15
  end
17
16
 
18
- x.publish("Hello!", :routing_key => q.name)
17
+ q.publish("Hello!", :routing_key => q.name)
19
18
 
20
19
  sleep 1.0
21
20
  conn.close
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require "rubygems"
5
+ require "bunny"
6
+
7
+ puts "=> Subscribing for messages using explicit acknowledgements model"
8
+ puts
9
+
10
+ connection1 = Bunny.new
11
+ connection1.start
12
+
13
+ connection2 = Bunny.new
14
+ connection2.start
15
+
16
+ connection3 = Bunny.new
17
+ connection3.start
18
+
19
+ ch1 = connection1.create_channel
20
+ ch1.prefetch(1)
21
+
22
+ ch2 = connection2.create_channel
23
+ ch2.prefetch(1)
24
+
25
+ ch3 = connection3.create_channel
26
+ ch3.prefetch(1)
27
+
28
+ x = ch3.direct("amq.direct")
29
+ q1 = ch1.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
30
+ q1.purge
31
+
32
+ q1.bind(x).subscribe(:ack => true, :block => false) do |delivery_info, properties, payload|
33
+ # do some work
34
+ sleep(0.2)
35
+
36
+ # acknowledge some messages, they will be removed from the queue
37
+ if rand > 0.5
38
+ # FYI: there is a shortcut, Bunny::Channel.ack
39
+ ch1.acknowledge(delivery_info.delivery_tag, false)
40
+ puts "[consumer1] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed"
41
+ else
42
+ # some messages are not ack-ed and will remain in the queue for redelivery
43
+ # when app #1 connection is closed (either properly or due to a crash)
44
+ puts "[consumer1] Got message ##{properties.headers['i']}, SKIPPPED"
45
+ end
46
+ end
47
+
48
+ q2 = ch2.queue("bunny.examples.acknowledgements.explicit", :auto_delete => false)
49
+ q2.bind(x).subscribe(:ack => true, :block => false) do |delivery_info, properties, payload|
50
+ # do some work
51
+ sleep(0.2)
52
+
53
+ ch2.acknowledge(delivery_info.delivery_tag, false)
54
+ puts "[consumer2] Got message ##{properties.headers['i']}, redelivered?: #{delivery_info.redelivered?}, ack-ed"
55
+ end
56
+
57
+ t1 = Thread.new do
58
+ i = 0
59
+ loop do
60
+ sleep 0.5
61
+
62
+ x.publish("Message ##{i}", :headers => { :i => i })
63
+ i += 1
64
+ end
65
+ end
66
+ t1.abort_on_exception = true
67
+
68
+ t2 = Thread.new do
69
+ sleep 4.0
70
+
71
+ connection1.close
72
+ puts "----- Connection 1 is now closed (we pretend that it has crashed) -----"
73
+ end
74
+ t2.abort_on_exception = true
75
+
76
+
77
+ sleep 7.0
78
+ connection2.close
79
+ connection3.close
@@ -0,0 +1,37 @@
1
+ module Bunny
2
+ module Authentication
3
+ class CredentialsEncoder
4
+
5
+ #
6
+ # API
7
+ #
8
+
9
+ attr_reader :session
10
+
11
+ def self.for_session(session)
12
+ registry[session.mechanism].new(session)
13
+ end
14
+
15
+ def self.registry
16
+ @@registry ||= Hash.new { raise NotImplementedError }
17
+ end
18
+
19
+ def self.auth_mechanism(*mechanisms)
20
+ mechanisms.each do |m|
21
+ registry[m] = self
22
+ end
23
+ end
24
+
25
+ def encode_credentials(username, challenge)
26
+ raise NotImplementedError.new("Subclasses must override this method")
27
+ end
28
+
29
+ protected
30
+
31
+ def initialize(session)
32
+ @session = session
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require "bunny/authentication/credentials_encoder"
2
+
3
+ module Bunny
4
+ module Authentication
5
+ class ExternalMechanismEncoder < CredentialsEncoder
6
+
7
+ auth_mechanism "EXTERNAL", "external"
8
+
9
+ # Encodes a username and password for the EXTERNAL mechanism. Since
10
+ # authentication is handled by an encapsulating protocol like SSL or
11
+ # UNIX domain sockets, EXTERNAL doesn't pass along any username or
12
+ # password information at all and this method always returns the
13
+ # empty string.
14
+ #
15
+ # @param [String] username The username to encode. This parameter is
16
+ # ignored.
17
+ # @param [String] password The password to encode. This parameter is
18
+ # ignored.
19
+ # @return [String] The username and password, encoded for the
20
+ # EXTERNAL mechanism. This is always the empty string.
21
+ def encode_credentials(username, password)
22
+ ""
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ require "bunny/authentication/credentials_encoder"
2
+
3
+ module Bunny
4
+ module Authentication
5
+ class PlainMechanismEncoder < CredentialsEncoder
6
+
7
+ auth_mechanism "PLAIN", "plain"
8
+
9
+ # @api public
10
+ # @see http://tools.ietf.org/rfc/rfc2595.txt RFC 2595
11
+ def encode_credentials(username, password)
12
+ "\0#{username}\0#{password}"
13
+ end
14
+
15
+ end
16
+ end
17
+ end