amqp 0.7.0.pre → 0.7.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.
Files changed (71) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +2 -0
  3. data/CHANGELOG +8 -2
  4. data/CONTRIBUTORS +22 -0
  5. data/Gemfile +3 -3
  6. data/README.md +20 -11
  7. data/Rakefile +30 -6
  8. data/amqp.gemspec +1 -1
  9. data/bin/cleanify.rb +50 -0
  10. data/examples/amqp/simple.rb +6 -4
  11. data/examples/mq/ack.rb +8 -6
  12. data/examples/mq/automatic_binding_for_default_direct_exchange.rb +65 -0
  13. data/examples/mq/callbacks.rb +9 -1
  14. data/examples/mq/clock.rb +17 -17
  15. data/examples/mq/hashtable.rb +19 -10
  16. data/examples/mq/internal.rb +13 -11
  17. data/examples/mq/logger.rb +38 -36
  18. data/examples/mq/multiclock.rb +16 -7
  19. data/examples/mq/pingpong.rb +16 -7
  20. data/examples/mq/pop.rb +8 -6
  21. data/examples/mq/primes-simple.rb +2 -0
  22. data/examples/mq/primes.rb +7 -5
  23. data/examples/mq/stocks.rb +14 -5
  24. data/lib/amqp.rb +12 -8
  25. data/lib/amqp/buffer.rb +35 -158
  26. data/lib/amqp/client.rb +34 -22
  27. data/lib/amqp/frame.rb +8 -64
  28. data/lib/amqp/protocol.rb +21 -70
  29. data/lib/amqp/server.rb +11 -9
  30. data/lib/amqp/spec.rb +8 -6
  31. data/lib/amqp/version.rb +2 -0
  32. data/lib/ext/blankslate.rb +3 -1
  33. data/lib/ext/em.rb +2 -0
  34. data/lib/ext/emfork.rb +13 -11
  35. data/lib/mq.rb +253 -156
  36. data/lib/mq/collection.rb +6 -88
  37. data/lib/mq/exchange.rb +70 -13
  38. data/lib/mq/header.rb +12 -6
  39. data/lib/mq/logger.rb +9 -7
  40. data/lib/mq/queue.rb +42 -30
  41. data/lib/mq/rpc.rb +6 -4
  42. data/protocol/codegen.rb +20 -18
  43. data/research/api.rb +10 -46
  44. data/research/primes-forked.rb +9 -7
  45. data/research/primes-processes.rb +74 -72
  46. data/research/primes-threaded.rb +9 -7
  47. data/spec/integration/automatic_binding_for_default_direct_exchange_spec.rb +61 -0
  48. data/spec/mq_helper.rb +70 -0
  49. data/spec/spec_helper.rb +84 -29
  50. data/spec/unit/amqp/buffer_spec.rb +178 -0
  51. data/spec/unit/amqp/client_spec.rb +472 -0
  52. data/spec/unit/amqp/frame_spec.rb +60 -0
  53. data/spec/unit/amqp/misc_spec.rb +123 -0
  54. data/spec/unit/amqp/protocol_spec.rb +53 -0
  55. data/spec/unit/mq/channel_close_spec.rb +15 -0
  56. data/spec/unit/mq/collection_spec.rb +129 -0
  57. data/spec/unit/mq/exchange_declaration_spec.rb +524 -0
  58. data/spec/unit/mq/misc_spec.rb +228 -0
  59. data/spec/unit/mq/mq_basic_spec.rb +39 -0
  60. data/spec/unit/mq/queue_declaration_spec.rb +97 -0
  61. data/spec/unit/mq/queue_spec.rb +71 -0
  62. metadata +33 -21
  63. data/Gemfile.lock +0 -16
  64. data/old/README +0 -30
  65. data/old/Rakefile +0 -12
  66. data/old/amqp-0.8.json +0 -606
  67. data/old/amqp_spec.rb +0 -796
  68. data/old/amqpc.rb +0 -695
  69. data/old/codegen.rb +0 -148
  70. data/spec/channel_close_spec.rb +0 -13
  71. data/spec/sync_async_spec.rb +0 -52
@@ -1,6 +1,8 @@
1
+ # encoding: utf-8
2
+
1
3
  require File.expand_path('../ext/em', __FILE__)
2
4
  require File.expand_path('../ext/blankslate', __FILE__)
3
-
5
+
4
6
  %w[ version buffer spec protocol frame client ].each do |file|
5
7
  require File.expand_path("../amqp/#{file}", __FILE__)
6
8
  end
@@ -76,7 +78,7 @@ module AMQP
76
78
  # block. See the code examples in MQ for details.
77
79
  #
78
80
  def self.start *args, &blk
79
- EM.run{
81
+ EM.run {
80
82
  @conn ||= connect *args
81
83
  @conn.callback(&blk) if blk
82
84
  @conn
@@ -86,15 +88,17 @@ module AMQP
86
88
  class << self
87
89
  alias :run :start
88
90
  end
89
-
91
+
90
92
  def self.stop
91
93
  if @conn and not @closing
92
94
  @closing = true
93
- @conn.close{
94
- yield if block_given?
95
- @conn = nil
96
- @closing = false
97
- }
95
+ EM.next_tick do
96
+ @conn.close {
97
+ yield if block_given?
98
+ @conn = nil
99
+ @closing = false
100
+ }
101
+ end
98
102
  end
99
103
  end
100
104
 
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  if [].map.respond_to? :with_index
2
4
  class Array #:nodoc:
3
5
  def enum_with_index
@@ -12,14 +14,14 @@ module AMQP
12
14
  class Buffer #:nodoc: all
13
15
  class Overflow < StandardError; end
14
16
  class InvalidType < StandardError; end
15
-
16
- def initialize data = ''
17
+
18
+ def initialize(data = '')
17
19
  @data = data
18
20
  @pos = 0
19
21
  end
20
22
 
21
23
  attr_reader :pos
22
-
24
+
23
25
  def data
24
26
  @data.clone
25
27
  end
@@ -30,22 +32,22 @@ module AMQP
30
32
  @data << data.to_s
31
33
  self
32
34
  end
33
-
35
+
34
36
  def length
35
37
  @data.bytesize
36
38
  end
37
-
39
+
38
40
  def empty?
39
41
  pos == length
40
42
  end
41
-
43
+
42
44
  def rewind
43
45
  @pos = 0
44
46
  end
45
-
46
- def read_properties *types
47
+
48
+ def read_properties(*types)
47
49
  types.shift if types.first == :properties
48
-
50
+
49
51
  i = 0
50
52
  values = []
51
53
 
@@ -53,9 +55,9 @@ module AMQP
53
55
  (0..14).each do |n|
54
56
  # no more property types
55
57
  break unless types[i]
56
-
58
+
57
59
  # if flag is set
58
- if props & (1<<(15-n)) != 0
60
+ if props & (1 << (15-n)) != 0
59
61
  if types[i] == :bit
60
62
  # bit values exist in flags only
61
63
  values << true
@@ -68,7 +70,7 @@ module AMQP
68
70
  values << (types[i] == :bit ? false : nil)
69
71
  end
70
72
 
71
- i+=1
73
+ i += 1
72
74
  end
73
75
 
74
76
  # bit(0) == 0 means no more property flags
@@ -80,7 +82,7 @@ module AMQP
80
82
  end
81
83
  end
82
84
 
83
- def read *types
85
+ def read(*types)
84
86
  if types.first == :properties
85
87
  return read_properties(*types)
86
88
  end
@@ -129,7 +131,7 @@ module AMQP
129
131
  when :bit
130
132
  if (@bits ||= []).empty?
131
133
  val = read(:octet)
132
- @bits = (0..7).map{|i| (val & 1<<i) != 0 }
134
+ @bits = (0..7).map { |i| (val & 1 << i) != 0 }
133
135
  end
134
136
 
135
137
  @bits.shift
@@ -137,11 +139,11 @@ module AMQP
137
139
  raise InvalidType, "Cannot read data of type #{type}"
138
140
  end
139
141
  end
140
-
142
+
141
143
  types.size == 1 ? values.first : values
142
144
  end
143
-
144
- def write type, data
145
+
146
+ def write(type, data)
145
147
  case type
146
148
  when :octet
147
149
  _write(data, 'C')
@@ -195,9 +197,9 @@ module AMQP
195
197
  table
196
198
  end)
197
199
  when :bit
198
- [*data].to_enum(:each_slice, 8).each{|bits|
199
- write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
200
- byte |= 1<<i if bit
200
+ [*data].to_enum(:each_slice, 8).each { |bits|
201
+ write(:octet, bits.enum_with_index.inject(0) { |byte, (bit, i)|
202
+ byte |= 1 << i if bit
201
203
  byte
202
204
  })
203
205
  }
@@ -209,10 +211,10 @@ module AMQP
209
211
 
210
212
  if (n == 0 and i != 0) or last
211
213
  if data.size > i+1
212
- short |= 1<<0
214
+ short |= 1 << 0
213
215
  elsif last and value
214
- values << [type,value]
215
- short |= 1<<(15-n)
216
+ values << [type, value]
217
+ short |= 1 << (15-n)
216
218
  end
217
219
 
218
220
  write(:short, short)
@@ -220,20 +222,20 @@ module AMQP
220
222
  end
221
223
 
222
224
  if value and !last
223
- values << [type,value]
224
- short |= 1<<(15-n)
225
+ values << [type, value]
226
+ short |= 1 << (15-n)
225
227
  end
226
228
 
227
229
  short
228
230
  end
229
-
231
+
230
232
  values.each do |type, value|
231
233
  write(type, value) unless type == :bit
232
234
  end
233
235
  else
234
236
  raise InvalidType, "Cannot write data of type #{type}"
235
237
  end
236
-
238
+
237
239
  self
238
240
  end
239
241
 
@@ -247,12 +249,12 @@ module AMQP
247
249
  end
248
250
  end
249
251
 
250
- def _read size, pack = nil
252
+ def _read(size, pack = nil)
251
253
  if @pos + size > length
252
254
  raise Overflow
253
255
  else
254
- data = @data[@pos,size]
255
- @data[@pos,size] = ''
256
+ data = @data[@pos, size]
257
+ @data[@pos, size] = ''
256
258
  if pack
257
259
  data = data.unpack(pack)
258
260
  data = data.pop if data.size == 1
@@ -260,136 +262,11 @@ module AMQP
260
262
  data
261
263
  end
262
264
  end
263
-
264
- def _write data, pack = nil
265
+
266
+ def _write(data, pack = nil)
265
267
  data = [*data].pack(pack) if pack
266
- @data[@pos,0] = data
268
+ @data[@pos, 0] = data
267
269
  @pos += data.bytesize
268
270
  end
269
271
  end
270
272
  end
271
-
272
- if $0 =~ /bacon/ or $0 == __FILE__
273
- require 'bacon'
274
- include AMQP
275
-
276
- describe Buffer do
277
- before do
278
- @buf = Buffer.new
279
- end
280
-
281
- should 'have contents' do
282
- @buf.contents.should == ''
283
- end
284
-
285
- should 'initialize with data' do
286
- @buf = Buffer.new('abc')
287
- @buf.contents.should == 'abc'
288
- end
289
-
290
- should 'append raw data' do
291
- @buf << 'abc'
292
- @buf << 'def'
293
- @buf.contents.should == 'abcdef'
294
- end
295
-
296
- should 'append other buffers' do
297
- @buf << Buffer.new('abc')
298
- @buf.data.should == 'abc'
299
- end
300
-
301
- should 'have a position' do
302
- @buf.pos.should == 0
303
- end
304
-
305
- should 'have a length' do
306
- @buf.length.should == 0
307
- @buf << 'abc'
308
- @buf.length.should == 3
309
- end
310
-
311
- should 'know the end' do
312
- @buf.empty?.should == true
313
- end
314
-
315
- should 'read and write data' do
316
- @buf._write('abc')
317
- @buf.rewind
318
- @buf._read(2).should == 'ab'
319
- @buf._read(1).should == 'c'
320
- end
321
-
322
- should 'raise on overflow' do
323
- lambda{ @buf._read(1) }.should.raise Buffer::Overflow
324
- end
325
-
326
- should 'raise on invalid types' do
327
- lambda{ @buf.read(:junk) }.should.raise Buffer::InvalidType
328
- lambda{ @buf.write(:junk, 1) }.should.raise Buffer::InvalidType
329
- end
330
-
331
- { :octet => 0b10101010,
332
- :short => 100,
333
- :long => 100_000_000,
334
- :longlong => 666_555_444_333_222_111,
335
- :shortstr => 'hello',
336
- :longstr => 'bye'*500,
337
- :timestamp => time = Time.at(Time.now.to_i),
338
- :table => { :this => 'is', :a => 'hash', :with => {:nested => 123, :and => time, :also => 123.456} },
339
- :bit => true
340
- }.each do |type, value|
341
-
342
- should "read and write a #{type}" do
343
- @buf.write(type, value)
344
- @buf.rewind
345
- @buf.read(type).should == value
346
- @buf.should.be.empty
347
- end
348
-
349
- end
350
-
351
- should 'read and write multiple bits' do
352
- bits = [true, false, false, true, true, false, false, true, true, false]
353
- @buf.write(:bit, bits)
354
- @buf.write(:octet, 100)
355
-
356
- @buf.rewind
357
-
358
- bits.map do
359
- @buf.read(:bit)
360
- end.should == bits
361
- @buf.read(:octet).should == 100
362
- end
363
-
364
- should 'read and write properties' do
365
- properties = ([
366
- [:octet, 1],
367
- [:shortstr, 'abc'],
368
- [:bit, true],
369
- [:bit, false],
370
- [:shortstr, nil],
371
- [:timestamp, nil],
372
- [:table, { :a => 'hash' }],
373
- ]*5).sort_by{rand}
374
-
375
- @buf.write(:properties, properties)
376
- @buf.rewind
377
- @buf.read(:properties, *properties.map{|type,_| type }).should == properties.map{|_,value| value }
378
- @buf.should.be.empty
379
- end
380
-
381
- should 'do transactional reads with #extract' do
382
- @buf.write :octet, 8
383
- orig = @buf.to_s
384
-
385
- @buf.rewind
386
- @buf.extract do |b|
387
- b.read :octet
388
- b.read :short
389
- end
390
-
391
- @buf.pos.should == 0
392
- @buf.data.should == orig
393
- end
394
- end
395
- end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require File.expand_path('../frame', __FILE__)
2
4
 
3
5
  require 'uri'
@@ -6,7 +8,9 @@ module AMQP
6
8
  class Error < StandardError; end
7
9
 
8
10
  module BasicClient
9
- def process_frame frame
11
+ attr_reader :broker
12
+
13
+ def process_frame(frame)
10
14
  if mq = channels[frame.channel]
11
15
  mq.process_frame(frame)
12
16
  return
@@ -16,9 +20,10 @@ module AMQP
16
20
  when Frame::Method
17
21
  case method = frame.payload
18
22
  when Protocol::Connection::Start
23
+ @broker = method
19
24
  send Protocol::Connection::StartOk.new({:platform => 'Ruby/EventMachine',
20
25
  :product => 'AMQP',
21
- :information => 'http://github.com/tmm1/amqp',
26
+ :information => 'http://github.com/ruby-amqp/amqp',
22
27
  :version => VERSION},
23
28
  'AMQPLAIN',
24
29
  {:LOGIN => @settings[:user],
@@ -34,6 +39,8 @@ module AMQP
34
39
  :capabilities => '',
35
40
  :insist => @settings[:insist])
36
41
 
42
+ @on_disconnect = method(:disconnected)
43
+
37
44
  when Protocol::Connection::OpenOk
38
45
  succeed(self)
39
46
 
@@ -60,14 +67,16 @@ module AMQP
60
67
  module Client
61
68
  include EM::Deferrable
62
69
 
63
- def initialize opts = {}
70
+ def initialize(opts = {})
64
71
  @settings = opts
65
72
  extend AMQP.client
66
73
 
67
- @on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
74
+ @_channel_mutex = Mutex.new
75
+
76
+ @on_disconnect ||= proc { raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
68
77
 
69
78
  timeout @settings[:timeout] if @settings[:timeout]
70
- errback{ @on_disconnect.call } unless @reconnecting
79
+ errback { @on_disconnect.call } unless @reconnecting
71
80
 
72
81
  @connected = false
73
82
  end
@@ -75,9 +84,8 @@ module AMQP
75
84
  def connection_completed
76
85
  start_tls if @settings[:ssl]
77
86
  log 'connected'
78
- # @on_disconnect = proc{ raise Error, 'Disconnected from server' }
87
+ # @on_disconnect = proc { raise Error, 'Disconnected from server' }
79
88
  unless @closing
80
- @on_disconnect = method(:disconnected)
81
89
  @reconnecting = false
82
90
  end
83
91
 
@@ -96,11 +104,11 @@ module AMQP
96
104
  def unbind
97
105
  log 'disconnected'
98
106
  @connected = false
99
- EM.next_tick{ @on_disconnect.call }
107
+ EM.next_tick { @on_disconnect.call }
100
108
  end
101
109
 
102
- def add_channel mq
103
- (@_channel_mutex ||= Mutex.new).synchronize do
110
+ def add_channel(mq)
111
+ @_channel_mutex.synchronize do
104
112
  channels[ key = (channels.keys.max || 0) + 1 ] = mq
105
113
  key
106
114
  end
@@ -110,7 +118,7 @@ module AMQP
110
118
  @channels ||= {}
111
119
  end
112
120
 
113
- def receive_data data
121
+ def receive_data(data)
114
122
  # log 'receive_data', data
115
123
  @buf << data
116
124
 
@@ -120,12 +128,12 @@ module AMQP
120
128
  end
121
129
  end
122
130
 
123
- def process_frame frame
131
+ def process_frame(frame)
124
132
  # this is a stub meant to be
125
133
  # replaced by the module passed into initialize
126
134
  end
127
135
 
128
- def send data, opts = {}
136
+ def send(data, opts = {})
129
137
  channel = opts[:channel] ||= 0
130
138
  data = data.to_frame(channel) unless data.is_a? Frame
131
139
  data.channel = channel
@@ -141,16 +149,16 @@ module AMQP
141
149
  # end
142
150
  #:startdoc:
143
151
 
144
- def close &on_disconnect
152
+ def close(&on_disconnect)
145
153
  if on_disconnect
146
154
  @closing = true
147
- @on_disconnect = proc{
155
+ @on_disconnect = proc {
148
156
  on_disconnect.call
149
157
  @closing = false
150
158
  }
151
159
  end
152
160
 
153
- callback{ |c|
161
+ callback { |c|
154
162
  if c.channels.any?
155
163
  c.channels.each do |ch, mq|
156
164
  mq.close
@@ -164,10 +172,14 @@ module AMQP
164
172
  }
165
173
  end
166
174
 
167
- def reconnect force = false
175
+ def closing?
176
+ @closing
177
+ end
178
+
179
+ def reconnect(force = false)
168
180
  if @reconnecting and not force
169
181
  # wait 1 second after first reconnect attempt, in between each subsequent attempt
170
- EM.add_timer(1){ reconnect(true) }
182
+ EM.add_timer(1) { reconnect(true) }
171
183
  return
172
184
  end
173
185
 
@@ -179,7 +191,7 @@ module AMQP
179
191
 
180
192
  mqs = @channels
181
193
  @channels = {}
182
- mqs.each{ |_,mq| mq.reset } if mqs
194
+ mqs.each { |_, mq| mq.reset } if mqs
183
195
  end
184
196
 
185
197
  log 'reconnecting'
@@ -199,7 +211,7 @@ module AMQP
199
211
  EM.connect opts[:host], opts[:port], self, opts
200
212
  end
201
213
 
202
- def connection_status &blk
214
+ def connection_status(&blk)
203
215
  @connection_status = blk
204
216
  end
205
217
 
@@ -210,7 +222,7 @@ module AMQP
210
222
  reconnect
211
223
  end
212
224
 
213
- def log *args
225
+ def log(*args)
214
226
  return unless @settings[:logging] or AMQP.logging
215
227
  require 'pp'
216
228
  pp args
@@ -226,7 +238,7 @@ module AMQP
226
238
  opts[:vhost] = URI.unescape(uri.path) if uri.path
227
239
  opts[:host] = uri.host if uri.host
228
240
  opts[:port] = uri.port ? uri.port :
229
- {"amqp"=>5672, "amqps"=>5671}[uri.scheme]
241
+ {"amqp" => 5672, "amqps" => 5671}[uri.scheme]
230
242
  opts[:ssl] = uri.scheme == "amqps"
231
243
  return opts
232
244
  end