tmm1-amqp 0.6.0 → 0.6.1

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.
data/README CHANGED
@@ -21,10 +21,14 @@ To use with RabbitMQ, first run the server:
21
21
 
22
22
  To get started, refer to the various bundled examples:
23
23
 
24
- ruby examples/mq/simple.rb # low-level Queue/Exchange api
25
24
  ruby examples/mq/pingpong.rb # 1-1 communication using amq.direct
26
25
  ruby examples/mq/clock.rb # 1-N communication using amq.fanout
27
26
  ruby examples/mq/stocks.rb # 1-subscriber communication using amq.topic
27
+
28
+ ruby examples/mq/multiclock.rb # header based routing (new rabbitmq feature)
29
+ ruby examples/mq/ack.rb # using ack
30
+ ruby examples/mq/pop.rb # pop off messages one at a time
31
+
28
32
  ruby examples/mq/hashtable.rb # simple async rpc layer
29
33
  ruby examples/mq/primes.rb 4 # parallelized prime number generation
30
34
  ruby examples/mq/logger.rb # simple logging api
@@ -32,6 +36,7 @@ To get started, refer to the various bundled examples:
32
36
  For more details into the lower level AMQP client API, run the simple client example:
33
37
 
34
38
  ruby examples/amqp/simple.rb # low-level AMQP api
39
+ ruby examples/mq/internal.rb # low-level Queue/Exchange api
35
40
 
36
41
  Or refer to protocol/doc.txt, which enumerates packets sent between a server and client
37
42
  during a typical session, in both binary and decoded formats.
@@ -0,0 +1,15 @@
1
+ task :codegen do
2
+ sh 'ruby protocol/codegen.rb > lib/amqp/spec.rb'
3
+ sh 'ruby lib/amqp/spec.rb'
4
+ end
5
+
6
+ task :spec do
7
+ sh 'bacon lib/amqp.rb'
8
+ end
9
+
10
+ task :gem do
11
+ sh 'gem build *.gemspec'
12
+ end
13
+
14
+ task :pkg => :gem
15
+ task :package => :gem
@@ -0,0 +1,83 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'amqp'
3
+ s.version = '0.6.1'
4
+ s.date = '2009-01-09'
5
+ s.summary = 'AMQP client implementation in Ruby/EventMachine'
6
+ s.email = "amqp@tmm1.net"
7
+ s.homepage = "http://amqp.rubyforge.org/"
8
+ s.description = "AMQP client implementation in Ruby/EventMachine"
9
+ s.has_rdoc = true
10
+ s.rdoc_options = '--include=examples'
11
+
12
+ # ruby -rpp -e' pp `git ls-files`.split("\n").grep(/^(doc|README)/) '
13
+ s.extra_rdoc_files = [
14
+ "README",
15
+ "doc/EXAMPLE_01_PINGPONG",
16
+ "doc/EXAMPLE_02_CLOCK",
17
+ "doc/EXAMPLE_03_STOCKS",
18
+ "doc/EXAMPLE_04_MULTICLOCK",
19
+ "doc/EXAMPLE_05_ACK",
20
+ "doc/EXAMPLE_05_POP",
21
+ "doc/EXAMPLE_06_HASHTABLE"
22
+ ]
23
+
24
+ s.authors = ["Aman Gupta"]
25
+ s.add_dependency('eventmachine', '>= 0.12.4')
26
+
27
+ # ruby -rpp -e' pp `git ls-files`.split("\n") '
28
+ s.files = [
29
+ "README",
30
+ "Rakefile",
31
+ "amqp.gemspec",
32
+ "amqp.todo",
33
+ "doc/EXAMPLE_01_PINGPONG",
34
+ "doc/EXAMPLE_02_CLOCK",
35
+ "doc/EXAMPLE_03_STOCKS",
36
+ "doc/EXAMPLE_04_MULTICLOCK",
37
+ "doc/EXAMPLE_05_ACK",
38
+ "doc/EXAMPLE_05_POP",
39
+ "doc/EXAMPLE_06_HASHTABLE",
40
+ "examples/amqp/simple.rb",
41
+ "examples/mq/ack.rb",
42
+ "examples/mq/clock.rb",
43
+ "examples/mq/pop.rb",
44
+ "examples/mq/hashtable.rb",
45
+ "examples/mq/internal.rb",
46
+ "examples/mq/logger.rb",
47
+ "examples/mq/multiclock.rb",
48
+ "examples/mq/pingpong.rb",
49
+ "examples/mq/primes-simple.rb",
50
+ "examples/mq/primes.rb",
51
+ "examples/mq/stocks.rb",
52
+ "lib/amqp.rb",
53
+ "lib/amqp/buffer.rb",
54
+ "lib/amqp/client.rb",
55
+ "lib/amqp/frame.rb",
56
+ "lib/amqp/protocol.rb",
57
+ "lib/amqp/server.rb",
58
+ "lib/amqp/spec.rb",
59
+ "lib/ext/blankslate.rb",
60
+ "lib/ext/em.rb",
61
+ "lib/ext/emfork.rb",
62
+ "lib/mq.rb",
63
+ "lib/mq/exchange.rb",
64
+ "lib/mq/header.rb",
65
+ "lib/mq/logger.rb",
66
+ "lib/mq/queue.rb",
67
+ "lib/mq/rpc.rb",
68
+ "old/README",
69
+ "old/Rakefile",
70
+ "old/amqp-0.8.json",
71
+ "old/amqp_spec.rb",
72
+ "old/amqpc.rb",
73
+ "old/codegen.rb",
74
+ "protocol/amqp-0.8.json",
75
+ "protocol/amqp-0.8.xml",
76
+ "protocol/codegen.rb",
77
+ "protocol/doc.txt",
78
+ "research/api.rb",
79
+ "research/primes-forked.rb",
80
+ "research/primes-processes.rb",
81
+ "research/primes-threaded.rb"
82
+ ]
83
+ end
@@ -0,0 +1,32 @@
1
+ - breaks with header values that are nil
2
+ - breaks with header values that are ruby objects (convert to strings?)
3
+ - sending utf8 data in 1.9 breaks
4
+
5
+ - generate amqp/spec.rb from original xml spec
6
+ - add peek and pop to queues
7
+ - use rabbitmq generated consumer tag from basic.consume-ok reply
8
+
9
+ - allow temporary queues with amq.queue(nil) syntax (use uuids)
10
+ - use as temp queue in rpc
11
+ - use uuids for message ids in rpc
12
+
13
+ - add ack/completed responses for messages
14
+ - deleting queues/bindings/exchanges
15
+ + queue.unbind
16
+ - queue.remove or queue.close or queue.delete
17
+ - exchange.remove
18
+ - rpc.remove
19
+
20
+ - handle errors and exceptions
21
+ binding to a non-existent (or not yet created in clock.rb) exchange
22
+ #<AMQP::Protocol::Channel::Close:0x11d35d4
23
+ @class_id=50,
24
+ @debug=1,
25
+ @method_id=20,
26
+ @reply_code=404,
27
+ @reply_text="NOT_FOUND - no exchange 'clock' in vhost '/'">>]
28
+
29
+ - handle connection.redirect during connect (for rabbitmq in distributed mode) [or just set insist to true]
30
+
31
+ - add amq.queue('name').size{ |num| "#{num} messages in the queue" } (send declare passive, look at declare-ok response)
32
+ - clean up MQ.default on disconnect
@@ -0,0 +1,2 @@
1
+ == Ping Pong Example
2
+ :include: mq/pingpong.rb
@@ -0,0 +1,2 @@
1
+ == Clock Example
2
+ :include: mq/clock.rb
@@ -0,0 +1,2 @@
1
+ == Stocks Example
2
+ :include: mq/stocks.rb
@@ -0,0 +1,2 @@
1
+ == Muti-format Clock Example
2
+ :include: mq/multiclock.rb
@@ -0,0 +1,2 @@
1
+ == Ack Example
2
+ :include: mq/ack.rb
@@ -0,0 +1,2 @@
1
+ == Pop Example
2
+ :include: mq/pop.rb
@@ -0,0 +1,2 @@
1
+ == HashTable RPC Example
2
+ :include: mq/hashtable.rb
@@ -1,13 +1,12 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
- require 'pp'
4
3
 
5
- # For ack to work appropriatly you must shutdown AMQP gracefully,
4
+ # For ack to work appropriately you must shutdown AMQP gracefully,
6
5
  # otherwise all items in your queue will be returned
7
6
  Signal.trap('INT') { AMQP.stop{ EM.stop } }
8
7
  Signal.trap('TERM'){ AMQP.stop{ EM.stop } }
9
8
 
10
- EM.run do
9
+ AMQP.start(:host => 'localhost') do
11
10
  MQ.queue('awesome').publish('Totally rad 1')
12
11
  MQ.queue('awesome').publish('Totally rad 2')
13
12
  MQ.queue('awesome').publish('Totally rad 3')
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p args
@@ -28,7 +28,7 @@ EM.run{
28
28
  log 'every 5 seconds', :received, time if time.strftime('%S').to_i%5 == 0
29
29
  }
30
30
 
31
- }
31
+ end
32
32
 
33
33
  __END__
34
34
 
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p args
@@ -37,10 +37,10 @@ EM.run{
37
37
  client.set(:one, 1)
38
38
  client.keys do |res|
39
39
  log 'client', :keys => res
40
- EM.stop_event_loop
40
+ AMQP.stop{ EM.stop }
41
41
  end
42
42
 
43
- }
43
+ end
44
44
 
45
45
  __END__
46
46
 
File without changes
@@ -4,7 +4,7 @@ require 'mq/logger'
4
4
 
5
5
  Logger = MQ::Logger
6
6
 
7
- EM.run{
7
+ AMQP.start(:host => 'localhost') do
8
8
  if ARGV[0] == 'server'
9
9
 
10
10
  MQ.queue('logger').bind(MQ.fanout('logging', :durable => true)).subscribe{|msg|
@@ -37,7 +37,7 @@ EM.run{
37
37
  log = Logger.new(:webserver, :timestamp, :hostname, &log.printer)
38
38
  log.info 'Request for /', :GET, :session => 'abc'
39
39
 
40
- AMQP.stop{ EM.stop_event_loop }
40
+ AMQP.stop{ EM.stop }
41
41
 
42
42
  else
43
43
 
@@ -50,7 +50,7 @@ EM.run{
50
50
  EM.stop
51
51
 
52
52
  end
53
- }
53
+ end
54
54
 
55
55
  __END__
56
56
 
@@ -0,0 +1,49 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'mq'
3
+ require 'time'
4
+
5
+ AMQP.start(:host => 'localhost') do
6
+
7
+ def log *args
8
+ p args
9
+ end
10
+
11
+ #AMQP.logging = true
12
+
13
+ clock = MQ.new.headers('multiformat_clock')
14
+ EM.add_periodic_timer(1){
15
+ puts
16
+
17
+ time = Time.new
18
+ ["iso8601","rfc2822"].each do |format|
19
+ formatted_time = time.send(format)
20
+ log :publish, format, formatted_time
21
+ clock.publish "#{formatted_time}", :headers => {"format" => format}
22
+ end
23
+ }
24
+
25
+ ["iso8601","rfc2822"].each do |format|
26
+ amq = MQ.new
27
+ amq.queue(format.to_s).bind(amq.headers('multiformat_clock'), :arguments => {"format" => format}).subscribe{ |time|
28
+ log "received #{format}", time
29
+ }
30
+ end
31
+
32
+ end
33
+
34
+ __END__
35
+
36
+ [:publish, "iso8601", "2009-02-13T19:55:40-08:00"]
37
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:40 -0800"]
38
+ ["received iso8601", "2009-02-13T19:55:40-08:00"]
39
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:40 -0800"]
40
+
41
+ [:publish, "iso8601", "2009-02-13T19:55:41-08:00"]
42
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:41 -0800"]
43
+ ["received iso8601", "2009-02-13T19:55:41-08:00"]
44
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:41 -0800"]
45
+
46
+ [:publish, "iso8601", "2009-02-13T19:55:42-08:00"]
47
+ [:publish, "rfc2822", "Fri, 13 Feb 2009 19:55:42 -0800"]
48
+ ["received iso8601", "2009-02-13T19:55:42-08:00"]
49
+ ["received rfc2822", "Fri, 13 Feb 2009 19:55:42 -0800"]
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p [ Time.now, *args ]
@@ -28,7 +28,7 @@ EM.run{
28
28
  log 'two', :received, msg
29
29
  }
30
30
 
31
- }
31
+ end
32
32
 
33
33
  __END__
34
34
 
File without changes
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  # spawn workers
13
13
  workers = ARGV[0] ? (Integer(ARGV[0]) rescue 1) : 1
14
- EM.fork(workers) do
14
+ AMQP.fork(workers) do
15
15
 
16
16
  log MQ.id, :started
17
17
 
@@ -33,7 +33,7 @@ EM.fork(workers) do
33
33
  end
34
34
 
35
35
  # use workers to check which numbers are prime
36
- EM.run{
36
+ AMQP.start(:host => 'localhost') do
37
37
 
38
38
  prime_checker = MQ.rpc('prime checker')
39
39
 
@@ -52,7 +52,7 @@ EM.run{
52
52
 
53
53
  end
54
54
 
55
- }
55
+ end
56
56
 
57
57
  __END__
58
58
 
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
3
 
4
- EM.run{
4
+ AMQP.start(:host => 'localhost') do
5
5
 
6
6
  def log *args
7
7
  p [ Time.now, *args ]
@@ -41,7 +41,7 @@ EM.run{
41
41
  watch_appl_stock
42
42
  watch_us_stocks
43
43
 
44
- }
44
+ end
45
45
 
46
46
  __END__
47
47
 
@@ -99,4 +99,14 @@ module AMQP
99
99
  }
100
100
  end
101
101
  end
102
+
103
+ def self.fork workers
104
+ EM.fork(workers) do
105
+ # clean up globals in the fork
106
+ Thread.current[:mq] = nil
107
+ AMQP.instance_variable_set('@conn', nil)
108
+
109
+ yield
110
+ end
111
+ end
102
112
  end
@@ -65,7 +65,7 @@ module AMQP
65
65
  @on_disconnect ||= proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
66
66
 
67
67
  timeout @settings[:timeout] if @settings[:timeout]
68
- errback{ @on_disconnect.call }
68
+ errback{ @on_disconnect.call } unless @reconnecting
69
69
  end
70
70
 
71
71
  def connection_completed
@@ -158,14 +158,14 @@ module AMQP
158
158
  end
159
159
 
160
160
  unless @reconnecting
161
+ @reconnecting = true
162
+
161
163
  @deferred_status = nil
162
164
  initialize(@settings)
163
165
 
164
166
  mqs = @channels
165
167
  @channels = {}
166
168
  mqs.each{ |_,mq| mq.reset } if mqs
167
-
168
- @reconnecting = true
169
169
  end
170
170
 
171
171
  log 'reconnecting'
@@ -186,4 +186,4 @@ module AMQP
186
186
  puts
187
187
  end
188
188
  end
189
- end
189
+ end
@@ -0,0 +1,99 @@
1
+ require 'amqp/frame'
2
+
3
+ module AMQP
4
+ module Server
5
+ def post_init
6
+ @buf = ''
7
+ @channels = {}
8
+ @started = false
9
+ end
10
+
11
+ def receive_data data
12
+ @buf << data
13
+
14
+ unless @started
15
+ if @buf.size >= 8
16
+ if @buf.slice!(0,8) == "AMQP\001\001\b\000"
17
+ send Protocol::Connection::Start.new(
18
+ 8,
19
+ 0,
20
+ {
21
+ :information => 'Licensed under the Ruby license. See http://github.com/tmm1/amqp',
22
+ :copyright => 'Copyright (c) 2008-2009 Aman Gupta',
23
+ :platform => 'Ruby/EventMachine',
24
+ :version => '0.6.1',
25
+ :product => 'SquirrelMQ'
26
+ },
27
+ 'PLAIN AMQPLAIN',
28
+ 'en_US'
29
+ )
30
+ else
31
+ close_connection
32
+ return
33
+ end
34
+ @started = true
35
+ else
36
+ return
37
+ end
38
+ end
39
+
40
+ while frame = Frame.parse(@buf)
41
+ process_frame frame
42
+ end
43
+ end
44
+
45
+ def process_frame frame
46
+ channel = frame.channel
47
+
48
+ case method = frame.payload
49
+ when Protocol::Connection::StartOk
50
+ @user = method.response[:LOGIN]
51
+ @pass = method.response[:PASSWORD]
52
+ send Protocol::Connection::Tune.new(0, 131072, 0)
53
+
54
+ when Protocol::Connection::TuneOk
55
+ # mnnk
56
+
57
+ when Protocol::Connection::Open
58
+ @vhost = method.virtual_host
59
+ send Protocol::Connection::OpenOk.new('localhost')
60
+
61
+ when Protocol::Channel::Open
62
+ @channels[channel] = []
63
+ send Protocol::Channel::OpenOk.new, :channel => channel
64
+
65
+ when Protocol::Access::Request
66
+ send Protocol::Access::RequestOk.new(101)
67
+ end
68
+ end
69
+
70
+ def send data, opts = {}
71
+ channel = opts[:channel] ||= 0
72
+ data = data.to_frame(channel) unless data.is_a? Frame
73
+ data.channel = channel
74
+
75
+ log 'send', data
76
+ send_data data.to_s
77
+ end
78
+
79
+ def self.start host = 'localhost', port = 5672
80
+ EM.start_server host, port, self
81
+ end
82
+
83
+ private
84
+
85
+ def log *args
86
+ require 'pp'
87
+ pp args
88
+ puts
89
+ end
90
+ end
91
+ end
92
+
93
+ if __FILE__ == $0
94
+ require 'rubygems'
95
+ require 'eventmachine'
96
+ EM.run{
97
+ AMQP::Server.start
98
+ }
99
+ end