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 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
- 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
-
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
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :patch: 0
3
3
  :major: 0
4
- :minor: 2
4
+ :minor: 3
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 size, pack = nil
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(Protocol::Exchange::Delete.new({ :exchange => name, :nowait => true }.merge(opts)))
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 buf
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
@@ -1,4 +1,4 @@
1
- module AMQP
1
+ module Carrot::AMQP
2
2
  class Header
3
3
  def initialize(server, header_obj)
4
4
  @server = server
data/lib/amqp/protocol.rb CHANGED
@@ -1,4 +1,4 @@
1
- module AMQP
1
+ module Carrot::AMQP
2
2
  module Protocol
3
3
  #:stopdoc:
4
4
  class Class::Method
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 if method.is_a?(Protocol::Basic::GetEmpty)
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 :retry_at, :channel, :ticket
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.get(self)
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.payload
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
- with_socket_management do |socket|
113
- socket.read(*args)
114
- end
73
+ socket.read(*args)
115
74
  end
116
75
 
117
76
  def write(*args)
118
- with_socket_management do |socket|
119
- socket.write(*args)
120
- end
77
+ socket.write(*args)
121
78
  end
122
79
 
123
80
  private
124
81
 
125
- def with_socket_management(&block)
126
- retried = false
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
- close_socket
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 multithread?
179
- @multithread
180
- end
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
- def retry?
183
- @retry_at.nil? or @retry_at < Time.now
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 unexpected_eof!
187
- raise ConnectionError, 'unexpected end of file'
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
- def self.queue(name, opts = {})
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(@server, name, opts)
37
+ queues[name] ||= AMQP::Queue.new(server, name, opts)
39
38
  end
40
39
 
41
40
  def stop
42
- @server.close
41
+ server.close
43
42
  end
44
43
 
45
44
  def queues
46
45
  @queues ||= {}
47
46
  end
48
47
 
49
- def send_data(data)
50
- @server.send_data(data)
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 send_command(cmd)
54
- @server.send_command(cmd)
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.2.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-08 00:00:00 -07:00
12
+ date: 2009-04-12 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15