famoseagle-carrot 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +17 -17
- data/VERSION.yml +1 -1
- data/lib/amqp/buffer.rb +8 -2
- data/lib/amqp/exchange.rb +4 -2
- data/lib/amqp/frame.rb +2 -11
- data/lib/amqp/header.rb +1 -1
- data/lib/amqp/protocol.rb +1 -1
- data/lib/amqp/queue.rb +31 -10
- data/lib/amqp/server.rb +48 -87
- data/lib/amqp/spec.rb +2 -14
- data/lib/carrot.rb +40 -17
- metadata +2 -2
data/README.markdown
CHANGED
@@ -2,23 +2,23 @@
|
|
2
2
|
|
3
3
|
A synchronous amqp client. Based on Aman's amqp client:
|
4
4
|
|
5
|
-
http://github.com/tmm1/amqp/tree/master
|
6
|
-
|
7
|
-
## Example
|
8
|
-
|
9
|
-
q = Carrot.queue('name', :durable => true, :host => 'q1.rabbitmq.com')
|
10
|
-
100.times do
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
pp :count, q.message_count
|
15
|
-
|
16
|
-
while msg = q.pop(:ack => true)
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
Carrot.stop
|
21
|
-
|
5
|
+
[http://github.com/tmm1/amqp/tree/master] (http://github.com/tmm1/amqp/tree/master)
|
6
|
+
|
7
|
+
## Example
|
8
|
+
|
9
|
+
q = Carrot.queue('name', :durable => true, :host => 'q1.rabbitmq.com')
|
10
|
+
100.times do
|
11
|
+
q.publish('foo')
|
12
|
+
end
|
13
|
+
|
14
|
+
pp :count, q.message_count
|
15
|
+
|
16
|
+
while msg = q.pop(:ack => true)
|
17
|
+
puts msg
|
18
|
+
q.ack
|
19
|
+
end
|
20
|
+
Carrot.stop
|
21
|
+
|
22
22
|
# LICENSE
|
23
23
|
|
24
24
|
Copyright (c) 2009 Amos Elliston, Geni.com; Published under The MIT License, see License
|
data/VERSION.yml
CHANGED
data/lib/amqp/buffer.rb
CHANGED
@@ -8,7 +8,7 @@ else
|
|
8
8
|
require 'enumerator'
|
9
9
|
end
|
10
10
|
|
11
|
-
module AMQP
|
11
|
+
module Carrot::AMQP
|
12
12
|
class Buffer #:nodoc: all
|
13
13
|
class Overflow < StandardError; end
|
14
14
|
class InvalidType < StandardError; end
|
@@ -247,7 +247,13 @@ module AMQP
|
|
247
247
|
end
|
248
248
|
end
|
249
249
|
|
250
|
-
def _read
|
250
|
+
def _read(size, pack = nil)
|
251
|
+
if @data.is_a?(Server)
|
252
|
+
raw = @data.read(size)
|
253
|
+
return raw if raw.nil? or pack.nil?
|
254
|
+
return raw.unpack(pack).first
|
255
|
+
end
|
256
|
+
|
251
257
|
if @pos + size > length
|
252
258
|
raise Overflow
|
253
259
|
else
|
data/lib/amqp/exchange.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module AMQP
|
1
|
+
module Carrot::AMQP
|
2
2
|
class Exchange
|
3
3
|
attr_accessor :server, :type, :name, :opts, :key
|
4
4
|
|
@@ -37,7 +37,9 @@ module AMQP
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def delete(opts = {})
|
40
|
-
server.send_frame(
|
40
|
+
server.send_frame(
|
41
|
+
Protocol::Exchange::Delete.new({ :exchange => name, :nowait => true }.merge(opts))
|
42
|
+
)
|
41
43
|
end
|
42
44
|
|
43
45
|
def reset
|
data/lib/amqp/frame.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module AMQP
|
1
|
+
module Carrot::AMQP
|
2
2
|
class Frame #:nodoc: all
|
3
3
|
def initialize payload = nil, channel = 0
|
4
4
|
@channel, @payload = channel, payload
|
@@ -51,22 +51,13 @@ module AMQP
|
|
51
51
|
|
52
52
|
class Body; end
|
53
53
|
|
54
|
-
def self.parse
|
54
|
+
def self.parse(buf)
|
55
55
|
buf = Buffer.new(buf) unless buf.is_a? Buffer
|
56
56
|
buf.extract do
|
57
57
|
id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
|
58
58
|
Frame.types[id].new(payload, channel) if footer == FOOTER
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
62
|
-
def self.get(server)
|
63
|
-
id = server.read(1).unpack('C').first
|
64
|
-
channel = server.read(2).unpack('n').first
|
65
|
-
size = server.read(4).unpack('N').first
|
66
|
-
data = server.read(size)
|
67
|
-
footer = server.read(1).unpack('C').first
|
68
|
-
Frame.types[id].new(data, channel) if footer == FOOTER
|
69
|
-
end
|
70
61
|
end
|
71
62
|
end
|
72
63
|
|
data/lib/amqp/header.rb
CHANGED
data/lib/amqp/protocol.rb
CHANGED
data/lib/amqp/queue.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
module AMQP
|
1
|
+
module Carrot::AMQP
|
2
2
|
class Queue
|
3
3
|
attr_reader :name, :server
|
4
4
|
attr_accessor :delivery_tag
|
@@ -10,14 +10,6 @@ module AMQP
|
|
10
10
|
server.send_frame(
|
11
11
|
Protocol::Queue::Declare.new({ :queue => name, :nowait => true }.merge(opts))
|
12
12
|
)
|
13
|
-
nil
|
14
|
-
end
|
15
|
-
|
16
|
-
def delete(opts = {})
|
17
|
-
server.send_frame(
|
18
|
-
Protocol::Queue::Delete.new({ :queue => name, :nowait => true }.merge(opts))
|
19
|
-
)
|
20
|
-
nil
|
21
13
|
end
|
22
14
|
|
23
15
|
def pop(opts = {})
|
@@ -26,7 +18,7 @@ module AMQP
|
|
26
18
|
Protocol::Basic::Get.new({ :queue => name, :consumer_tag => name, :no_ack => !opts.delete(:ack), :nowait => true }.merge(opts))
|
27
19
|
)
|
28
20
|
method = server.next_method
|
29
|
-
return
|
21
|
+
return unless method.is_a?(Protocol::Basic::GetOk)
|
30
22
|
|
31
23
|
self.delivery_tag = method.delivery_tag
|
32
24
|
|
@@ -63,9 +55,38 @@ module AMQP
|
|
63
55
|
[method.message_count, method.consumer_count]
|
64
56
|
end
|
65
57
|
|
58
|
+
def bind(exchange, opts = {})
|
59
|
+
exchange = exchange.respond_to?(:name) ? exchange.name : exchange
|
60
|
+
bindings[exchange] = opts
|
61
|
+
server.send_frame(
|
62
|
+
Protocol::Queue::Bind.new({ :queue => name, :exchange => exchange, :routing_key => opts.delete(:key), :nowait => true }.merge(opts))
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def unbind(exchange, opts = {})
|
67
|
+
exchange = exchange.respond_to?(:name) ? exchange.name : exchange
|
68
|
+
bindings.delete(exchange)
|
69
|
+
|
70
|
+
server.send_frame(
|
71
|
+
Protocol::Queue::Unbind.new({
|
72
|
+
:queue => name, :exchange => exchange, :routing_key => opts.delete(:key), :nowait => true }.merge(opts)
|
73
|
+
)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete(opts = {})
|
78
|
+
server.send_frame(
|
79
|
+
Protocol::Queue::Delete.new({ :queue => name, :nowait => true }.merge(opts))
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
66
83
|
private
|
67
84
|
def exchange
|
68
85
|
@exchange ||= Exchange.new(server, :direct, '', :key => name)
|
69
86
|
end
|
87
|
+
|
88
|
+
def bindings
|
89
|
+
@bindings ||= {}
|
90
|
+
end
|
70
91
|
end
|
71
92
|
end
|
data/lib/amqp/server.rb
CHANGED
@@ -2,18 +2,15 @@ require 'socket'
|
|
2
2
|
require 'thread'
|
3
3
|
require 'timeout'
|
4
4
|
|
5
|
-
module AMQP
|
5
|
+
module Carrot::AMQP
|
6
6
|
class Server
|
7
7
|
CONNECT_TIMEOUT = 1.0
|
8
8
|
RETRY_DELAY = 10.0
|
9
9
|
DEFAULT_PORT = 5672
|
10
10
|
|
11
11
|
attr_reader :host, :port, :status
|
12
|
-
attr_accessor :
|
12
|
+
attr_accessor :channel, :ticket
|
13
13
|
|
14
|
-
class ConnectionError < StandardError; end
|
15
|
-
class ServerError < StandardError; end
|
16
|
-
class ClientError < StandardError; end
|
17
14
|
class ServerDown < StandardError; end
|
18
15
|
class ProtocolError < StandardError; end
|
19
16
|
|
@@ -30,44 +27,6 @@ module AMQP
|
|
30
27
|
start_session
|
31
28
|
end
|
32
29
|
|
33
|
-
def start_session
|
34
|
-
@channel = 0
|
35
|
-
write(HEADER)
|
36
|
-
write([1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4'))
|
37
|
-
raise ProtocolError, 'bad start connection' unless next_method.is_a?(Protocol::Connection::Start)
|
38
|
-
|
39
|
-
send_frame(
|
40
|
-
Protocol::Connection::StartOk.new(
|
41
|
-
{:platform => 'Ruby', :product => 'Carrot', :information => 'http://github.com/famosagle/carrot', :version => VERSION},
|
42
|
-
'AMQPLAIN',
|
43
|
-
{:LOGIN => @user, :PASSWORD => @pass},
|
44
|
-
'en_US'
|
45
|
-
)
|
46
|
-
)
|
47
|
-
|
48
|
-
if next_method.is_a?(Protocol::Connection::Tune)
|
49
|
-
send_frame(
|
50
|
-
Protocol::Connection::TuneOk.new( :channel_max => 0, :frame_max => 131072, :heartbeat => 0)
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
send_frame(
|
55
|
-
Protocol::Connection::Open.new(:virtual_host => @vhost, :capabilities => '', :insist => @insist)
|
56
|
-
)
|
57
|
-
raise ProtocolError, 'bad open connection' unless next_method.is_a?(Protocol::Connection::OpenOk)
|
58
|
-
|
59
|
-
@channel = 1
|
60
|
-
send_frame(Protocol::Channel::Open.new)
|
61
|
-
raise ProtocolError, "cannot open channel #{channel}" unless next_method.is_a?(Protocol::Channel::OpenOk)
|
62
|
-
|
63
|
-
send_frame(
|
64
|
-
Protocol::Access::Request.new(:realm => '/data', :read => true, :write => true, :active => true, :passive => true)
|
65
|
-
)
|
66
|
-
method = next_method
|
67
|
-
raise ProtocolError, 'access denied' unless method.is_a?(Protocol::Access::RequestOk)
|
68
|
-
self.ticket = method.ticket
|
69
|
-
end
|
70
|
-
|
71
30
|
def send_frame(*args)
|
72
31
|
args.each do |data|
|
73
32
|
data.ticket = ticket if ticket and data.respond_to?(:ticket=)
|
@@ -77,10 +36,11 @@ module AMQP
|
|
77
36
|
log :send, data
|
78
37
|
write(data.to_s)
|
79
38
|
end
|
39
|
+
nil
|
80
40
|
end
|
81
41
|
|
82
42
|
def next_frame
|
83
|
-
frame = Frame.
|
43
|
+
frame = Frame.parse(buffer)
|
84
44
|
log :received, frame
|
85
45
|
frame
|
86
46
|
end
|
@@ -90,7 +50,8 @@ module AMQP
|
|
90
50
|
end
|
91
51
|
|
92
52
|
def next_payload
|
93
|
-
next_frame
|
53
|
+
frame = next_frame
|
54
|
+
frame and frame.payload
|
94
55
|
end
|
95
56
|
|
96
57
|
def close
|
@@ -109,49 +70,21 @@ module AMQP
|
|
109
70
|
end
|
110
71
|
|
111
72
|
def read(*args)
|
112
|
-
|
113
|
-
socket.read(*args)
|
114
|
-
end
|
73
|
+
socket.read(*args)
|
115
74
|
end
|
116
75
|
|
117
76
|
def write(*args)
|
118
|
-
|
119
|
-
socket.write(*args)
|
120
|
-
end
|
77
|
+
socket.write(*args)
|
121
78
|
end
|
122
79
|
|
123
80
|
private
|
124
81
|
|
125
|
-
def
|
126
|
-
|
127
|
-
begin
|
128
|
-
mutex.lock if multithread?
|
129
|
-
yield socket
|
130
|
-
|
131
|
-
rescue ClientError, ServerError, SocketError, SystemCallError, IOError => error
|
132
|
-
if not retried
|
133
|
-
# Close the socket and retry once.
|
134
|
-
close_socket
|
135
|
-
#start_session
|
136
|
-
retried = true
|
137
|
-
retry
|
138
|
-
else
|
139
|
-
# Mark the server dead and raise an error.
|
140
|
-
close(error.message)
|
141
|
-
|
142
|
-
# Reraise as a ConnectionError
|
143
|
-
new_error = ConnectionError.new("#{error.class}: #{error.message}")
|
144
|
-
new_error.set_backtrace(error.backtrace)
|
145
|
-
raise new_error
|
146
|
-
end
|
147
|
-
ensure
|
148
|
-
mutex.unlock if multithread?
|
149
|
-
end
|
82
|
+
def buffer
|
83
|
+
@buffer ||= Buffer.new(self)
|
150
84
|
end
|
151
85
|
|
152
86
|
def socket
|
153
87
|
return @socket if @socket and not @socket.closed?
|
154
|
-
raise ServerDown, "will retry at #{retry_at}" unless retry?
|
155
88
|
|
156
89
|
begin
|
157
90
|
# Attempt to connect.
|
@@ -163,10 +96,9 @@ module AMQP
|
|
163
96
|
if Socket.constants.include? 'TCP_NODELAY'
|
164
97
|
@socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
|
165
98
|
end
|
166
|
-
@retry_at = nil
|
167
99
|
@status = 'CONNECTED'
|
168
100
|
rescue SocketError, SystemCallError, IOError, Timeout::Error => e
|
169
|
-
|
101
|
+
kill(e.message)
|
170
102
|
raise ServerDown, e.message
|
171
103
|
ensure
|
172
104
|
mutex.unlock if multithread?
|
@@ -175,16 +107,46 @@ module AMQP
|
|
175
107
|
@socket
|
176
108
|
end
|
177
109
|
|
178
|
-
def
|
179
|
-
@
|
180
|
-
|
110
|
+
def start_session
|
111
|
+
@channel = 0
|
112
|
+
write(HEADER)
|
113
|
+
write([1, 1, VERSION_MAJOR, VERSION_MINOR].pack('C4'))
|
114
|
+
raise ProtocolError, 'bad start connection' unless next_method.is_a?(Protocol::Connection::Start)
|
181
115
|
|
182
|
-
|
183
|
-
|
116
|
+
send_frame(
|
117
|
+
Protocol::Connection::StartOk.new(
|
118
|
+
{:platform => 'Ruby', :product => 'Carrot', :information => 'http://github.com/famosagle/carrot', :version => VERSION},
|
119
|
+
'AMQPLAIN',
|
120
|
+
{:LOGIN => @user, :PASSWORD => @pass},
|
121
|
+
'en_US'
|
122
|
+
)
|
123
|
+
)
|
124
|
+
|
125
|
+
if next_method.is_a?(Protocol::Connection::Tune)
|
126
|
+
send_frame(
|
127
|
+
Protocol::Connection::TuneOk.new( :channel_max => 0, :frame_max => 131072, :heartbeat => 0)
|
128
|
+
)
|
129
|
+
end
|
130
|
+
|
131
|
+
send_frame(
|
132
|
+
Protocol::Connection::Open.new(:virtual_host => @vhost, :capabilities => '', :insist => @insist)
|
133
|
+
)
|
134
|
+
raise ProtocolError, 'bad open connection' unless next_method.is_a?(Protocol::Connection::OpenOk)
|
135
|
+
|
136
|
+
@channel = 1
|
137
|
+
send_frame(Protocol::Channel::Open.new)
|
138
|
+
raise ProtocolError, "cannot open channel #{channel}" unless next_method.is_a?(Protocol::Channel::OpenOk)
|
139
|
+
|
140
|
+
send_frame(
|
141
|
+
Protocol::Access::Request.new(:realm => '/data', :read => true, :write => true, :active => true, :passive => true)
|
142
|
+
)
|
143
|
+
method = next_method
|
144
|
+
raise ProtocolError, 'access denied' unless method.is_a?(Protocol::Access::RequestOk)
|
145
|
+
self.ticket = method.ticket
|
184
146
|
end
|
185
147
|
|
186
|
-
def
|
187
|
-
|
148
|
+
def multithread?
|
149
|
+
@multithread
|
188
150
|
end
|
189
151
|
|
190
152
|
def close_socket(reason=nil)
|
@@ -192,7 +154,6 @@ module AMQP
|
|
192
154
|
mutex.lock if multithread?
|
193
155
|
@socket.close if @socket and not @socket.closed?
|
194
156
|
@socket = nil
|
195
|
-
@retry_at = nil
|
196
157
|
@status = "NOT CONNECTED"
|
197
158
|
ensure
|
198
159
|
mutex.unlock if multithread?
|
data/lib/amqp/spec.rb
CHANGED
@@ -1,16 +1,4 @@
|
|
1
|
-
|
2
|
-
#:stopdoc:
|
3
|
-
# this file was autogenerated on Sat Jan 03 14:05:57 -0600 2009
|
4
|
-
# using amqp-0.8.json (mtime: Sat Jan 03 08:58:13 -0600 2009)
|
5
|
-
#
|
6
|
-
# DO NOT EDIT! (edit protocol/codegen.rb instead, and run `rake codegen`)
|
7
|
-
|
8
|
-
module AMQP
|
9
|
-
HEADER = "AMQP".freeze
|
10
|
-
VERSION_MAJOR = 8
|
11
|
-
VERSION_MINOR = 0
|
12
|
-
PORT = 5672
|
13
|
-
|
1
|
+
module Carrot::AMQP
|
14
2
|
class Frame
|
15
3
|
def self.types
|
16
4
|
@types ||= {}
|
@@ -143,7 +131,7 @@ module AMQP
|
|
143
131
|
end
|
144
132
|
end
|
145
133
|
|
146
|
-
module AMQP
|
134
|
+
module Carrot::AMQP
|
147
135
|
module Protocol
|
148
136
|
class Connection < Class( 10, :connection ); end
|
149
137
|
class Channel < Class( 20, :channel ); end
|
data/lib/carrot.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
class Carrot
|
2
|
+
module AMQP
|
3
|
+
HEADER = "AMQP".freeze
|
4
|
+
VERSION_MAJOR = 8
|
5
|
+
VERSION_MINOR = 0
|
6
|
+
PORT = 5672
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
1
10
|
$:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
|
2
11
|
require 'amqp/spec'
|
3
12
|
require 'amqp/buffer'
|
@@ -18,40 +27,38 @@ class Carrot
|
|
18
27
|
end
|
19
28
|
class Error < StandardError; end
|
20
29
|
|
21
|
-
|
22
|
-
instance(opts).queue(name, opts)
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.stop
|
26
|
-
instance.stop
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.instance(opts = {})
|
30
|
-
@instance ||= new(opts)
|
31
|
-
end
|
30
|
+
attr_accessor :server
|
32
31
|
|
33
32
|
def initialize(opts = {})
|
34
33
|
@server = AMQP::Server.new(opts)
|
35
34
|
end
|
36
35
|
|
37
36
|
def queue(name, opts = {})
|
38
|
-
queues[name] ||= AMQP::Queue.new(
|
37
|
+
queues[name] ||= AMQP::Queue.new(server, name, opts)
|
39
38
|
end
|
40
39
|
|
41
40
|
def stop
|
42
|
-
|
41
|
+
server.close
|
43
42
|
end
|
44
43
|
|
45
44
|
def queues
|
46
45
|
@queues ||= {}
|
47
46
|
end
|
48
47
|
|
49
|
-
def
|
50
|
-
|
48
|
+
def direct(name = 'amq.direct', opts = {})
|
49
|
+
exchanges[name] ||= Exchange.new(server, :direct, name, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
def topic(name = 'amq.topic', opts = {})
|
53
|
+
exchanges[name] ||= Exchange.new(server, :topic, name, opts)
|
51
54
|
end
|
52
55
|
|
53
|
-
def
|
54
|
-
|
56
|
+
def headers(name = 'amq.match', opts = {})
|
57
|
+
exchanges[name] ||= Exchange.new(server, :headers, name, opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
def exchanges
|
61
|
+
@exchanges ||= {}
|
55
62
|
end
|
56
63
|
|
57
64
|
private
|
@@ -62,3 +69,19 @@ private
|
|
62
69
|
puts
|
63
70
|
end
|
64
71
|
end
|
72
|
+
|
73
|
+
#-- convenience wrapper (read: HACK) for thread-local Carrot object
|
74
|
+
|
75
|
+
class Carrot
|
76
|
+
def Carrot.default
|
77
|
+
#-- XXX clear this when connection is closed
|
78
|
+
Thread.current[:carrot] ||= Carrot.new
|
79
|
+
end
|
80
|
+
|
81
|
+
# Allows for calls to all Carrot instance methods. This implicitly calls
|
82
|
+
# Carrot.new so that a new channel is allocated for subsequent operations.
|
83
|
+
def Carrot.method_missing(meth, *args, &blk)
|
84
|
+
Carrot.default.__send__(meth, *args, &blk)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: famoseagle-carrot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amos Elliston
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|