bunny 0.2.0 → 0.3.0

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.
@@ -0,0 +1,62 @@
1
+ module Bunny
2
+ module Transport #:nodoc: all
3
+ class Frame
4
+ def initialize payload = nil, channel = 0
5
+ @channel, @payload = channel, payload
6
+ end
7
+ attr_accessor :channel, :payload
8
+
9
+ def id
10
+ self.class::ID
11
+ end
12
+
13
+ def to_binary
14
+ buf = Transport::Buffer.new
15
+ buf.write :octet, id
16
+ buf.write :short, channel
17
+ buf.write :longstr, payload
18
+ buf.write :octet, Transport::Frame::FOOTER
19
+ buf.rewind
20
+ buf
21
+ end
22
+
23
+ def to_s
24
+ to_binary.to_s
25
+ end
26
+
27
+ def == frame
28
+ [ :id, :channel, :payload ].inject(true) do |eql, field|
29
+ eql and __send__(field) == frame.__send__(field)
30
+ end
31
+ end
32
+
33
+ class Method
34
+ def initialize payload = nil, channel = 0
35
+ super
36
+ unless @payload.is_a? Protocol::Class::Method or @payload.nil?
37
+ @payload = Protocol.parse(@payload)
38
+ end
39
+ end
40
+ end
41
+
42
+ class Header
43
+ def initialize payload = nil, channel = 0
44
+ super
45
+ unless @payload.is_a? Protocol::Header or @payload.nil?
46
+ @payload = Protocol::Header.new(@payload)
47
+ end
48
+ end
49
+ end
50
+
51
+ class Body; end
52
+
53
+ def self.parse buf
54
+ buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
55
+ buf.extract do
56
+ id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
57
+ Transport::Frame.types[id].new(payload, channel) if footer == Transport::Frame::FOOTER
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -11,12 +11,12 @@ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib bunny]))
11
11
  describe Bunny do
12
12
 
13
13
  before(:each) do
14
- @b = Bunny.new
14
+ @b = Bunny::Client.new
15
15
  @b.start
16
16
  end
17
17
 
18
18
  it "should connect to an AMQP server" do
19
- @b.status.should == 'CONNECTED'
19
+ @b.status.should == :connected
20
20
  end
21
21
 
22
22
  it "should be able to create an exchange" do
@@ -11,12 +11,12 @@ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib bunny]))
11
11
  describe Bunny::Exchange do
12
12
 
13
13
  before(:each) do
14
- @b = Bunny.new
14
+ @b = Bunny::Client.new
15
15
  @b.start
16
16
  end
17
17
 
18
18
  it "should raise an error if instantiated as non-existent type" do
19
- lambda { @b.exchange('bogus_ex', :type => :bogus) }.should raise_error(API::ProtocolError)
19
+ lambda { @b.exchange('bogus_ex', :type => :bogus) }.should raise_error(Bunny::ProtocolError)
20
20
  end
21
21
 
22
22
  it "should allow a default direct exchange to be instantiated by specifying :type" do
@@ -105,7 +105,7 @@ describe Bunny::Exchange do
105
105
  it "should be able to be deleted" do
106
106
  exch = @b.exchange('direct_exchange')
107
107
  res = exch.delete
108
- res.should == 'EXCHANGE DELETED'
108
+ res.should == :delete_ok
109
109
  @b.exchanges.has_key?('direct_exchange').should be false
110
110
  end
111
111
 
@@ -11,7 +11,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib bunny]))
11
11
  describe Bunny::Queue do
12
12
 
13
13
  before(:each) do
14
- @b = Bunny.new
14
+ @b = Bunny::Client.new
15
15
  @b.start
16
16
  end
17
17
 
@@ -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).should == 'BIND SUCCEEDED'
25
+ q.bind(exch, :nowait => true).should == :bind_ok
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).should == 'BIND SUCCEEDED'
31
+ q.bind(exch).should == :bind_ok
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).should == 'UNBIND SUCCEEDED'
37
+ q.unbind(exch, :nowait => true).should == :unbind_ok
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).should == 'UNBIND SUCCEEDED'
43
+ q.unbind(exch).should == :unbind_ok
44
44
  end
45
45
 
46
46
  it "should be able to publish a message" do
@@ -49,12 +49,13 @@ describe Bunny::Queue do
49
49
  q.message_count.should == 1
50
50
  end
51
51
 
52
- it "should be able to pop a message complete with header" do
52
+ it "should be able to pop a message complete with header and delivery details" do
53
53
  q = @b.queue('test1')
54
54
  msg = q.pop(:header => true)
55
55
  msg.should be_an_instance_of Hash
56
- msg[:header].should be_an_instance_of Protocol::Header
56
+ msg[:header].should be_an_instance_of Bunny::Protocol::Header
57
57
  msg[:payload].should == 'This is a test message'
58
+ msg[:delivery_details].should be_an_instance_of Hash
58
59
  q.message_count.should == 0
59
60
  end
60
61
 
@@ -71,13 +72,13 @@ describe Bunny::Queue do
71
72
  q.publish('This is another test message')
72
73
  q.pop
73
74
  msg = q.pop
74
- msg.should == 'QUEUE EMPTY'
75
+ msg.should == :queue_empty
75
76
  end
76
77
 
77
78
  it "should be able to be deleted" do
78
79
  q = @b.queue('test1')
79
80
  res = q.delete
80
- res.should == 'QUEUE DELETED'
81
+ res.should == :delete_ok
81
82
  @b.queues.has_key?('test1').should be false
82
83
  end
83
84
 
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.2.0
4
+ version: 0.3.0
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-05-01 00:00:00 +01:00
12
+ date: 2009-05-10 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -19,38 +19,38 @@ executables: []
19
19
 
20
20
  extensions: []
21
21
 
22
- extra_rdoc_files: []
23
-
22
+ extra_rdoc_files:
23
+ - README
24
24
  files:
25
+ - LICENSE
26
+ - README
25
27
  - Rakefile
26
- - README.markdown
27
- - lib/api_messages.rb
28
- - lib/bunny.rb
29
- - lib/engineroom/buffer.rb
30
- - lib/engineroom/frame.rb
31
- - lib/engineroom/protocol.rb
32
- - lib/engineroom/spec.rb
33
- - lib/bunny/exchange.rb
34
- - lib/bunny/header.rb
35
- - lib/bunny/queue.rb
36
- - lib/bunny/client.rb
28
+ - bunny.gemspec
37
29
  - examples/simple.rb
38
- - examples/simple_fanout.rb
30
+ - examples/simple_ack.rb
39
31
  - examples/simple_consumer.rb
32
+ - examples/simple_fanout.rb
40
33
  - examples/simple_publisher.rb
41
- - examples/simple_ack.rb
42
34
  - examples/simple_topic.rb
35
+ - ext/amqp-0.8.json
36
+ - ext/codegen.rb
37
+ - lib/bunny.rb
38
+ - lib/bunny/client.rb
39
+ - lib/bunny/exchange.rb
40
+ - lib/bunny/protocol/protocol.rb
41
+ - lib/bunny/protocol/spec.rb
42
+ - lib/bunny/queue.rb
43
+ - lib/bunny/transport/buffer.rb
44
+ - lib/bunny/transport/frame.rb
43
45
  - spec/bunny_spec.rb
44
46
  - spec/exchange_spec.rb
45
47
  - spec/queue_spec.rb
46
- - protocol/amqp-0.8.json
47
- - protocol/codegen.rb
48
48
  has_rdoc: true
49
49
  homepage: http://github.com/celldee/bunny
50
50
  post_install_message:
51
51
  rdoc_options:
52
- - --inline-source
53
- - --charset=UTF-8
52
+ - --main
53
+ - README
54
54
  require_paths:
55
55
  - lib
56
56
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -71,6 +71,6 @@ rubyforge_project: bunny-amqp
71
71
  rubygems_version: 1.3.1
72
72
  signing_key:
73
73
  specification_version: 2
74
- summary: Another synchronous Ruby AMQP client
74
+ summary: A synchronous Ruby AMQP client that enables interaction with AMQP-compliant brokers/servers.
75
75
  test_files: []
76
76
 
@@ -1,126 +0,0 @@
1
- # Bunny: A synchronous Ruby AMQP client
2
-
3
- Google Group: [http://groups.google.com/group/bunny-amqp](http://groups.google.com/group/bunny-amqp)
4
-
5
- Mailing List: [http://rubyforge.org/mailman/listinfo/bunny-amqp-devel](http://rubyforge.org/mailman/listinfo/bunny-amqp-devel)
6
-
7
- Rubyforge: [http://rubyforge.org/projects/bunny-amqp](http://rubyforge.org/projects/bunny-amqp)
8
-
9
- Twitter: [http://twitter.com/bunny_amqp](https://twitter.com/bunny_amqp)
10
-
11
- ## Announcements
12
-
13
- Bunny v0.2.0 is now available. The highlights are -
14
-
15
- * Code has been re-organised enabling Bunny to play nicely with [amqp](http://github.com/tmm1/amqp) (thanks [Dan](http://github.com/danielsdeleo))
16
- * When instantiating a default exchange (one beginning with ‘amq.’) the type will be inferred from the name.
17
- * Fixed Queue#subscribe and included Queue#unsubscribe method. See examples/simple_consumer.rb for details
18
-
19
- ## About
20
-
21
- *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.
22
-
23
- 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.
24
-
25
- You can use *Bunny* to -
26
-
27
- * Create and delete exchanges
28
- * Create and delete queues
29
- * Publish and consume messages
30
-
31
- *Bunny* is known to work with RabbitMQ version 1.5.4 and version 0-8 of the AMQP specification. If you want to try to use it with other AMQP message brokers/servers please let me know how you get on.
32
-
33
- ## Quick Start
34
-
35
- require 'bunny'
36
-
37
- b = Bunny.new(:logging => true)
38
-
39
- # start a communication session with the amqp server
40
- b.start
41
-
42
- # declare a queue
43
- q = b.queue('test1')
44
-
45
- # publish a message to the queue
46
- q.publish('Hello everybody!')
47
-
48
- # get message from the queue
49
- msg = q.pop
50
-
51
- puts 'This is the message: ' + msg + "\n\n"
52
-
53
- # close the connection
54
- b.close
55
-
56
- ## Bunny methods
57
-
58
- These are the Bunny methods that you will probably want to use -
59
-
60
- ### Create a Bunny instance
61
- Bunny#new({_options_})
62
-
63
- ### Start a communication session with the target server
64
- Bunny#start
65
-
66
- ### Stop a communication session with the target server
67
- Bunny#stop
68
-
69
- ### Create a Queue
70
- Bunny#queue(_**name**_, {_options_})
71
-
72
- ### Create an Exchange
73
- Bunny#exchange(_**name**_, {_options_})
74
-
75
- ### Return connection status ('CONNECTED' or 'NOT CONNECTED')
76
- Bunny#status
77
-
78
- ### Publish a message to an exchange
79
- Exchange#publish(_**data**_, {_options_})
80
-
81
- ### Delete an exchange from the target server
82
- Exchange#delete({_options_})
83
-
84
- ### Bind a queue to an exchange
85
- Queue#bind(_**exchange**_, {_options_})
86
-
87
- ### Unbind a queue from an exchange
88
- Queue#unbind(_**exchange**_, {_options_})
89
-
90
- ### Publish a message to a queue
91
- Queue#publish(_**data**_, {_options_})
92
-
93
- ### Pop a message off of a queue
94
- Queue#pop({_options_})
95
-
96
- ### Subscribe to a queue
97
- Queue#subscribe({_options_}, &blk)
98
-
99
- ### Unsubscribe from a queue
100
- Queue#unsubscribe({_options_})
101
-
102
- ### Return queue message count
103
- Queue#message_count
104
-
105
- ### Return queue consumer count
106
- Queue#consumer_count
107
-
108
- ### Return queue status (hash {:message count, :consumer_count})
109
- Queue#status
110
-
111
- ### Delete a queue from the target server
112
- Queue#delete({_options_})
113
-
114
- ### Acknowledge receipt of a message
115
- Queue#ack
116
-
117
- ## Acknowledgements
118
-
119
- This project has borrowed heavily from the following two projects and owes their respective creators and collaborators a whole lot of gratitude:
120
-
121
- 1. **amqp** by *tmm1* [http://github.com/tmm1/amqp/tree/master](http://github.com/tmm1/amqp/tree/master)
122
- 2. **carrot** by *famoseagle* [http://github.com/famoseagle/carrot/tree/master](http://github.com/famoseagle/carrot/tree/master)
123
-
124
- ## LICENSE
125
-
126
- Copyright (c) 2009 Chris Duncan; Published under The MIT License, see License
@@ -1,18 +0,0 @@
1
- module API
2
- # return messages
3
- CONNECTED = 'CONNECTED'
4
- NOT_CONNECTED = 'NOT CONNECTED'
5
- QUEUE_EMPTY = 'QUEUE EMPTY'
6
- QUEUE_DELETED = 'QUEUE DELETED'
7
- EXCHANGE_DELETED = 'EXCHANGE DELETED'
8
- BIND_SUCCEEDED = 'BIND SUCCEEDED'
9
- UNBIND_SUCCEEDED = 'UNBIND SUCCEEDED'
10
-
11
- # specific error definitions
12
- class ProtocolError < StandardError; end
13
- class ServerDownError < StandardError; end
14
- class BufferOverflowError < StandardError; end
15
- class InvalidTypeError < StandardError; end
16
- class ConnectionError < StandardError; end
17
- class MessageError < StandardError; end
18
- end
@@ -1,30 +0,0 @@
1
- module API
2
- class Header
3
-
4
- attr_reader :client
5
-
6
- def initialize(client, header_obj)
7
- @client = client
8
- @header = header_obj
9
- end
10
-
11
- # Acknowledges the receipt of this message with the server.
12
- def ack
13
- client.send(Protocol::Basic::Ack.new(:delivery_tag => properties[:delivery_tag]))
14
- end
15
-
16
- # Reject this message (XXX currently unimplemented in rabbitmq)
17
- # * :requeue => true | false (default false)
18
- def reject(opts = {})
19
- client.send(Protocol::Basic::Reject.new(opts.merge(:delivery_tag => properties[:delivery_tag])))
20
- end
21
-
22
- def method_missing(meth, *args, &blk)
23
- @header.send(meth, *args, &blk)
24
- end
25
-
26
- def inspect
27
- @header.inspect
28
- end
29
- end
30
- end
@@ -1,274 +0,0 @@
1
- if [].map.respond_to? :with_index
2
- class Array #:nodoc:
3
- def enum_with_index
4
- each.with_index
5
- end
6
- end
7
- else
8
- require 'enumerator'
9
- end
10
-
11
- module Transport
12
- class Buffer #:nodoc: all
13
-
14
- def initialize data = ''
15
- @data = data
16
- @pos = 0
17
- end
18
-
19
- attr_reader :pos
20
-
21
- def data
22
- @data.clone
23
- end
24
- alias :contents :data
25
- alias :to_s :data
26
-
27
- def << data
28
- @data << data.to_s
29
- self
30
- end
31
-
32
- def length
33
- @data.length
34
- end
35
-
36
- def empty?
37
- pos == length
38
- end
39
-
40
- def rewind
41
- @pos = 0
42
- end
43
-
44
- def read_properties *types
45
- types.shift if types.first == :properties
46
-
47
- i = 0
48
- values = []
49
-
50
- while props = read(:short)
51
- (0..14).each do |n|
52
- # no more property types
53
- break unless types[i]
54
-
55
- # if flag is set
56
- if props & (1<<(15-n)) != 0
57
- if types[i] == :bit
58
- # bit values exist in flags only
59
- values << true
60
- else
61
- # save type name for later reading
62
- values << types[i]
63
- end
64
- else
65
- # property not set or is false bit
66
- values << (types[i] == :bit ? false : nil)
67
- end
68
-
69
- i+=1
70
- end
71
-
72
- # bit(0) == 0 means no more property flags
73
- break unless props & 1 == 1
74
- end
75
-
76
- values.map do |value|
77
- value.is_a?(Symbol) ? read(value) : value
78
- end
79
- end
80
-
81
- def read *types
82
- if types.first == :properties
83
- return read_properties(*types)
84
- end
85
-
86
- values = types.map do |type|
87
- case type
88
- when :octet
89
- _read(1, 'C')
90
- when :short
91
- _read(2, 'n')
92
- when :long
93
- _read(4, 'N')
94
- when :longlong
95
- upper, lower = _read(8, 'NN')
96
- upper << 32 | lower
97
- when :shortstr
98
- _read read(:octet)
99
- when :longstr
100
- _read read(:long)
101
- when :timestamp
102
- Time.at read(:longlong)
103
- when :table
104
- t = Hash.new
105
-
106
- table = Buffer.new(read(:longstr))
107
- until table.empty?
108
- key, type = table.read(:shortstr, :octet)
109
- key = key.intern
110
- t[key] ||= case type
111
- when 83 # 'S'
112
- table.read(:longstr)
113
- when 73 # 'I'
114
- table.read(:long)
115
- when 68 # 'D'
116
- exp = table.read(:octet)
117
- num = table.read(:long)
118
- num / 10.0**exp
119
- when 84 # 'T'
120
- table.read(:timestamp)
121
- when 70 # 'F'
122
- table.read(:table)
123
- end
124
- end
125
-
126
- t
127
- when :bit
128
- if (@bits ||= []).empty?
129
- val = read(:octet)
130
- @bits = (0..7).map{|i| (val & 1<<i) != 0 }
131
- end
132
-
133
- @bits.shift
134
- else
135
- raise API::InvalidTypeError, "Cannot read data of type #{type}"
136
- end
137
- end
138
-
139
- types.size == 1 ? values.first : values
140
- end
141
-
142
- def write type, data
143
- case type
144
- when :octet
145
- _write(data, 'C')
146
- when :short
147
- _write(data, 'n')
148
- when :long
149
- _write(data, 'N')
150
- when :longlong
151
- lower = data & 0xffffffff
152
- upper = (data & ~0xffffffff) >> 32
153
- _write([upper, lower], 'NN')
154
- when :shortstr
155
- data = (data || '').to_s
156
- _write([data.length, data], 'Ca*')
157
- when :longstr
158
- if data.is_a? Hash
159
- write(:table, data)
160
- else
161
- data = (data || '').to_s
162
- _write([data.length, data], 'Na*')
163
- end
164
- when :timestamp
165
- write(:longlong, data.to_i)
166
- when :table
167
- data ||= {}
168
- write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
169
- table.write(:shortstr, key.to_s)
170
-
171
- case value
172
- when String
173
- table.write(:octet, 83) # 'S'
174
- table.write(:longstr, value.to_s)
175
- when Fixnum
176
- table.write(:octet, 73) # 'I'
177
- table.write(:long, value)
178
- when Float
179
- table.write(:octet, 68) # 'D'
180
- # XXX there's gotta be a better way to do this..
181
- exp = value.to_s.split('.').last.length
182
- num = value * 10**exp
183
- table.write(:octet, exp)
184
- table.write(:long, num)
185
- when Time
186
- table.write(:octet, 84) # 'T'
187
- table.write(:timestamp, value)
188
- when Hash
189
- table.write(:octet, 70) # 'F'
190
- table.write(:table, value)
191
- end
192
-
193
- table
194
- end)
195
- when :bit
196
- [*data].to_enum(:each_slice, 8).each{|bits|
197
- write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
198
- byte |= 1<<i if bit
199
- byte
200
- })
201
- }
202
- when :properties
203
- values = []
204
- data.enum_with_index.inject(0) do |short, ((type, value), i)|
205
- n = i % 15
206
- last = i+1 == data.size
207
-
208
- if (n == 0 and i != 0) or last
209
- if data.size > i+1
210
- short |= 1<<0
211
- elsif last and value
212
- values << [type,value]
213
- short |= 1<<(15-n)
214
- end
215
-
216
- write(:short, short)
217
- short = 0
218
- end
219
-
220
- if value and !last
221
- values << [type,value]
222
- short |= 1<<(15-n)
223
- end
224
-
225
- short
226
- end
227
-
228
- values.each do |type, value|
229
- write(type, value) unless type == :bit
230
- end
231
- else
232
- raise API::InvalidTypeError, "Cannot write data of type #{type}"
233
- end
234
-
235
- self
236
- end
237
-
238
- def extract
239
- begin
240
- cur_data, cur_pos = @data.clone, @pos
241
- yield self
242
- rescue API::BufferOverflowError
243
- @data, @pos = cur_data, cur_pos
244
- nil
245
- end
246
- end
247
-
248
- def _read(size, pack = nil)
249
- if @data.is_a?(API::Client)
250
- raw = @data.read(size)
251
- return raw if raw.nil? or pack.nil?
252
- return raw.unpack(pack).first
253
- end
254
-
255
- if @pos + size > length
256
- raise API::BufferOverflowError
257
- else
258
- data = @data[@pos,size]
259
- @data[@pos,size] = ''
260
- if pack
261
- data = data.unpack(pack)
262
- data = data.pop if data.size == 1
263
- end
264
- data
265
- end
266
- end
267
-
268
- def _write data, pack = nil
269
- data = [*data].pack(pack) if pack
270
- @data[@pos,0] = data
271
- @pos += data.length
272
- end
273
- end
274
- end