bunny 0.9.0.pre3 → 0.9.0.pre4

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