bunny 0.8.0 → 0.9.0.pre1

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.
Files changed (91) hide show
  1. data/.gitignore +7 -1
  2. data/.travis.yml +14 -4
  3. data/ChangeLog.md +72 -0
  4. data/Gemfile +17 -11
  5. data/README.md +82 -0
  6. data/bunny.gemspec +6 -13
  7. data/examples/connection/heartbeat.rb +17 -0
  8. data/lib/bunny.rb +40 -56
  9. data/lib/bunny/channel.rb +615 -19
  10. data/lib/bunny/channel_id_allocator.rb +59 -0
  11. data/lib/bunny/compatibility.rb +24 -0
  12. data/lib/bunny/concurrent/condition.rb +63 -0
  13. data/lib/bunny/consumer.rb +42 -26
  14. data/lib/bunny/consumer_tag_generator.rb +22 -0
  15. data/lib/bunny/consumer_work_pool.rb +67 -0
  16. data/lib/bunny/exceptions.rb +128 -0
  17. data/lib/bunny/exchange.rb +131 -136
  18. data/lib/bunny/framing.rb +53 -0
  19. data/lib/bunny/heartbeat_sender.rb +59 -0
  20. data/lib/bunny/main_loop.rb +70 -0
  21. data/lib/bunny/message_metadata.rb +126 -0
  22. data/lib/bunny/queue.rb +102 -275
  23. data/lib/bunny/session.rb +478 -0
  24. data/lib/bunny/socket.rb +44 -0
  25. data/lib/bunny/system_timer.rb +9 -9
  26. data/lib/bunny/transport.rb +179 -0
  27. data/lib/bunny/version.rb +1 -1
  28. data/spec/compatibility/queue_declare_spec.rb +40 -0
  29. data/spec/higher_level_api/integration/basic_ack_spec.rb +54 -0
  30. data/spec/higher_level_api/integration/basic_consume_spec.rb +51 -0
  31. data/spec/higher_level_api/integration/basic_get_spec.rb +47 -0
  32. data/spec/higher_level_api/integration/basic_nack_spec.rb +39 -0
  33. data/spec/higher_level_api/integration/basic_publish_spec.rb +105 -0
  34. data/spec/higher_level_api/integration/basic_qos_spec.rb +32 -0
  35. data/spec/higher_level_api/integration/basic_recover_spec.rb +18 -0
  36. data/spec/higher_level_api/integration/basic_reject_spec.rb +53 -0
  37. data/spec/higher_level_api/integration/basic_return_spec.rb +33 -0
  38. data/spec/higher_level_api/integration/channel_close_spec.rb +29 -0
  39. data/spec/higher_level_api/integration/channel_flow_spec.rb +24 -0
  40. data/spec/higher_level_api/integration/channel_open_spec.rb +57 -0
  41. data/spec/higher_level_api/integration/channel_open_stress_spec.rb +22 -0
  42. data/spec/higher_level_api/integration/confirm_select_spec.rb +19 -0
  43. data/spec/higher_level_api/integration/connection_spec.rb +340 -0
  44. data/spec/higher_level_api/integration/exchange_bind_spec.rb +31 -0
  45. data/spec/higher_level_api/integration/exchange_declare_spec.rb +183 -0
  46. data/spec/higher_level_api/integration/exchange_delete_spec.rb +37 -0
  47. data/spec/higher_level_api/integration/exchange_unbind_spec.rb +40 -0
  48. data/spec/higher_level_api/integration/queue_bind_spec.rb +109 -0
  49. data/spec/higher_level_api/integration/queue_declare_spec.rb +129 -0
  50. data/spec/higher_level_api/integration/queue_delete_spec.rb +38 -0
  51. data/spec/higher_level_api/integration/queue_purge_spec.rb +30 -0
  52. data/spec/higher_level_api/integration/queue_unbind_spec.rb +33 -0
  53. data/spec/higher_level_api/integration/tx_commit_spec.rb +21 -0
  54. data/spec/higher_level_api/integration/tx_rollback_spec.rb +21 -0
  55. data/spec/lower_level_api/integration/basic_cancel_spec.rb +57 -0
  56. data/spec/lower_level_api/integration/basic_consume_spec.rb +100 -0
  57. data/spec/spec_helper.rb +64 -0
  58. data/spec/unit/bunny_spec.rb +15 -0
  59. data/spec/unit/concurrent/condition_spec.rb +66 -0
  60. metadata +135 -93
  61. data/CHANGELOG +0 -21
  62. data/README.textile +0 -76
  63. data/Rakefile +0 -14
  64. data/examples/simple.rb +0 -32
  65. data/examples/simple_ack.rb +0 -35
  66. data/examples/simple_consumer.rb +0 -55
  67. data/examples/simple_fanout.rb +0 -41
  68. data/examples/simple_headers.rb +0 -42
  69. data/examples/simple_publisher.rb +0 -29
  70. data/examples/simple_topic.rb +0 -61
  71. data/ext/amqp-0.9.1.json +0 -389
  72. data/ext/config.yml +0 -4
  73. data/ext/qparser.rb +0 -426
  74. data/lib/bunny/client.rb +0 -370
  75. data/lib/bunny/subscription.rb +0 -92
  76. data/lib/qrack/amq-client-url.rb +0 -165
  77. data/lib/qrack/channel.rb +0 -20
  78. data/lib/qrack/client.rb +0 -247
  79. data/lib/qrack/errors.rb +0 -5
  80. data/lib/qrack/protocol/protocol.rb +0 -135
  81. data/lib/qrack/protocol/spec.rb +0 -525
  82. data/lib/qrack/qrack.rb +0 -20
  83. data/lib/qrack/queue.rb +0 -40
  84. data/lib/qrack/subscription.rb +0 -152
  85. data/lib/qrack/transport/buffer.rb +0 -305
  86. data/lib/qrack/transport/frame.rb +0 -102
  87. data/spec/spec_09/amqp_url_spec.rb +0 -19
  88. data/spec/spec_09/bunny_spec.rb +0 -76
  89. data/spec/spec_09/connection_spec.rb +0 -34
  90. data/spec/spec_09/exchange_spec.rb +0 -173
  91. data/spec/spec_09/queue_spec.rb +0 -240
@@ -1,20 +0,0 @@
1
- # encoding: utf-8
2
-
3
- $: << File.expand_path(File.dirname(__FILE__))
4
-
5
- require 'protocol/spec'
6
- require 'protocol/protocol'
7
-
8
- require 'transport/buffer'
9
- require 'transport/frame'
10
-
11
- require 'qrack/client'
12
- require 'qrack/channel'
13
- require 'qrack/queue'
14
- require 'bunny/consumer'
15
- require 'qrack/errors'
16
-
17
- module Qrack
18
- include Protocol
19
- include Transport
20
- end
@@ -1,40 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Qrack
4
-
5
- # Queue ancestor class
6
- class Queue
7
-
8
- # @return [AMQ::Client::Consumer] Default consumer (registered with {Queue#subscribe}).
9
- attr_accessor :default_consumer
10
-
11
- attr_reader :name, :client
12
-
13
- attr_accessor :delivery_tag
14
-
15
-
16
- # Returns consumer count from {Queue#status}.
17
- def consumer_count
18
- s = status
19
- s[:consumer_count]
20
- end
21
-
22
- # Returns message count from {Queue#status}.
23
- def message_count
24
- s = status
25
- s[:message_count]
26
- end
27
-
28
- # Publishes a message to the queue via the default nameless '' direct exchange.
29
-
30
- # @return [NilClass] nil
31
- # @deprecated
32
- # @note This method will be removed before 0.8 release.
33
- def publish(data, opts = {})
34
- Bunny.deprecation_warning("Qrack::Queue#publish", "0.8", "Use direct_exchange = bunny.exchange(''); direct_exchange.publish('message', key: queue.name) if you want to publish directly to one given queue. For more informations see https://github.com/ruby-amqp/bunny/issues/15 and for more theoretical explanation check http://bit.ly/nOF1CK")
35
- exchange.publish(data, opts)
36
- end
37
-
38
- end
39
-
40
- end
@@ -1,152 +0,0 @@
1
- # encoding: utf-8
2
-
3
- #################################################
4
- # WARNING: THIS CLASS IS DEPRECATED, DO NOT #
5
- # USE IT DIRECTLY! USE BUNNY::CONSUMER INSTEAD! #
6
- #################################################
7
-
8
- module Qrack
9
- # Subscription ancestor class
10
- # @deprecated
11
- class Subscription
12
-
13
- attr_accessor :consumer_tag, :delivery_tag, :message_max, :timeout, :ack, :exclusive
14
- attr_reader :client, :queue, :message_count
15
-
16
- def initialize(client, queue, opts = {})
17
- @client = client
18
- @queue = queue
19
-
20
- # Get timeout value
21
- @timeout = opts[:timeout] || nil
22
-
23
- # Get maximum amount of messages to process
24
- @message_max = opts[:message_max] || nil
25
-
26
- # If a consumer tag is not passed in the server will generate one
27
- @consumer_tag = opts[:consumer_tag] || nil
28
-
29
- # Ignore the :nowait option if passed, otherwise program will hang waiting for a
30
- # response from the server causing an error.
31
- opts.delete(:nowait)
32
-
33
- # Do we want to have to provide an acknowledgement?
34
- @ack = opts[:ack] || nil
35
-
36
- # Does this consumer want exclusive use of the queue?
37
- @exclusive = opts[:exclusive] || false
38
-
39
- # Initialize message counter
40
- @message_count = 0
41
-
42
- # Store cancellator
43
- @cancellator = opts[:cancellator]
44
-
45
- # Store options
46
- @opts = opts
47
- end
48
-
49
- def start(&blk)
50
- # Do not process any messages if zero message_max
51
- if message_max == 0
52
- return
53
- end
54
-
55
- # Notify server about new consumer
56
- setup_consumer
57
-
58
- # We need to keep track of three possible subscription states
59
- # :subscribed, :pending, and :unsubscribed
60
- # 'pending' occurs because of network latency, where we tried to unsubscribe but were already given a message
61
- subscribe_state = :subscribed
62
-
63
- # Start subscription loop
64
- loop do
65
-
66
- begin
67
- method = client.next_method(:timeout => timeout, :cancellator => @cancellator)
68
- rescue Qrack::FrameTimeout
69
- begin
70
- queue.unsubscribe
71
- subscribe_state = :unsubscribed
72
-
73
- break
74
- rescue Bunny::ProtocolError
75
- # Unsubscribe failed because we actually got a message, so we're in a weird state.
76
- # We have to keep processing the message or else it may be lost...
77
- # ...and there is also a CancelOk method floating around that we need to consume from the socket
78
-
79
- method = client.last_method
80
- subscribe_state = :pending
81
- end
82
- end
83
-
84
- # Increment message counter
85
- @message_count += 1
86
-
87
- # get delivery tag to use for acknowledge
88
- queue.delivery_tag = method.delivery_tag if @ack
89
- header = client.next_payload
90
-
91
- # The unsubscribe ok may be sprinked into the payload
92
- if subscribe_state == :pending and header.is_a?(Qrack::Protocol::Basic::CancelOk)
93
- # We popped off the CancelOk, so we don't have to keep looking for it
94
- subscribe_state = :unsubscribed
95
-
96
- # Get the actual header now
97
- header = client.next_payload
98
- end
99
-
100
- # If maximum frame size is smaller than message payload body then message
101
- # will have a message header and several message bodies
102
- msg = ''
103
- while msg.length < header.size
104
- message = client.next_payload
105
-
106
- # The unsubscribe ok may be sprinked into the payload
107
- if subscribe_state == :pending and message.is_a?(Qrack::Protocol::Basic::CancelOk)
108
- # We popped off the CancelOk, so we don't have to keep looking for it
109
- subscribe_state = :unsubscribed
110
- next
111
- end
112
-
113
- msg << message
114
- end
115
-
116
- # If block present, pass the message info to the block for processing
117
- blk.call({:header => header, :payload => msg, :delivery_details => method.arguments}) if !blk.nil?
118
-
119
- # Unsubscribe if we've encountered the maximum number of messages
120
- if subscribe_state == :subscribed and !message_max.nil? and message_count == message_max
121
- queue.unsubscribe
122
- subscribe_state = :unsubscribed
123
- end
124
-
125
- # Exit the loop if we've unsubscribed
126
- if subscribe_state != :subscribed
127
- # We still haven't found the CancelOk, so it's the next method
128
- if subscribe_state == :pending
129
- method = client.next_method
130
- client.check_response(method, Qrack::Protocol::Basic::CancelOk, "Error unsubscribing from queue #{queue.name}, got #{method.class}")
131
-
132
- subscribe_state = :unsubscribed
133
- end
134
-
135
- # Acknowledge receipt of the final message
136
- queue.ack() if @ack
137
-
138
- # Quit the loop
139
- break
140
- end
141
-
142
- # Have to do the ack here because the ack triggers the release of messages from the server
143
- # if you are using Client#qos prefetch and you will get extra messages sent through before
144
- # the unsubscribe takes effect to stop messages being sent to this consumer unless the ack is
145
- # deferred.
146
- queue.ack() if @ack
147
- end
148
- end
149
-
150
- end
151
-
152
- end
@@ -1,305 +0,0 @@
1
- # encoding: utf-8
2
-
3
- if [].map.respond_to? :with_index
4
- class Array #:nodoc:
5
- def enum_with_index
6
- each.with_index
7
- end
8
- end
9
- else
10
- require 'enumerator'
11
- end
12
-
13
- module Qrack
14
- module Transport #:nodoc: all
15
- class Buffer
16
-
17
- def initialize data = ''
18
- @data = data
19
- @pos = 0
20
- end
21
-
22
- attr_reader :pos
23
-
24
- def data
25
- @data.clone
26
- end
27
- alias :contents :data
28
- alias :to_s :data
29
-
30
- def << data
31
- @data << data.to_s
32
- self
33
- end
34
-
35
- def length
36
- @data.bytesize
37
- end
38
-
39
- def empty?
40
- pos == length
41
- end
42
-
43
- def rewind
44
- @pos = 0
45
- end
46
-
47
- def read_properties *types
48
- types.shift if types.first == :properties
49
-
50
- i = 0
51
- values = []
52
-
53
- while props = read(:short)
54
- (0..14).each do |n|
55
- # no more property types
56
- break unless types[i]
57
-
58
- # if flag is set
59
- if props & (1<<(15-n)) != 0
60
- if types[i] == :bit
61
- # bit values exist in flags only
62
- values << true
63
- else
64
- # save type name for later reading
65
- values << types[i]
66
- end
67
- else
68
- # property not set or is false bit
69
- values << (types[i] == :bit ? false : nil)
70
- end
71
-
72
- i+=1
73
- end
74
-
75
- # bit(0) == 0 means no more property flags
76
- break unless props & 1 == 1
77
- end
78
-
79
- values.map do |value|
80
- value.is_a?(Symbol) ? read(value) : value
81
- end
82
- end
83
-
84
- def read *types
85
- if types.first == :properties
86
- return read_properties(*types)
87
- end
88
-
89
- values = types.map do |type|
90
- case type
91
- when :octet
92
- _read(1, 'C')
93
- when :short
94
- _read(2, 'n')
95
- when :long
96
- _read(4, 'N')
97
- when :longlong
98
- upper, lower = _read(8, 'NN')
99
- upper << 32 | lower
100
- when :shortstr
101
- _read read(:octet)
102
- when :longstr
103
- _read read(:long)
104
- when :timestamp
105
- Time.at read(:longlong)
106
- when :table
107
- t = Hash.new
108
-
109
- table = Buffer.new(read(:longstr))
110
- until table.empty?
111
- key, type = table.read(:shortstr, :octet)
112
- key = key.intern
113
- t[key] ||= case type
114
- when 83 # 'S'
115
- table.read(:longstr)
116
- when 73 # 'I'
117
- table.read(:long)
118
- when 68 # 'D'
119
- exp = table.read(:octet)
120
- num = table.read(:long)
121
- num / 10.0**exp
122
- when 84 # 'T'
123
- table.read(:timestamp)
124
- when 70 # 'F'
125
- table.read(:table)
126
- when 65 # 'A'
127
- table.read(:array)
128
- when 108 # 'l'
129
- table.read(:longlong)
130
- when 116 # 't'
131
- table.read(:octet)
132
- end
133
- end
134
-
135
- t
136
- when :bit
137
- if (@bits ||= []).empty?
138
- val = read(:octet)
139
- @bits = (0..7).map{|i| (val & (1 << i)) != 0 }
140
- end
141
-
142
- @bits.shift
143
- when :array
144
- a = Array.new
145
-
146
- array = Buffer.new(read(:longstr))
147
- until array.empty?
148
- type = array.read(:octet)
149
- a << case type
150
- when 70 # 'F'
151
- array.read(:table)
152
- end
153
- end
154
-
155
- a
156
- else
157
- raise Qrack::InvalidTypeError, "Cannot read data of type #{type}"
158
- end
159
- end
160
-
161
- types.size == 1 ? values.first : values
162
- end
163
-
164
- def write type, data
165
- case type
166
- when :octet
167
- _write(data, 'C')
168
- when :short
169
- _write(data, 'n')
170
- when :long
171
- _write(data, 'N')
172
- when :longlong
173
- lower = data & 0xffffffff
174
- upper = (data & ~0xffffffff) >> 32
175
- _write([upper, lower], 'NN')
176
- when :shortstr
177
- data = (data || '').to_s
178
- _write([data.bytesize, data], 'Ca*')
179
- when :longstr
180
- if data.is_a? Hash
181
- write(:table, data)
182
- else
183
- data = (data || '').to_s
184
- _write([data.bytesize, data], 'Na*')
185
- end
186
- when :timestamp
187
- write(:longlong, data.to_i)
188
- when :table
189
- data ||= {}
190
- write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
191
- table.write(:shortstr, key.to_s)
192
-
193
- case value
194
- when String
195
- table.write(:octet, 83) # 'S'
196
- table.write(:longstr, value.to_s)
197
- when Fixnum
198
- table.write(:octet, 73) # 'I'
199
- table.write(:long, value)
200
- when Float
201
- table.write(:octet, 68) # 'D'
202
- # XXX there's gotta be a better way to do this..
203
- exp = value.to_s.split('.').last.bytesize
204
- num = value * 10**exp
205
- table.write(:octet, exp)
206
- table.write(:long, num)
207
- when Time
208
- table.write(:octet, 84) # 'T'
209
- table.write(:timestamp, value)
210
- when Hash
211
- table.write(:octet, 70) # 'F'
212
- table.write(:table, value)
213
- end
214
-
215
- table
216
- end)
217
- when :bit
218
- [*data].to_enum(:each_slice, 8).each{|bits|
219
- write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
220
- byte |= (1 << i) if bit
221
- byte
222
- })
223
- }
224
- when :properties
225
- values = []
226
- data.enum_with_index.inject(0) do |short, ((type, value), i)|
227
- n = i % 15
228
- last = i+1 == data.size
229
-
230
- if (n == 0 and i != 0) or last
231
- if data.size > i+1
232
- short |= (1 << 0)
233
- elsif last and value
234
- values << [type,value]
235
- short |= 1<<(15-n)
236
- end
237
-
238
- write(:short, short)
239
- short = 0
240
- end
241
-
242
- if value and !last
243
- values << [type,value]
244
- short |= 1<<(15-n)
245
- end
246
-
247
- short
248
- end
249
-
250
- values.each do |type, value|
251
- write(type, value) unless type == :bit
252
- end
253
- else
254
- raise Qrack::InvalidTypeError, "Cannot write data of type #{type}"
255
- end
256
-
257
- self
258
- end
259
-
260
- def extract
261
- begin
262
- cur_data, cur_pos = @data.clone, @pos
263
- yield self
264
- rescue Qrack::BufferOverflowError
265
- @data, @pos = cur_data, cur_pos
266
- nil
267
- end
268
- end
269
-
270
- def read_ready?(timeout, cancellator)
271
- if @data.is_a?(Qrack::Client)
272
- @data.read_ready?(timeout, cancellator)
273
- else
274
- true
275
- end
276
- end
277
-
278
- def _read(size, pack = nil)
279
- if @data.is_a?(Qrack::Client)
280
- raw = @data.read(size)
281
- return raw if raw.nil? or pack.nil?
282
- return raw.unpack(pack).first
283
- end
284
-
285
- if @pos + size > length
286
- raise Qrack::BufferOverflowError
287
- else
288
- data = @data[@pos,size]
289
- @data[@pos,size] = ''
290
- if pack
291
- data = data.unpack(pack)
292
- data = data.pop if data.size == 1
293
- end
294
- data
295
- end
296
- end
297
-
298
- def _write data, pack = nil
299
- data = [*data].pack(pack) if pack
300
- @data[@pos,0] = data
301
- @pos += data.bytesize
302
- end
303
- end
304
- end
305
- end