totty-amqp 0.6.7.1.totty

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.
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,3 @@
1
+ module AMQP
2
+ VERSION = '0.6.7.1.totty'
3
+ end
data/lib/amqp.rb ADDED
@@ -0,0 +1,152 @@
1
+ require File.expand_path('../ext/em', __FILE__)
2
+ require File.expand_path('../ext/blankslate', __FILE__)
3
+
4
+ %w[ version buffer spec protocol frame client ].each do |file|
5
+ require File.expand_path("../amqp/#{file}", __FILE__)
6
+ end
7
+
8
+ module AMQP
9
+ class << self
10
+ @logging = false
11
+ attr_accessor :logging
12
+ attr_reader :conn, :closing
13
+ alias :closing? :closing
14
+ alias :connection :conn
15
+ end
16
+
17
+ def self.connect *args
18
+ Client.connect *args
19
+ end
20
+
21
+ def self.settings
22
+ @settings ||= {
23
+ # server address
24
+ :host => '127.0.0.1',
25
+ :port => PORT,
26
+
27
+ # login details
28
+ :user => 'guest',
29
+ :pass => 'guest',
30
+ :vhost => '/',
31
+
32
+ # connection timeout
33
+ :timeout => nil,
34
+
35
+ # logging
36
+ :logging => false,
37
+
38
+ # ssl
39
+ :ssl => false
40
+ }
41
+ end
42
+
43
+ # Must be called to startup the connection to the AMQP server.
44
+ #
45
+ # The method takes several arguments and an optional block.
46
+ #
47
+ # This takes any option that is also accepted by EventMachine::connect.
48
+ # Additionally, there are several AMQP-specific options.
49
+ #
50
+ # * :user => String (default 'guest')
51
+ # The username as defined by the AMQP server.
52
+ # * :pass => String (default 'guest')
53
+ # The password for the associated :user as defined by the AMQP server.
54
+ # * :vhost => String (default '/')
55
+ # The virtual host as defined by the AMQP server.
56
+ # * :timeout => Numeric (default nil)
57
+ # Measured in seconds.
58
+ # * :logging => true | false (default false)
59
+ # Toggle the extremely verbose logging of all protocol communications
60
+ # between the client and the server. Extremely useful for debugging.
61
+ #
62
+ # AMQP.start do
63
+ # # default is to connect to localhost:5672
64
+ #
65
+ # # define queues, exchanges and bindings here.
66
+ # # also define all subscriptions and/or publishers
67
+ # # here.
68
+ #
69
+ # # this block never exits unless EM.stop_event_loop
70
+ # # is called.
71
+ # end
72
+ #
73
+ # Most code will use the MQ api. Any calls to MQ.direct / MQ.fanout /
74
+ # MQ.topic / MQ.queue will implicitly call #start. In those cases,
75
+ # it is sufficient to put your code inside of an EventMachine.run
76
+ # block. See the code examples in MQ for details.
77
+ #
78
+ def self.start *args, &blk
79
+ EM.run {
80
+ @conn ||= connect *args
81
+ @conn.callback(&blk) if blk
82
+ @conn
83
+ }
84
+ end
85
+
86
+ def self.vstart *args, &blk
87
+ opts = Hash[*args.collect]
88
+ opts[:hosts].shuffle.each do |host_port|
89
+ host, port = host_port.split ":"
90
+ opts[:host] = host
91
+ opts[:port] = port if port
92
+
93
+ begin
94
+ puts "AMQP.run : Try connect to server #{host_port}"
95
+ #start opts, &blk
96
+
97
+ #EM.error_handler { |e|
98
+ # puts "Error raised during event loop: #{e.message}"
99
+ # EM.stop_event_loop
100
+ #}
101
+ EM.run {
102
+ _start opts, &blk
103
+ }
104
+ rescue => e
105
+ puts "AMQP.err : #{e}"
106
+ @conn = nil
107
+ else
108
+ break
109
+ end
110
+ end
111
+ @conn
112
+ end
113
+
114
+ class << self
115
+ alias :run :start
116
+ end
117
+
118
+ def self.stop
119
+ if @conn and not @closing
120
+ @closing = true
121
+ @conn.close{
122
+ yield if block_given?
123
+ @conn = nil
124
+ @closing = false
125
+ }
126
+ end
127
+ end
128
+
129
+ def self.fork workers
130
+ EM.fork(workers) do
131
+ # clean up globals in the fork
132
+ Thread.current[:mq] = nil
133
+ AMQP.instance_variable_set('@conn', nil)
134
+
135
+ yield
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def self._start *args, &blk
142
+ @conn ||= connect *args
143
+ @conn.callback(&blk) if blk
144
+ @conn
145
+ end
146
+
147
+ def self.log *args
148
+ return unless MQ.logging
149
+ pp args
150
+ puts
151
+ end
152
+ end
@@ -0,0 +1,7 @@
1
+ unless defined?(BlankSlate)
2
+ class BlankSlate < BasicObject; end if defined?(BasicObject)
3
+
4
+ class BlankSlate #:nodoc:
5
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ }
6
+ end
7
+ end
data/lib/ext/em.rb ADDED
@@ -0,0 +1,8 @@
1
+ begin
2
+ require 'eventmachine'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'eventmachine'
6
+ end
7
+
8
+ require 'ext/emfork'
data/lib/ext/emfork.rb ADDED
@@ -0,0 +1,69 @@
1
+ EMFORK = $0 == __FILE__
2
+
3
+ if EMFORK
4
+ require 'rubygems'
5
+ end
6
+
7
+ require 'eventmachine'
8
+
9
+ #:stopdoc:
10
+
11
+ # helper to fork off EM reactors
12
+ def EM.fork num = 1, &blk
13
+ unless @forks
14
+ trap('CHLD'){
15
+ pid = Process.wait
16
+ p [:pid, pid, :died] if EMFORK
17
+ block = @forks.delete(pid)
18
+ EM.fork(1, &block)
19
+ }
20
+
21
+ trap('EXIT'){
22
+ p [:pid, Process.pid, :exit] if EMFORK
23
+ @forks.keys.each{ |pid|
24
+ p [:pid, Process.pid, :killing, pid] if EMFORK
25
+ Process.kill('USR1', pid)
26
+ }
27
+ }
28
+
29
+ @forks = {}
30
+ end
31
+
32
+ num.times do
33
+ pid = EM.fork_reactor do
34
+ p [:pid, Process.pid, :started] if EMFORK
35
+
36
+ trap('USR1'){ EM.stop_event_loop }
37
+ trap('CHLD'){}
38
+ trap('EXIT'){}
39
+
40
+ blk.call
41
+ end
42
+
43
+ @forks[pid] = blk
44
+ p [:children, EM.forks] if EMFORK
45
+ end
46
+ end
47
+
48
+ def EM.forks
49
+ @forks ? @forks.keys : []
50
+ end
51
+
52
+ if EMFORK
53
+ p 'starting reactor'
54
+
55
+ trap('INT'){ EM.stop_event_loop }
56
+
57
+ EM.run{
58
+ p [:parent, Process.pid]
59
+
60
+ EM.fork(2){
61
+ EM.add_periodic_timer(1) do
62
+ p [:fork, Process.pid, :ping]
63
+ end
64
+ }
65
+
66
+ }
67
+
68
+ p 'reactor stopped'
69
+ end
@@ -0,0 +1,314 @@
1
+ class MQ
2
+ # An Exchange acts as an ingress point for all published messages. An
3
+ # exchange may also be described as a router or a matcher. Every
4
+ # published message is received by an exchange which, depending on its
5
+ # type (described below), determines how to deliver the message.
6
+ #
7
+ # It determines the next delivery hop by examining the bindings associated
8
+ # with the exchange.
9
+ #
10
+ # There are three (3) supported Exchange types: direct, fanout and topic.
11
+ #
12
+ # As part of the standard, the server _must_ predeclare the direct exchange
13
+ # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
14
+ # starting with 'amq.' are reserved). Attempts to declare an exchange using
15
+ # 'amq.' as the name will raise an MQ:Error and fail. In practice these
16
+ # default exchanges are never used directly by client code.
17
+ #
18
+ # These predececlared exchanges are used when the client code declares
19
+ # an exchange without a name. In these cases the library will use
20
+ # the default exchange for publishing the messages.
21
+ #
22
+ class Exchange
23
+ include AMQP
24
+
25
+ # Defines, intializes and returns an Exchange to act as an ingress
26
+ # point for all published messages.
27
+ #
28
+ # There are three (3) supported Exchange types: direct, fanout and topic.
29
+ #
30
+ # As part of the standard, the server _must_ predeclare the direct exchange
31
+ # 'amq.direct' and the fanout exchange 'amq.fanout' (all exchange names
32
+ # starting with 'amq.' are reserved). Attempts to declare an exchange using
33
+ # 'amq.' as the name will raise an MQ:Error and fail. In practice these
34
+ # default exchanges are never used directly by client code.
35
+ #
36
+ # == Direct
37
+ # A direct exchange is useful for 1:1 communication between a publisher and
38
+ # subscriber. Messages are routed to the queue with a binding that shares
39
+ # the same name as the exchange. Alternately, the messages are routed to
40
+ # the bound queue that shares the same name as the routing key used for
41
+ # defining the exchange. This exchange type does not honor the :key option
42
+ # when defining a new instance with a name. It _will_ honor the :key option
43
+ # if the exchange name is the empty string. This is because an exchange
44
+ # defined with the empty string uses the default pre-declared exchange
45
+ # called 'amq.direct'. In this case it needs to use :key to do its matching.
46
+ #
47
+ # # exchange is named 'foo'
48
+ # exchange = MQ::Exchange.new(MQ.new, :direct, 'foo')
49
+ #
50
+ # # or, the exchange can use the default name (amq.direct) and perform
51
+ # # routing comparisons using the :key
52
+ # exchange = MQ::Exchange.new(MQ.new, :direct, "", :key => 'foo')
53
+ # exchange.publish('some data') # will be delivered to queue bound to 'foo'
54
+ #
55
+ # queue = MQ::Queue.new(MQ.new, 'foo')
56
+ # # can receive data since the queue name and the exchange key match exactly
57
+ # queue.pop { |data| puts "received data [#{data}]" }
58
+ #
59
+ # == Fanout
60
+ # A fanout exchange is useful for 1:N communication where one publisher
61
+ # feeds multiple subscribers. Like direct exchanges, messages published
62
+ # to a fanout exchange are delivered to queues whose name matches the
63
+ # exchange name (or are bound to that exchange name). Each queue gets
64
+ # its own copy of the message.
65
+ #
66
+ # Like the direct exchange type, this exchange type does not honor the
67
+ # :key option when defining a new instance with a name. It _will_ honor
68
+ # the :key option if the exchange name is the empty string. Fanout exchanges
69
+ # defined with the empty string as the name use the default 'amq.fanout'.
70
+ # In this case it needs to use :key to do its matching.
71
+ #
72
+ # EM.run do
73
+ # clock = MQ::Exchange.new(MQ.new, :fanout, 'clock')
74
+ # EM.add_periodic_timer(1) do
75
+ # puts "\npublishing #{time = Time.now}"
76
+ # clock.publish(Marshal.dump(time))
77
+ # end
78
+ #
79
+ # # one way of defining a queue
80
+ # amq = MQ::Queue.new(MQ.new, 'every second')
81
+ # amq.bind(MQ.fanout('clock')).subscribe do |time|
82
+ # puts "every second received #{Marshal.load(time)}"
83
+ # end
84
+ #
85
+ # # defining a queue using the convenience method
86
+ # # note the string passed to #bind
87
+ # MQ.queue('every 5 seconds').bind('clock').subscribe do |time|
88
+ # time = Marshal.load(time)
89
+ # puts "every 5 seconds received #{time}" if time.strftime('%S').to_i%5 == 0
90
+ # end
91
+ # end
92
+ #
93
+ # == Topic
94
+ # A topic exchange allows for messages to be published to an exchange
95
+ # tagged with a specific routing key. The Exchange uses the routing key
96
+ # to determine which queues to deliver the message. Wildcard matching
97
+ # is allowed. The topic must be declared using dot notation to separate
98
+ # each subtopic.
99
+ #
100
+ # This is the only exchange type to honor the :key parameter.
101
+ #
102
+ # As part of the AMQP standard, each server _should_ predeclare a topic
103
+ # exchange called 'amq.topic' (this is not required by the standard).
104
+ #
105
+ # The classic example is delivering market data. When publishing market
106
+ # data for stocks, we may subdivide the stream based on 2
107
+ # characteristics: nation code and trading symbol. The topic tree for
108
+ # Apple Computer would look like:
109
+ # 'stock.us.aapl'
110
+ # For a foreign stock, it may look like:
111
+ # 'stock.de.dax'
112
+ #
113
+ # When publishing data to the exchange, bound queues subscribing to the
114
+ # exchange indicate which data interests them by passing a routing key
115
+ # for matching against the published routing key.
116
+ #
117
+ # EM.run do
118
+ # exch = MQ::Exchange.new(MQ.new, :topic, "stocks")
119
+ # keys = ['stock.us.aapl', 'stock.de.dax']
120
+ #
121
+ # EM.add_periodic_timer(1) do # every second
122
+ # puts
123
+ # exch.publish(10+rand(10), :routing_key => keys[rand(2)])
124
+ # end
125
+ #
126
+ # # match against one dot-separated item
127
+ # MQ.queue('us stocks').bind(exch, :key => 'stock.us.*').subscribe do |price|
128
+ # puts "us stock price [#{price}]"
129
+ # end
130
+ #
131
+ # # match against multiple dot-separated items
132
+ # MQ.queue('all stocks').bind(exch, :key => 'stock.#').subscribe do |price|
133
+ # puts "all stocks: price [#{price}]"
134
+ # end
135
+ #
136
+ # # require exact match
137
+ # MQ.queue('only dax').bind(exch, :key => 'stock.de.dax').subscribe do |price|
138
+ # puts "dax price [#{price}]"
139
+ # end
140
+ # end
141
+ #
142
+ # For matching, the '*' (asterisk) wildcard matches against one
143
+ # dot-separated item only. The '#' wildcard (hash or pound symbol)
144
+ # matches against 0 or more dot-separated items. If none of these
145
+ # symbols are used, the exchange performs a comparison looking for an
146
+ # exact match.
147
+ #
148
+ # == Options
149
+ # * :passive => true | false (default false)
150
+ # If set, the server will not create the exchange if it does not
151
+ # already exist. The client can use this to check whether an exchange
152
+ # exists without modifying the server state.
153
+ #
154
+ # * :durable => true | false (default false)
155
+ # If set when creating a new exchange, the exchange will be marked as
156
+ # durable. Durable exchanges remain active when a server restarts.
157
+ # Non-durable exchanges (transient exchanges) are purged if/when a
158
+ # server restarts.
159
+ #
160
+ # A transient exchange (the default) is stored in memory-only
161
+ # therefore it is a good choice for high-performance and low-latency
162
+ # message publishing.
163
+ #
164
+ # Durable exchanges cause all messages to be written to non-volatile
165
+ # backing store (i.e. disk) prior to routing to any bound queues.
166
+ #
167
+ # * :auto_delete => true | false (default false)
168
+ # If set, the exchange is deleted when all queues have finished
169
+ # using it. The server waits for a short period of time before
170
+ # determining the exchange is unused to give time to the client code
171
+ # to bind a queue to it.
172
+ #
173
+ # If the exchange has been previously declared, this option is ignored
174
+ # on subsequent declarations.
175
+ #
176
+ # * :internal => true | false (default false)
177
+ # If set, the exchange may not be used directly by publishers, but
178
+ # only when bound to other exchanges. Internal exchanges are used to
179
+ # construct wiring that is not visible to applications.
180
+ #
181
+ # * :nowait => true | false (default true)
182
+ # If set, the server will not respond to the method. The client should
183
+ # not wait for a reply method. If the server could not complete the
184
+ # method it will raise a channel or connection exception.
185
+ #
186
+ # * :no_declare => true | false (default false)
187
+ # If set, the exchange will not be declared to the
188
+ # AMQP broker at instantiation-time. This allows the AMQP
189
+ # client to send messages to exchanges that were
190
+ # already declared by someone else, e.g. if the client
191
+ # does not have sufficient privilege to declare (create)
192
+ # an exchange. Use with caution, as binding to an exchange
193
+ # with the no-declare option causes your system to become
194
+ # sensitive to the ordering of clients' actions!
195
+ #
196
+ # == Exceptions
197
+ # Doing any of these activities are illegal and will raise MQ:Error.
198
+ # * redeclare an already-declared exchange to a different type
199
+ # * :passive => true and the exchange does not exist (NOT_FOUND)
200
+ #
201
+ def initialize mq, type, name, opts = {}
202
+ @mq = mq
203
+ @type, @name, @opts = type, name, opts
204
+ @mq.exchanges[@name = name] ||= self
205
+ @key = opts[:key]
206
+
207
+ unless name == "amq.#{type}" or name == '' or opts[:no_declare]
208
+ @mq.callback{
209
+ @mq.send Protocol::Exchange::Declare.new({ :exchange => name,
210
+ :type => type,
211
+ :nowait => true }.merge(opts))
212
+ }
213
+ end
214
+ end
215
+ attr_reader :name, :type, :key
216
+
217
+ # This method publishes a staged file message to a specific exchange.
218
+ # The file message will be routed to queues as defined by the exchange
219
+ # configuration and distributed to any active consumers when the
220
+ # transaction, if any, is committed.
221
+ #
222
+ # exchange = MQ.direct('name', :key => 'foo.bar')
223
+ # exchange.publish("some data")
224
+ #
225
+ # The method takes several hash key options which modify the behavior or
226
+ # lifecycle of the message.
227
+ #
228
+ # * :routing_key => 'string'
229
+ #
230
+ # Specifies the routing key for the message. The routing key is
231
+ # used for routing messages depending on the exchange configuration.
232
+ #
233
+ # * :mandatory => true | false (default false)
234
+ #
235
+ # This flag tells the server how to react if the message cannot be
236
+ # routed to a queue. If this flag is set, the server will return an
237
+ # unroutable message with a Return method. If this flag is zero, the
238
+ # server silently drops the message.
239
+ #
240
+ # * :immediate => true | false (default false)
241
+ #
242
+ # This flag tells the server how to react if the message cannot be
243
+ # routed to a queue consumer immediately. If this flag is set, the
244
+ # server will return an undeliverable message with a Return method.
245
+ # If this flag is zero, the server will queue the message, but with
246
+ # no guarantee that it will ever be consumed.
247
+ #
248
+ # * :persistent
249
+ # True or False. When true, this message will remain in the queue until
250
+ # it is consumed (if the queue is durable). When false, the message is
251
+ # lost if the server restarts and the queue is recreated.
252
+ #
253
+ # For high-performance and low-latency, set :persistent => false so the
254
+ # message stays in memory and is never persisted to non-volatile (slow)
255
+ # storage.
256
+ #
257
+ def publish data, opts = {}
258
+ @mq.callback{
259
+ out = []
260
+
261
+ out << Protocol::Basic::Publish.new({ :exchange => name,
262
+ :routing_key => opts[:key] || @key }.merge(opts))
263
+
264
+ data = data.to_s
265
+
266
+ out << Protocol::Header.new(Protocol::Basic,
267
+ data.length, { :content_type => 'application/octet-stream',
268
+ :delivery_mode => (opts[:persistent] ? 2 : 1),
269
+ :priority => 0 }.merge(opts))
270
+
271
+ out << Frame::Body.new(data)
272
+
273
+ @mq.send *out
274
+ }
275
+ self
276
+ end
277
+
278
+ # This method deletes an exchange. When an exchange is deleted all queue
279
+ # bindings on the exchange are cancelled.
280
+ #
281
+ # Further attempts to publish messages to a deleted exchange will raise
282
+ # an MQ::Error due to a channel close exception.
283
+ #
284
+ # exchange = MQ.direct('name', :key => 'foo.bar')
285
+ # exchange.delete
286
+ #
287
+ # == Options
288
+ # * :nowait => true | false (default true)
289
+ # If set, the server will not respond to the method. The client should
290
+ # not wait for a reply method. If the server could not complete the
291
+ # method it will raise a channel or connection exception.
292
+ #
293
+ # exchange.delete(:nowait => false)
294
+ #
295
+ # * :if_unused => true | false (default false)
296
+ # If set, the server will only delete the exchange if it has no queue
297
+ # bindings. If the exchange has queue bindings the server does not
298
+ # delete it but raises a channel exception instead (MQ:Error).
299
+ #
300
+ def delete opts = {}
301
+ @mq.callback{
302
+ @mq.send Protocol::Exchange::Delete.new({ :exchange => name,
303
+ :nowait => true }.merge(opts))
304
+ @mq.exchanges.delete name
305
+ }
306
+ nil
307
+ end
308
+
309
+ def reset
310
+ @deferred_status = nil
311
+ initialize @mq, @type, @name, @opts
312
+ end
313
+ end
314
+ end
data/lib/mq/header.rb ADDED
@@ -0,0 +1,33 @@
1
+ class MQ
2
+ class Header
3
+ include AMQP
4
+
5
+ def initialize(mq, header_obj)
6
+ @mq = mq
7
+ @header = header_obj
8
+ end
9
+
10
+ # Acknowledges the receipt of this message with the server.
11
+ def ack
12
+ @mq.callback{
13
+ @mq.send Protocol::Basic::Ack.new(:delivery_tag => properties[:delivery_tag])
14
+ }
15
+ end
16
+
17
+ # Reject this message (XXX currently unimplemented in rabbitmq)
18
+ # * :requeue => true | false (default false)
19
+ def reject opts = {}
20
+ @mq.callback{
21
+ @mq.send Protocol::Basic::Reject.new(opts.merge(:delivery_tag => properties[:delivery_tag]))
22
+ }
23
+ end
24
+
25
+ def method_missing meth, *args, &blk
26
+ @header.send meth, *args, &blk
27
+ end
28
+
29
+ def inspect
30
+ @header.inspect
31
+ end
32
+ end
33
+ 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