totty-amqp 0.6.7.1.totty

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/README +143 -0
  2. data/Rakefile +20 -0
  3. data/amqp.todo +32 -0
  4. data/doc/EXAMPLE_01_PINGPONG +2 -0
  5. data/doc/EXAMPLE_02_CLOCK +2 -0
  6. data/doc/EXAMPLE_03_STOCKS +2 -0
  7. data/doc/EXAMPLE_04_MULTICLOCK +2 -0
  8. data/doc/EXAMPLE_05_ACK +2 -0
  9. data/doc/EXAMPLE_05_POP +2 -0
  10. data/doc/EXAMPLE_06_HASHTABLE +2 -0
  11. data/examples/amqp/simple.rb +79 -0
  12. data/examples/mq/ack.rb +45 -0
  13. data/examples/mq/clock.rb +56 -0
  14. data/examples/mq/hashtable.rb +52 -0
  15. data/examples/mq/internal.rb +49 -0
  16. data/examples/mq/logger.rb +88 -0
  17. data/examples/mq/multiclock.rb +49 -0
  18. data/examples/mq/pingpong.rb +45 -0
  19. data/examples/mq/pop.rb +43 -0
  20. data/examples/mq/primes-simple.rb +19 -0
  21. data/examples/mq/primes.rb +99 -0
  22. data/examples/mq/stocks.rb +58 -0
  23. data/lib/amqp/buffer.rb +395 -0
  24. data/lib/amqp/client.rb +210 -0
  25. data/lib/amqp/frame.rb +124 -0
  26. data/lib/amqp/protocol.rb +212 -0
  27. data/lib/amqp/server.rb +99 -0
  28. data/lib/amqp/spec.rb +832 -0
  29. data/lib/amqp/version.rb +3 -0
  30. data/lib/amqp.rb +152 -0
  31. data/lib/ext/blankslate.rb +7 -0
  32. data/lib/ext/em.rb +8 -0
  33. data/lib/ext/emfork.rb +69 -0
  34. data/lib/mq/exchange.rb +314 -0
  35. data/lib/mq/header.rb +33 -0
  36. data/lib/mq/logger.rb +89 -0
  37. data/lib/mq/queue.rb +455 -0
  38. data/lib/mq/rpc.rb +100 -0
  39. data/lib/mq.rb +877 -0
  40. data/old/README +30 -0
  41. data/old/Rakefile +12 -0
  42. data/old/amqp-0.8.json +606 -0
  43. data/old/amqp_spec.rb +796 -0
  44. data/old/amqpc.rb +695 -0
  45. data/old/codegen.rb +148 -0
  46. data/protocol/amqp-0.8.json +617 -0
  47. data/protocol/amqp-0.8.xml +3908 -0
  48. data/protocol/codegen.rb +173 -0
  49. data/protocol/doc.txt +281 -0
  50. data/research/api.rb +88 -0
  51. data/research/primes-forked.rb +63 -0
  52. data/research/primes-processes.rb +135 -0
  53. data/research/primes-threaded.rb +49 -0
  54. data/totty-amqp.gemspec +87 -0
  55. metadata +142 -0
@@ -0,0 +1,45 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+
4
+ AMQP.start(:host => 'localhost') do
5
+
6
+ def log *args
7
+ p [ Time.now, *args ]
8
+ end
9
+
10
+ # AMQP.logging = true
11
+
12
+ amq = MQ.new
13
+ EM.add_periodic_timer(1){
14
+ puts
15
+
16
+ log :sending, 'ping'
17
+ amq.queue('one').publish('ping')
18
+ }
19
+
20
+ amq = MQ.new
21
+ amq.queue('one').subscribe{ |msg|
22
+ log 'one', :received, msg, :sending, 'pong'
23
+ amq.queue('two').publish('pong')
24
+ }
25
+
26
+ amq = MQ.new
27
+ amq.queue('two').subscribe{ |msg|
28
+ log 'two', :received, msg
29
+ }
30
+
31
+ end
32
+
33
+ __END__
34
+
35
+ [Sun Jul 20 03:52:24 -0700 2008, :sending, "ping"]
36
+ [Sun Jul 20 03:52:24 -0700 2008, "one", :received, "ping", :sending, "pong"]
37
+ [Sun Jul 20 03:52:24 -0700 2008, "two", :received, "pong"]
38
+
39
+ [Sun Jul 20 03:52:25 -0700 2008, :sending, "ping"]
40
+ [Sun Jul 20 03:52:25 -0700 2008, "one", :received, "ping", :sending, "pong"]
41
+ [Sun Jul 20 03:52:25 -0700 2008, "two", :received, "pong"]
42
+
43
+ [Sun Jul 20 03:52:26 -0700 2008, :sending, "ping"]
44
+ [Sun Jul 20 03:52:26 -0700 2008, "one", :received, "ping", :sending, "pong"]
45
+ [Sun Jul 20 03:52:26 -0700 2008, "two", :received, "pong"]
@@ -0,0 +1,43 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+ require 'pp'
4
+
5
+ Signal.trap('INT') { AMQP.stop{ EM.stop } }
6
+ Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
7
+
8
+ AMQP.start do
9
+ queue = MQ.queue('awesome')
10
+
11
+ queue.publish('Totally rad 1')
12
+ queue.publish('Totally rad 2')
13
+ EM.add_timer(5){ queue.publish('Totally rad 3') }
14
+
15
+ queue.pop{ |msg|
16
+ unless msg
17
+ # queue was empty
18
+ p [Time.now, :queue_empty!]
19
+
20
+ # try again in 1 second
21
+ EM.add_timer(1){ queue.pop }
22
+ else
23
+ # process this message
24
+ p [Time.now, msg]
25
+
26
+ # get the next message in the queue
27
+ queue.pop
28
+ end
29
+ }
30
+ end
31
+
32
+ __END__
33
+
34
+ [Wed Oct 15 15:24:30 -0700 2008, "Totally rad 1"]
35
+ [Wed Oct 15 15:24:30 -0700 2008, "Totally rad 2"]
36
+ [Wed Oct 15 15:24:30 -0700 2008, :queue_empty!]
37
+ [Wed Oct 15 15:24:31 -0700 2008, :queue_empty!]
38
+ [Wed Oct 15 15:24:32 -0700 2008, :queue_empty!]
39
+ [Wed Oct 15 15:24:33 -0700 2008, :queue_empty!]
40
+ [Wed Oct 15 15:24:34 -0700 2008, :queue_empty!]
41
+ [Wed Oct 15 15:24:35 -0700 2008, "Totally rad 3"]
42
+ [Wed Oct 15 15:24:35 -0700 2008, :queue_empty!]
43
+ [Wed Oct 15 15:24:36 -0700 2008, :queue_empty!]
@@ -0,0 +1,19 @@
1
+ MAX = 1000
2
+
3
+ class Fixnum
4
+ def prime?
5
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
6
+ end
7
+ end
8
+
9
+ class PrimeChecker
10
+ def is_prime? number
11
+ number.prime?
12
+ end
13
+ end
14
+
15
+ prime_checker = PrimeChecker.new
16
+
17
+ (10_000...(10_000+MAX)).each do |n|
18
+ prime_checker.is_prime? n
19
+ end
@@ -0,0 +1,99 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+
4
+ # check MAX numbers for prime-ness
5
+ MAX = 1000
6
+
7
+ # logging
8
+ def log *args
9
+ p args
10
+ end
11
+
12
+ # spawn workers
13
+ workers = ARGV[0] ? (Integer(ARGV[0]) rescue 1) : 1
14
+ AMQP.fork(workers) do
15
+
16
+ log MQ.id, :started
17
+
18
+ class Fixnum
19
+ def prime?
20
+ ('1' * self) !~ /^1?$|^(11+?)\1+$/
21
+ end
22
+ end
23
+
24
+ class PrimeChecker
25
+ def is_prime? number
26
+ log "prime checker #{MQ.id}", :prime?, number
27
+ number.prime?
28
+ end
29
+ end
30
+
31
+ MQ.rpc('prime checker', PrimeChecker.new)
32
+
33
+ end
34
+
35
+ # use workers to check which numbers are prime
36
+ AMQP.start(:host => 'localhost') do
37
+
38
+ prime_checker = MQ.rpc('prime checker')
39
+
40
+ (10_000...(10_000+MAX)).each do |num|
41
+ log :checking, num
42
+
43
+ prime_checker.is_prime?(num) { |is_prime|
44
+ log :prime?, num, is_prime
45
+ (@primes||=[]) << num if is_prime
46
+
47
+ if (@responses = (@responses || 0) + 1) == MAX
48
+ log :primes=, @primes
49
+ EM.stop_event_loop
50
+ end
51
+ }
52
+
53
+ end
54
+
55
+ end
56
+
57
+ __END__
58
+
59
+ $ uname -a
60
+ Linux gc 2.6.24-ARCH #1 SMP PREEMPT Sun Mar 30 10:50:22 CEST 2008 x86_64 Intel(R) Xeon(R) CPU X3220 @ 2.40GHz GenuineIntel GNU/Linux
61
+
62
+ $ cat /proc/cpuinfo | grep processor | wc -l
63
+ 4
64
+
65
+ $ time ruby primes-simple.rb
66
+
67
+ real 0m16.055s
68
+ user 0m16.052s
69
+ sys 0m0.000s
70
+
71
+ $ time ruby primes.rb 1 >/dev/null
72
+
73
+ real 0m18.278s
74
+ user 0m0.993s
75
+ sys 0m0.027s
76
+
77
+ $ time ruby primes.rb 2 >/dev/null
78
+
79
+ real 0m17.316s
80
+ user 0m0.967s
81
+ sys 0m0.053s
82
+
83
+ $ time ruby primes.rb 4 >/dev/null
84
+
85
+ real 0m8.229s
86
+ user 0m1.010s
87
+ sys 0m0.030s
88
+
89
+ $ time ruby primes.rb 8 >/dev/null
90
+
91
+ real 0m5.893s
92
+ user 0m1.023s
93
+ sys 0m0.050s
94
+
95
+ $ time ruby primes.rb 16 >/dev/null
96
+
97
+ real 0m5.601s
98
+ user 0m0.990s
99
+ sys 0m0.043s
@@ -0,0 +1,58 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+
4
+ AMQP.start(:host => 'localhost') do
5
+
6
+ def log *args
7
+ p [ Time.now, *args ]
8
+ end
9
+
10
+ def publish_stock_prices
11
+ mq = MQ.new
12
+ EM.add_periodic_timer(1){
13
+ puts
14
+
15
+ { :appl => 170+rand(1000)/100.0,
16
+ :msft => 22+rand(500)/100.0
17
+ }.each do |stock, price|
18
+ stock = "usd.#{stock}"
19
+
20
+ log :publishing, stock, price
21
+ mq.topic('stocks').publish(price, :key => stock)
22
+ end
23
+ }
24
+ end
25
+
26
+ def watch_appl_stock
27
+ mq = MQ.new
28
+ mq.queue('apple stock').bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
29
+ log 'apple stock', price
30
+ }
31
+ end
32
+
33
+ def watch_us_stocks
34
+ mq = MQ.new
35
+ mq.queue('us stocks').bind(mq.topic('stocks'), :key => 'usd.*').subscribe{ |info, price|
36
+ log 'us stock', info.routing_key, price
37
+ }
38
+ end
39
+
40
+ publish_stock_prices
41
+ watch_appl_stock
42
+ watch_us_stocks
43
+
44
+ end
45
+
46
+ __END__
47
+
48
+ [Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.appl", 173.45]
49
+ [Fri Aug 15 01:39:00 -0700 2008, :publishing, "usd.msft", 26.98]
50
+ [Fri Aug 15 01:39:00 -0700 2008, "apple stock", "173.45"]
51
+ [Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.appl", "173.45"]
52
+ [Fri Aug 15 01:39:00 -0700 2008, "us stock", "usd.msft", "26.98"]
53
+
54
+ [Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.appl", 179.72]
55
+ [Fri Aug 15 01:39:01 -0700 2008, :publishing, "usd.msft", 26.56]
56
+ [Fri Aug 15 01:39:01 -0700 2008, "apple stock", "179.72"]
57
+ [Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.appl", "179.72"]
58
+ [Fri Aug 15 01:39:01 -0700 2008, "us stock", "usd.msft", "26.56"]
@@ -0,0 +1,395 @@
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 AMQP
12
+ class Buffer #:nodoc: all
13
+ class Overflow < StandardError; end
14
+ class InvalidType < StandardError; end
15
+
16
+ def initialize data = ''
17
+ @data = data
18
+ @pos = 0
19
+ end
20
+
21
+ attr_reader :pos
22
+
23
+ def data
24
+ @data.clone
25
+ end
26
+ alias :contents :data
27
+ alias :to_s :data
28
+
29
+ def << data
30
+ @data << data.to_s
31
+ self
32
+ end
33
+
34
+ def length
35
+ @data.length
36
+ end
37
+
38
+ def empty?
39
+ pos == length
40
+ end
41
+
42
+ def rewind
43
+ @pos = 0
44
+ end
45
+
46
+ def read_properties *types
47
+ types.shift if types.first == :properties
48
+
49
+ i = 0
50
+ values = []
51
+
52
+ while props = read(:short)
53
+ (0..14).each do |n|
54
+ # no more property types
55
+ break unless types[i]
56
+
57
+ # if flag is set
58
+ if props & (1<<(15-n)) != 0
59
+ if types[i] == :bit
60
+ # bit values exist in flags only
61
+ values << true
62
+ else
63
+ # save type name for later reading
64
+ values << types[i]
65
+ end
66
+ else
67
+ # property not set or is false bit
68
+ values << (types[i] == :bit ? false : nil)
69
+ end
70
+
71
+ i+=1
72
+ end
73
+
74
+ # bit(0) == 0 means no more property flags
75
+ break unless props & 1 == 1
76
+ end
77
+
78
+ values.map do |value|
79
+ value.is_a?(Symbol) ? read(value) : value
80
+ end
81
+ end
82
+
83
+ def read *types
84
+ if types.first == :properties
85
+ return read_properties(*types)
86
+ end
87
+
88
+ values = types.map do |type|
89
+ case type
90
+ when :octet
91
+ _read(1, 'C')
92
+ when :short
93
+ _read(2, 'n')
94
+ when :long
95
+ _read(4, 'N')
96
+ when :longlong
97
+ upper, lower = _read(8, 'NN')
98
+ upper << 32 | lower
99
+ when :shortstr
100
+ _read read(:octet)
101
+ when :longstr
102
+ _read read(:long)
103
+ when :timestamp
104
+ Time.at read(:longlong)
105
+ when :table
106
+ t = Hash.new
107
+
108
+ table = Buffer.new(read(:longstr))
109
+ until table.empty?
110
+ key, type = table.read(:shortstr, :octet)
111
+ key = key.intern
112
+ t[key] ||= case type
113
+ when 83 # 'S'
114
+ table.read(:longstr)
115
+ when 73 # 'I'
116
+ table.read(:long)
117
+ when 68 # 'D'
118
+ exp = table.read(:octet)
119
+ num = table.read(:long)
120
+ num / 10.0**exp
121
+ when 84 # 'T'
122
+ table.read(:timestamp)
123
+ when 70 # 'F'
124
+ table.read(:table)
125
+ end
126
+ end
127
+
128
+ t
129
+ when :bit
130
+ if (@bits ||= []).empty?
131
+ val = read(:octet)
132
+ @bits = (0..7).map{|i| (val & 1<<i) != 0 }
133
+ end
134
+
135
+ @bits.shift
136
+ else
137
+ raise InvalidType, "Cannot read data of type #{type}"
138
+ end
139
+ end
140
+
141
+ types.size == 1 ? values.first : values
142
+ end
143
+
144
+ def write type, data
145
+ case type
146
+ when :octet
147
+ _write(data, 'C')
148
+ when :short
149
+ _write(data, 'n')
150
+ when :long
151
+ _write(data, 'N')
152
+ when :longlong
153
+ lower = data & 0xffffffff
154
+ upper = (data & ~0xffffffff) >> 32
155
+ _write([upper, lower], 'NN')
156
+ when :shortstr
157
+ data = (data || '').to_s
158
+ _write([data.length, data], 'Ca*')
159
+ when :longstr
160
+ if data.is_a? Hash
161
+ write(:table, data)
162
+ else
163
+ data = (data || '').to_s
164
+ _write([data.length, data], 'Na*')
165
+ end
166
+ when :timestamp
167
+ write(:longlong, data.to_i)
168
+ when :table
169
+ data ||= {}
170
+ write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
171
+ table.write(:shortstr, key.to_s)
172
+
173
+ case value
174
+ when String
175
+ table.write(:octet, 83) # 'S'
176
+ table.write(:longstr, value.to_s)
177
+ when Fixnum
178
+ table.write(:octet, 73) # 'I'
179
+ table.write(:long, value)
180
+ when Float
181
+ table.write(:octet, 68) # 'D'
182
+ # XXX there's gotta be a better way to do this..
183
+ exp = value.to_s.split('.').last.length
184
+ num = value * 10**exp
185
+ table.write(:octet, exp)
186
+ table.write(:long, num)
187
+ when Time
188
+ table.write(:octet, 84) # 'T'
189
+ table.write(:timestamp, value)
190
+ when Hash
191
+ table.write(:octet, 70) # 'F'
192
+ table.write(:table, value)
193
+ end
194
+
195
+ table
196
+ end)
197
+ 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
201
+ byte
202
+ })
203
+ }
204
+ when :properties
205
+ values = []
206
+ data.enum_with_index.inject(0) do |short, ((type, value), i)|
207
+ n = i % 15
208
+ last = i+1 == data.size
209
+
210
+ if (n == 0 and i != 0) or last
211
+ if data.size > i+1
212
+ short |= 1<<0
213
+ elsif last and value
214
+ values << [type,value]
215
+ short |= 1<<(15-n)
216
+ end
217
+
218
+ write(:short, short)
219
+ short = 0
220
+ end
221
+
222
+ if value and !last
223
+ values << [type,value]
224
+ short |= 1<<(15-n)
225
+ end
226
+
227
+ short
228
+ end
229
+
230
+ values.each do |type, value|
231
+ write(type, value) unless type == :bit
232
+ end
233
+ else
234
+ raise InvalidType, "Cannot write data of type #{type}"
235
+ end
236
+
237
+ self
238
+ end
239
+
240
+ def extract
241
+ begin
242
+ cur_data, cur_pos = @data.clone, @pos
243
+ yield self
244
+ rescue Overflow
245
+ @data, @pos = cur_data, cur_pos
246
+ nil
247
+ end
248
+ end
249
+
250
+ def _read size, pack = nil
251
+ if @pos + size > length
252
+ raise Overflow
253
+ else
254
+ data = @data[@pos,size]
255
+ @data[@pos,size] = ''
256
+ if pack
257
+ data = data.unpack(pack)
258
+ data = data.pop if data.size == 1
259
+ end
260
+ data
261
+ end
262
+ end
263
+
264
+ def _write data, pack = nil
265
+ data = [*data].pack(pack) if pack
266
+ @data[@pos,0] = data
267
+ @pos += data.length
268
+ end
269
+ end
270
+ 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