amqp 0.7.0.pre → 0.7.0

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