tmm1-amqp 0.5.5 → 0.5.9

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