tmm1-amqp 0.5.5 → 0.5.9

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
@@ -7,6 +7,8 @@ Simple AMQP driver for Ruby/EventMachine
7
7
  http://groups.google.com/group/ruby-amqp
8
8
  http://lists.rabbitmq.com/pipermail/rabbitmq-discuss/2008-July/001417.html
9
9
 
10
+ This library works with Ruby 1.8, Ruby 1.9 and JRuby.
11
+
10
12
  This library was tested primarily with RabbitMQ, although it should be compatible with any
11
13
  server implementing the AMQP 0-8 spec.
12
14
 
@@ -49,8 +51,8 @@ AMQP resources:
49
51
 
50
52
  Servers:
51
53
  RabbitMQ (Rabbit Technologies, Erlang/OTP, MPL) - http://rabbitmq.com
52
- ZeroMQ (iMatrix/FastMQ/Intel, C++, GPL3) - http://www.zeromq.org
53
- OpenAMQ (iMatrix, C, GPL2) - http://openamq.org
54
+ ZeroMQ (iMatix/FastMQ/Intel, C++, GPL3) - http://www.zeromq.org
55
+ OpenAMQ (iMatix, C, GPL2) - http://openamq.org
54
56
  ActiveMQ (Apache Foundation, Java, apache2) - http://activemq.apache.org
55
57
 
56
58
  Steve Vinoski explains AMQP in his column, Towards Integration
@@ -65,8 +67,8 @@ AMQP resources:
65
67
  ZeroMQ's analysis of the messaging technology market
66
68
  http://www.zeromq.org/whitepapers:market-analysis
67
69
 
68
- ZeroMQ's background to AMQP
69
- http://www.zeromq.org/whitepapers:amqp-analysis
70
+ Pieter Hintjens's background to AMQP
71
+ http://www.openamq.org/doc:amqp-background
70
72
 
71
73
  Barry Pederson's py-amqplib
72
74
  http://barryp.org/software/py-amqplib/
@@ -86,6 +88,7 @@ AMQP resources:
86
88
  http://hg.rabbitmq.com/rabbitmq-codegen/
87
89
 
88
90
  Erlang Exchange presentation on the implementation of RabbitMQ
91
+ http://skillsmatter.com/podcast/erlang/presenting-rabbitmq-an-erlang-based-implementatio-nof-amqp
89
92
  http://www.lshift.net/blog/2008/07/01/slides-from-our-erlang-exchange-talk
90
93
 
91
94
  Jonathan Conway's series on RabbitMQ and using it with Ruby/Merb
@@ -113,5 +116,8 @@ Messaging and distributed systems resources:
113
116
  Metaprotocol Taxonomy and Communications Patterns
114
117
  http://hessian.caucho.com/doc/metaprotocol-taxonomy.xtp
115
118
 
116
- Joe Armstrong on Erlang messaging vs RPC:
119
+ Joe Armstrong on Erlang messaging vs RPC
117
120
  http://armstrongonsoftware.blogspot.com/2008/05/road-we-didnt-go-down.html
121
+
122
+ SEDA: scalable internet services using message queues
123
+ http://www.eecs.harvard.edu/~mdw/papers/seda-sosp01.pdf
@@ -1,81 +1,20 @@
1
1
  $:.unshift File.dirname(__FILE__) + '/../../lib'
2
2
  require 'mq'
3
+ require 'mq/logger'
3
4
 
4
- class Logger
5
- def initialize *args, &block
6
- opts = args.pop if args.last.is_a? Hash
7
- opts ||= {}
8
- printer(block) if block
9
- @log = []
10
- @tags = ([:timestamp] + args).uniq
11
- end
12
-
13
- def log severity, *args
14
- opts = args.pop if args.last.is_a? Hash and args.size != 1
15
- opts ||= {}
16
- data = args.shift
17
-
18
- data = {:type => :exception,
19
- :name => data.class.to_s.intern,
20
- :backtrace => data.backtrace,
21
- :message => data.message} if data.is_a? Exception
22
-
23
- (@tags + args).each do |tag|
24
- tag = tag.to_sym
25
- case tag
26
- when :timestamp
27
- opts.update :timestamp => Time.now.to_i
28
- when :hostname
29
- @hostname ||= { :hostname => `hostname`.strip }
30
- opts.update @hostname
31
- when :process
32
- @process_id ||= { :process_id => Process.pid,
33
- :process_name => $0,
34
- :process_parent_id => Process.ppid,
35
- :thread_id => Thread.current.object_id }
36
- opts.update :process => @process_id
37
- else
38
- (opts[:tags] ||= []) << tag
39
- end
40
- end
41
-
42
- opts.update(:severity => severity,
43
- :data => data)
44
-
45
- print(opts)
46
- MQ.queue('logger', :durable => true).publish(Marshal.dump(opts), :persistent => true)
47
- opts
48
- end
49
- alias :method_missing :log
50
-
51
- def print data = nil, &block
52
- if block
53
- @printer = block
54
- elsif data.is_a? Proc
55
- @printer = data
56
- elsif @printer and data
57
- @printer.call(data)
58
- else
59
- @printer
60
- end
61
- end
62
- alias :printer :print
63
- end
5
+ Logger = MQ::Logger
64
6
 
65
7
  EM.run{
66
- # AMQP.logging = true
67
- # MQ.logging = true
68
-
69
8
  if ARGV[0] == 'server'
70
9
 
71
- MQ.queue('logger', :durable => true).subscribe{|msg|
10
+ MQ.queue('logger').bind(MQ.fanout('logging', :durable => true)).subscribe{|msg|
72
11
  msg = Marshal.load(msg)
73
12
  require 'pp'
74
13
  pp(msg)
75
14
  puts
76
15
  }
77
-
78
- else
16
+
17
+ elsif ARGV[0] == 'client'
79
18
 
80
19
  log = Logger.new
81
20
  log.debug 'its working!'
@@ -100,6 +39,16 @@ EM.run{
100
39
 
101
40
  AMQP.stop{ EM.stop_event_loop }
102
41
 
42
+ else
43
+
44
+ puts
45
+ puts "#{$0} <client|server>"
46
+ puts " client: send logs to message queue"
47
+ puts " server: read logs from message queue"
48
+ puts
49
+
50
+ EM.stop
51
+
103
52
  end
104
53
  }
105
54
 
@@ -7,50 +7,52 @@ EM.run{
7
7
  p [ Time.now, *args ]
8
8
  end
9
9
 
10
- # AMQP.logging = true
11
-
12
- EM.add_periodic_timer(1){
13
- puts
14
-
15
- log :publishing, 'stock.usd.appl', price = 170+rand(1000)/100.0
16
- MQ.topic.publish(price, :key => 'stock.usd.appl', :headers => {:symbol => 'appl'})
17
-
18
- log :publishing, 'stock.usd.msft', price = 22+rand(500)/100.0
19
- MQ.topic.publish(price, :key => 'stock.usd.msft', :headers => {:symbol => 'msft'})
20
- }
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
21
25
 
22
- Thread.new{
23
- amq = MQ.new
24
- amq.queue('apple stock').bind(amq.topic, :key => 'stock.usd.appl').subscribe{ |price|
26
+ def watch_appl_stock
27
+ mq = MQ.new
28
+ mq.queue('apple stock').bind(mq.topic('stocks'), :key => 'usd.appl').subscribe{ |price|
25
29
  log 'apple stock', price
26
30
  }
27
- }
31
+ end
28
32
 
29
- Thread.new{
30
- amq = MQ.new
31
- amq.queue('us stocks').bind(amq.topic, :key => 'stock.usd.*').subscribe{ |info, price|
32
- log 'us stock', info.headers[:symbol], price
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
33
37
  }
34
- }
38
+ end
39
+
40
+ publish_stock_prices
41
+ watch_appl_stock
42
+ watch_us_stocks
35
43
 
36
44
  }
37
45
 
38
46
  __END__
39
47
 
40
- [Thu Jul 17 14:51:07 -0700 2008, :publishing, "stock.usd.appl", 170.84]
41
- [Thu Jul 17 14:51:07 -0700 2008, :publishing, "stock.usd.msft", 23.68]
42
- [Thu Jul 17 14:51:07 -0700 2008, "apple stock", "170.84"]
43
- [Thu Jul 17 14:51:07 -0700 2008, "us stock", "appl", "170.84"]
44
- [Thu Jul 17 14:51:07 -0700 2008, "us stock", "msft", "23.68"]
45
-
46
- [Thu Jul 17 14:51:08 -0700 2008, :publishing, "stock.usd.appl", 173.61]
47
- [Thu Jul 17 14:51:08 -0700 2008, :publishing, "stock.usd.msft", 25.8]
48
- [Thu Jul 17 14:51:08 -0700 2008, "apple stock", "173.61"]
49
- [Thu Jul 17 14:51:08 -0700 2008, "us stock", "appl", "173.61"]
50
- [Thu Jul 17 14:51:08 -0700 2008, "us stock", "msft", "25.8"]
51
-
52
- [Thu Jul 17 14:51:09 -0700 2008, :publishing, "stock.usd.appl", 173.94]
53
- [Thu Jul 17 14:51:09 -0700 2008, :publishing, "stock.usd.msft", 24.88]
54
- [Thu Jul 17 14:51:09 -0700 2008, "apple stock", "173.94"]
55
- [Thu Jul 17 14:51:09 -0700 2008, "us stock", "appl", "173.94"]
56
- [Thu Jul 17 14:51:09 -0700 2008, "us stock", "msft", "24.88"]
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"]
data/lib/amqp.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module AMQP
2
- VERSION = '0.5.5'
2
+ VERSION = '0.5.9'
3
3
 
4
4
  DIR = File.expand_path(File.dirname(File.expand_path(__FILE__)))
5
5
  $:.unshift DIR
@@ -14,6 +14,8 @@ module AMQP
14
14
  class << self
15
15
  @logging = false
16
16
  attr_accessor :logging
17
+ attr_reader :conn, :closing
18
+ alias :connection :conn
17
19
  end
18
20
 
19
21
  def self.connect *args
@@ -22,19 +24,39 @@ module AMQP
22
24
 
23
25
  def self.settings
24
26
  @settings ||= {
27
+ # server address
28
+ :host => '127.0.0.1',
29
+ :port => PORT,
30
+
31
+ # login details
25
32
  :user => 'guest',
26
33
  :pass => 'guest',
27
34
  :vhost => '/',
35
+
36
+ # connection timeout
37
+ :timeout => nil,
38
+
39
+ # logging
28
40
  :logging => false
29
41
  }
30
42
  end
31
43
 
32
- def self.start *args
33
- @conn ||= connect *args
44
+ def self.start *args, &blk
45
+ EM.run{
46
+ @conn ||= connect *args
47
+ @conn.callback(&blk) if blk
48
+ @conn
49
+ }
50
+ end
51
+
52
+ class << self
53
+ alias :run :start
34
54
  end
35
55
 
36
56
  def self.stop
37
57
  if @conn
58
+ puts "Shutting down..."
59
+ @closing = true
38
60
  @conn.close{
39
61
  yield if block_given?
40
62
  @conn = nil
data/lib/amqp/buffer.rb CHANGED
@@ -10,8 +10,8 @@ end
10
10
 
11
11
  module AMQP
12
12
  class Buffer
13
- class Overflow < Exception; end
14
- class InvalidType < Exception; end
13
+ class Overflow < StandardError; end
14
+ class InvalidType < StandardError; end
15
15
 
16
16
  def initialize data = ''
17
17
  @data = data
data/lib/amqp/client.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'amqp/frame'
2
2
 
3
3
  module AMQP
4
- class Error < Exception; end
4
+ class Error < StandardError; end
5
5
 
6
6
  module BasicClient
7
7
  def process_frame frame
@@ -30,7 +30,7 @@ module AMQP
30
30
 
31
31
  send Protocol::Connection::Open.new(:virtual_host => @settings[:vhost],
32
32
  :capabilities => '',
33
- :insist => false)
33
+ :insist => @settings[:insist])
34
34
 
35
35
  when Protocol::Connection::OpenOk
36
36
  succeed(self)
@@ -60,21 +60,34 @@ module AMQP
60
60
  def initialize opts = {}
61
61
  @settings = opts
62
62
  extend AMQP.client
63
+
64
+ @on_disconnect = proc{ raise Error, "Could not connect to server #{opts[:host]}:#{opts[:port]}" }
65
+
66
+ timeout @settings[:timeout] if @settings[:timeout]
67
+ errback{ @on_disconnect.call }
63
68
  end
64
69
 
65
70
  def connection_completed
66
71
  log 'connected'
72
+ @on_disconnect = proc{ raise Error, 'Disconnected from server' }
67
73
  @buf = Buffer.new
68
74
  send_data HEADER
69
75
  send_data [1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4')
70
76
  end
71
77
 
78
+ def unbind
79
+ log 'disconnected'
80
+ EM.next_tick{ @on_disconnect.call }
81
+ end
82
+
72
83
  def add_channel mq
73
- channels[ key = (channels.keys.max || 0) + 1 ] = mq
74
- key
84
+ (@_channel_mutex ||= Mutex.new).synchronize do
85
+ channels[ key = (channels.keys.max || 0) + 1 ] = mq
86
+ key
87
+ end
75
88
  end
76
89
 
77
- def channels mq = nil
90
+ def channels
78
91
  @channels ||= {}
79
92
  end
80
93
 
@@ -111,8 +124,8 @@ module AMQP
111
124
  @on_disconnect = on_disconnect if on_disconnect
112
125
 
113
126
  callback{ |c|
114
- if c.channels.keys.any?
115
- c.channels.each do |_, mq|
127
+ if c.channels.any?
128
+ c.channels.each do |ch, mq|
116
129
  mq.close
117
130
  end
118
131
  else
@@ -123,16 +136,9 @@ module AMQP
123
136
  end
124
137
  }
125
138
  end
126
-
127
- def unbind
128
- log 'disconnected'
129
- end
130
139
 
131
140
  def self.connect opts = {}
132
141
  opts = AMQP.settings.merge(opts)
133
- opts[:host] ||= '127.0.0.1'
134
- opts[:port] ||= PORT
135
-
136
142
  EM.connect opts[:host], opts[:port], self, opts
137
143
  end
138
144
 
data/lib/amqp/frame.rb CHANGED
@@ -33,7 +33,7 @@ module AMQP
33
33
  end
34
34
  end
35
35
 
36
- class Invalid < Exception; end
36
+ class Invalid < StandardError; end
37
37
 
38
38
  class Method
39
39
  def initialize payload = nil, channel = 0
data/lib/amqp/spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
- # this file was autogenerated on Sun Jul 20 04:06:05 -0700 2008
3
- # using amqp-0.8.json (mtime: Wed Jul 16 12:10:42 -0700 2008)
2
+ # this file was autogenerated on Fri Aug 01 15:08:30 -0700 2008
3
+ # using amqp-0.8.json (mtime: Fri Aug 01 15:08:22 -0700 2008)
4
4
  #
5
5
  # DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
6
6
 
@@ -340,6 +340,8 @@ module AMQP
340
340
  class PurgeOk < Method( 31, :purge_ok ); end
341
341
  class Delete < Method( 40, :delete ); end
342
342
  class DeleteOk < Method( 41, :delete_ok ); end
343
+ class Unbind < Method( 50, :unbind ); end
344
+ class UnbindOk < Method( 51, :unbind_ok ); end
343
345
 
344
346
  class Declare
345
347
  short :ticket
@@ -392,6 +394,17 @@ module AMQP
392
394
  long :message_count
393
395
  end
394
396
 
397
+ class Unbind
398
+ short :ticket
399
+ shortstr :queue
400
+ shortstr :exchange
401
+ shortstr :routing_key
402
+ table :arguments
403
+ end
404
+
405
+ class UnbindOk
406
+ end
407
+
395
408
  end
396
409
 
397
410
  class Basic
data/lib/ext/em.rb CHANGED
@@ -5,8 +5,7 @@ rescue LoadError
5
5
  require 'eventmachine'
6
6
  end
7
7
 
8
- # copied from EM trunk, will be removed when 0.12.1 is released
9
- if EM::VERSION < '0.12.1'
8
+ if EM::VERSION < '0.12.2'
10
9
 
11
10
  def EventMachine::run blk=nil, tail=nil, &block
12
11
  @tails ||= []
data/lib/mq.rb CHANGED
@@ -11,7 +11,7 @@ class MQ
11
11
  attr_accessor :logging
12
12
  end
13
13
 
14
- class Error < Exception; end
14
+ class Error < StandardError; end
15
15
  end
16
16
 
17
17
  class MQ
@@ -42,8 +42,8 @@ class MQ
42
42
  @body << frame.payload
43
43
  if @body.length >= @header.size
44
44
  @header.properties.update(@method.arguments)
45
- @consumer.receive @header, @body
46
- @body = ''
45
+ @consumer.receive @header, @body if @consumer
46
+ @body = @header = @consumer = @method = nil
47
47
  end
48
48
 
49
49
  when Frame::Method
@@ -52,7 +52,8 @@ class MQ
52
52
  send Protocol::Access::Request.new(:realm => '/data',
53
53
  :read => true,
54
54
  :write => true,
55
- :active => true)
55
+ :active => true,
56
+ :passive => true)
56
57
 
57
58
  when Protocol::Access::RequestOk
58
59
  @ticket = method.ticket
@@ -64,31 +65,55 @@ class MQ
64
65
  } if @closing
65
66
  succeed
66
67
 
67
- when Protocol::Basic::Deliver
68
+ when Protocol::Basic::CancelOk
69
+ if @consumer = consumers[ method.consumer_tag ]
70
+ @consumer.cancelled
71
+ else
72
+ MQ.error "Basic.CancelOk for invalid consumer tag: #{method.consumer_tag}"
73
+ end
74
+
75
+ when Protocol::Queue::DeclareOk
76
+ queues[ method.queue ].recieve_status method
77
+
78
+ when Protocol::Basic::Deliver, Protocol::Basic::GetOk
68
79
  @method = method
69
80
  @header = nil
70
81
  @body = ''
71
- @consumer = queues[ method.consumer_tag ]
72
82
 
83
+ if method.is_a? Protocol::Basic::GetOk
84
+ @consumer = get_queue{|q| q.shift }
85
+ MQ.error "No pending Basic.GetOk requests" unless @consumer
86
+ else
87
+ @consumer = consumers[ method.consumer_tag ]
88
+ MQ.error "Basic.Deliver for invalid consumer tag: #{method.consumer_tag}" unless @consumer
89
+ end
90
+
91
+ when Protocol::Basic::GetEmpty
92
+ @consumer = get_queue{|q| q.shift }
93
+ @consumer.receive nil, nil
73
94
 
74
95
  when Protocol::Channel::Close
75
- raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]}"
96
+ raise Error, "#{method.reply_text} in #{Protocol.classes[method.class_id].methods[method.method_id]} on #{@channel}"
76
97
 
77
98
  when Protocol::Channel::CloseOk
78
99
  @closing = false
79
100
  conn.callback{ |c|
80
- c.channels.delete(@channel)
81
- c.close unless c.channels.keys.any?
101
+ c.channels.delete @channel
102
+ c.close if c.channels.empty?
82
103
  }
83
104
  end
84
105
  end
85
106
  end
86
107
 
87
- def send data
88
- data.ticket = @ticket if @ticket and data.respond_to? :ticket
108
+ def send *args
89
109
  conn.callback{ |c|
90
- log :sending, data
91
- c.send data, :channel => @channel
110
+ (@_send_mutex ||= Mutex.new).synchronize do
111
+ args.each do |data|
112
+ data.ticket = @ticket if @ticket and data.respond_to? :ticket=
113
+ log :sending, data
114
+ c.send data, :channel => @channel
115
+ end
116
+ end
92
117
  }
93
118
  end
94
119
 
@@ -119,6 +144,16 @@ class MQ
119
144
  end
120
145
  end
121
146
 
147
+ # error callback
148
+
149
+ def self.error msg = nil, &blk
150
+ if blk
151
+ @error_callback = blk
152
+ else
153
+ @error_callback.call(msg) if @error_callback and msg
154
+ end
155
+ end
156
+
122
157
  # keep track of proxy objects
123
158
 
124
159
  def exchanges
@@ -129,10 +164,24 @@ class MQ
129
164
  @queues ||= {}
130
165
  end
131
166
 
167
+ def get_queue
168
+ if block_given?
169
+ (@get_queue_mutex ||= Mutex.new).synchronize{
170
+ yield( @get_queue ||= [] )
171
+ }
172
+ end
173
+ end
174
+
132
175
  def rpcs
133
176
  @rcps ||= {}
134
177
  end
135
178
 
179
+ # queue objects keyed on their consumer tags
180
+
181
+ def consumers
182
+ @consumers ||= {}
183
+ end
184
+
136
185
  private
137
186
 
138
187
  def log *args
@@ -145,10 +194,11 @@ class MQ
145
194
  alias :conn :connection
146
195
  end
147
196
 
148
- # convenience wrapper for thread-local MQ object
197
+ # convenience wrapper (read: HACK) for thread-local MQ object
149
198
 
150
199
  class MQ
151
200
  def MQ.default
201
+ # XXX clear this when connection is closed
152
202
  Thread.current[:mq] ||= MQ.new
153
203
  end
154
204
 
data/lib/mq/exchange.rb CHANGED
@@ -18,18 +18,31 @@ class MQ
18
18
 
19
19
  def publish data, opts = {}
20
20
  @mq.callback{
21
- @mq.send Protocol::Basic::Publish.new({ :exchange => name,
22
- :routing_key => opts.delete(:key) || @key }.merge(opts))
21
+ out = []
22
+
23
+ out << Protocol::Basic::Publish.new({ :exchange => name,
24
+ :routing_key => opts.delete(:key) || @key }.merge(opts))
23
25
 
24
26
  data = data.to_s
25
27
 
26
- @mq.send Protocol::Header.new(Protocol::Basic,
27
- data.length, { :content_type => 'application/octet-stream',
28
- :delivery_mode => (opts.delete(:persistent) ? 2 : 1),
29
- :priority => 0 }.merge(opts))
30
- @mq.send Frame::Body.new(data)
28
+ out << Protocol::Header.new(Protocol::Basic,
29
+ data.length, { :content_type => 'application/octet-stream',
30
+ :delivery_mode => (opts.delete(:persistent) ? 2 : 1),
31
+ :priority => 0 }.merge(opts))
32
+
33
+ out << Frame::Body.new(data)
34
+
35
+ @mq.send *out
31
36
  }
32
37
  self
33
38
  end
39
+
40
+ def delete opts = {}
41
+ @mq.callback{
42
+ @mq.send Protocol::Exchange::Delete.new({ :exchange => name,
43
+ :nowait => true }.merge(opts))
44
+ }
45
+ nil
46
+ end
34
47
  end
35
48
  end
data/lib/mq/logger.rb ADDED
@@ -0,0 +1,89 @@
1
+ class MQ
2
+ class Logger
3
+ def initialize *args, &block
4
+ opts = args.pop if args.last.is_a? Hash
5
+ opts ||= {}
6
+
7
+ printer(block) if block
8
+
9
+ @prop = opts
10
+ @tags = ([:timestamp] + args).uniq
11
+ end
12
+
13
+ attr_reader :prop
14
+ alias :base :prop
15
+
16
+ def log severity, *args
17
+ opts = args.pop if args.last.is_a? Hash and args.size != 1
18
+ opts ||= {}
19
+ opts = @prop.clone.update(opts)
20
+
21
+ data = args.shift
22
+
23
+ data = {:type => :exception,
24
+ :name => data.class.to_s.intern,
25
+ :backtrace => data.backtrace,
26
+ :message => data.message} if data.is_a? Exception
27
+
28
+ (@tags + args).each do |tag|
29
+ tag = tag.to_sym
30
+ case tag
31
+ when :timestamp
32
+ opts.update :timestamp => Time.now
33
+ when :hostname
34
+ @hostname ||= { :hostname => `hostname`.strip }
35
+ opts.update @hostname
36
+ when :process
37
+ @process_id ||= { :process_id => Process.pid,
38
+ :process_name => $0,
39
+ :process_parent_id => Process.ppid,
40
+ :thread_id => Thread.current.object_id }
41
+ opts.update :process => @process_id
42
+ else
43
+ (opts[:tags] ||= []) << tag
44
+ end
45
+ end
46
+
47
+ opts.update(:severity => severity,
48
+ :msg => data)
49
+
50
+ print(opts)
51
+ unless Logger.disabled?
52
+ MQ.fanout('logging', :durable => true).publish Marshal.dump(opts)
53
+ end
54
+
55
+ opts
56
+ end
57
+ alias :method_missing :log
58
+
59
+ def print data = nil, &block
60
+ if block
61
+ @printer = block
62
+ elsif data.is_a? Proc
63
+ @printer = data
64
+ elsif data
65
+ (pr = @printer || self.class.printer) and pr.call(data)
66
+ else
67
+ @printer
68
+ end
69
+ end
70
+ alias :printer :print
71
+
72
+ def self.printer &block
73
+ @printer = block if block
74
+ @printer
75
+ end
76
+
77
+ def self.disabled?
78
+ !!@disabled
79
+ end
80
+
81
+ def self.enable
82
+ @disabled = false
83
+ end
84
+
85
+ def self.disable
86
+ @disabled = true
87
+ end
88
+ end
89
+ end
data/lib/mq/queue.rb CHANGED
@@ -21,26 +21,120 @@ class MQ
21
21
  }
22
22
  self
23
23
  end
24
-
24
+
25
+ def unbind exchange, opts = {}
26
+ @mq.callback{
27
+ @mq.send Protocol::Queue::Unbind.new({ :queue => name,
28
+ :exchange => exchange.respond_to?(:name) ? exchange.name : exchange,
29
+ :routing_key => opts.delete(:key),
30
+ :nowait => true }.merge(opts))
31
+ }
32
+ self
33
+ end
34
+
35
+ def delete opts = {}
36
+ @mq.callback{
37
+ @mq.send Protocol::Queue::Delete.new({ :queue => name,
38
+ :nowait => true }.merge(opts))
39
+ }
40
+ @mq.queues.delete @name
41
+ nil
42
+ end
43
+
44
+ def pop opts = {}, &blk
45
+ @ack = opts[:no_ack] === false
46
+
47
+ @on_pop = blk if blk
48
+
49
+ @mq.callback{
50
+ @mq.send Protocol::Basic::Get.new({ :queue => name,
51
+ :consumer_tag => name,
52
+ :no_ack => true,
53
+ :nowait => true }.merge(opts))
54
+ @mq.get_queue{ |q|
55
+ q.push(self)
56
+ }
57
+ }
58
+
59
+ self
60
+ end
61
+
25
62
  def subscribe opts = {}, &blk
63
+ @consumer_tag = "#{name}-#{Kernel.rand(999_999_999_999)}"
64
+ @mq.consumers[@consumer_tag] = self
65
+
66
+ raise Error, 'already subscribed to the queue' if @on_msg
67
+
26
68
  @on_msg = blk
69
+ @ack = opts[:no_ack] === false
70
+
27
71
  @mq.callback{
28
72
  @mq.send Protocol::Basic::Consume.new({ :queue => name,
29
- :consumer_tag => name,
73
+ :consumer_tag => @consumer_tag,
30
74
  :no_ack => true,
31
75
  :nowait => true }.merge(opts))
32
76
  }
33
77
  self
34
78
  end
35
79
 
80
+ def unsubscribe opts = {}, &blk
81
+ @on_msg = nil
82
+ @on_cancel = blk
83
+ @mq.callback{
84
+ @mq.send Protocol::Basic::Cancel.new({ :consumer_tag => @consumer_tag }.merge(opts))
85
+ }
86
+ self
87
+ end
88
+
36
89
  def publish data, opts = {}
37
90
  exchange.publish(data, opts)
38
91
  end
39
92
 
40
93
  def receive headers, body
41
- if @on_msg
42
- @on_msg.call *(@on_msg.arity == 1 ? [body] : [headers, body])
94
+ if AMQP.closing
95
+ #You don't need this if your using ack, and if you aren't it doesn't do much good either
96
+ #@mq.callback{
97
+ # @mq.send Protocol::Basic::Reject.new({
98
+ # :delivery_tag => headers.properties[:delivery_tag],
99
+ # :requeue => true
100
+ # })
101
+ #}
102
+ return
43
103
  end
104
+
105
+ if cb = (@on_msg || @on_pop)
106
+ cb.call *(cb.arity == 1 ? [body] : [headers, body])
107
+ end
108
+
109
+ if @ack && headers && !AMQP.closing
110
+ @mq.callback{
111
+ @mq.send Protocol::Basic::Ack.new({ :delivery_tag => headers.properties[:delivery_tag] })
112
+ }
113
+ end
114
+ end
115
+
116
+ def status opts = {}, &blk
117
+ @on_status = blk
118
+ @mq.callback{
119
+ @mq.send Protocol::Queue::Declare.new({ :queue => name,
120
+ :passive => true }.merge(opts))
121
+ }
122
+ self
123
+ end
124
+
125
+ def recieve_status declare_ok
126
+ if @on_status
127
+ m, c = declare_ok.message_count, declare_ok.consumer_count
128
+ @on_status.call *(@on_status.arity == 1 ? [m] : [m, c])
129
+ @on_status = nil
130
+ end
131
+ end
132
+
133
+ def cancelled
134
+ @on_cancel.call if @on_cancel
135
+ @on_cancel = @on_msg = nil
136
+ @mq.consumers.delete @consumer_tag
137
+ @consumer_tag = nil
44
138
  end
45
139
 
46
140
  private
@@ -275,7 +275,18 @@
275
275
  "name": "delete"},
276
276
  {"id": 41,
277
277
  "arguments": [{"type": "long", "name": "message-count"}],
278
- "name": "delete-ok"}],
278
+ "name": "delete-ok"},
279
+ {"id": 50,
280
+ "arguments": [{"type": "short", "name": "ticket"},
281
+ {"type": "shortstr", "name": "queue"},
282
+ {"type": "shortstr", "name": "exchange"},
283
+ {"type": "shortstr", "name": "routing-key"},
284
+ {"type": "table", "name": "arguments"}],
285
+ "name": "unbind"},
286
+ {"id": 51,
287
+ "arguments": [],
288
+ "name": "unbind-ok"}
289
+ ],
279
290
  "name": "queue"
280
291
  },
281
292
  {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmm1-amqp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-01 00:00:00 -07:00
12
+ date: 2008-09-01 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -19,7 +19,7 @@ dependencies:
19
19
  requirements:
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.12.0
22
+ version: 0.12.2
23
23
  version:
24
24
  description: AMQP client implementation in Ruby/EventMachine
25
25
  email: amqp@tmm1.net
@@ -50,6 +50,7 @@ files:
50
50
  - lib/ext/em.rb
51
51
  - lib/ext/emfork.rb
52
52
  - lib/mq/exchange.rb
53
+ - lib/mq/logger.rb
53
54
  - lib/mq/queue.rb
54
55
  - lib/mq/rpc.rb
55
56
  - lib/mq.rb