celldee-bunny 0.2.0 → 0.3.0
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/LICENSE +20 -0
- data/README +49 -0
- data/Rakefile +3 -4
- data/bunny.gemspec +37 -0
- data/examples/simple.rb +1 -1
- data/examples/simple_ack.rb +1 -1
- data/examples/simple_consumer.rb +1 -1
- data/examples/simple_fanout.rb +1 -1
- data/examples/simple_publisher.rb +1 -1
- data/examples/simple_topic.rb +3 -3
- data/ext/codegen.rb +177 -0
- data/lib/bunny/client.rb +161 -14
- data/lib/bunny/exchange.rb +155 -80
- data/lib/bunny/protocol/protocol.rb +135 -0
- data/lib/bunny/protocol/spec.rb +836 -0
- data/lib/bunny/queue.rb +223 -23
- data/lib/bunny/transport/buffer.rb +266 -0
- data/lib/bunny/transport/frame.rb +62 -0
- data/lib/bunny.rb +20 -62
- data/spec/bunny_spec.rb +2 -2
- data/spec/exchange_spec.rb +3 -3
- data/spec/queue_spec.rb +10 -9
- metadata +22 -22
- data/README.markdown +0 -126
- data/lib/api_messages.rb +0 -18
- data/lib/bunny/header.rb +0 -30
- data/lib/engineroom/buffer.rb +0 -274
- data/lib/engineroom/frame.rb +0 -61
- data/lib/engineroom/protocol.rb +0 -156
- data/lib/engineroom/spec.rb +0 -830
- data/protocol/codegen.rb +0 -171
- /data/{protocol → ext}/amqp-0.8.json +0 -0
data/lib/engineroom/buffer.rb
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
if [].map.respond_to? :with_index
|
|
2
|
-
class Array #:nodoc:
|
|
3
|
-
def enum_with_index
|
|
4
|
-
each.with_index
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
else
|
|
8
|
-
require 'enumerator'
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
module Transport
|
|
12
|
-
class Buffer #:nodoc: all
|
|
13
|
-
|
|
14
|
-
def initialize data = ''
|
|
15
|
-
@data = data
|
|
16
|
-
@pos = 0
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
attr_reader :pos
|
|
20
|
-
|
|
21
|
-
def data
|
|
22
|
-
@data.clone
|
|
23
|
-
end
|
|
24
|
-
alias :contents :data
|
|
25
|
-
alias :to_s :data
|
|
26
|
-
|
|
27
|
-
def << data
|
|
28
|
-
@data << data.to_s
|
|
29
|
-
self
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def length
|
|
33
|
-
@data.length
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def empty?
|
|
37
|
-
pos == length
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def rewind
|
|
41
|
-
@pos = 0
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def read_properties *types
|
|
45
|
-
types.shift if types.first == :properties
|
|
46
|
-
|
|
47
|
-
i = 0
|
|
48
|
-
values = []
|
|
49
|
-
|
|
50
|
-
while props = read(:short)
|
|
51
|
-
(0..14).each do |n|
|
|
52
|
-
# no more property types
|
|
53
|
-
break unless types[i]
|
|
54
|
-
|
|
55
|
-
# if flag is set
|
|
56
|
-
if props & (1<<(15-n)) != 0
|
|
57
|
-
if types[i] == :bit
|
|
58
|
-
# bit values exist in flags only
|
|
59
|
-
values << true
|
|
60
|
-
else
|
|
61
|
-
# save type name for later reading
|
|
62
|
-
values << types[i]
|
|
63
|
-
end
|
|
64
|
-
else
|
|
65
|
-
# property not set or is false bit
|
|
66
|
-
values << (types[i] == :bit ? false : nil)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
i+=1
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# bit(0) == 0 means no more property flags
|
|
73
|
-
break unless props & 1 == 1
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
values.map do |value|
|
|
77
|
-
value.is_a?(Symbol) ? read(value) : value
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def read *types
|
|
82
|
-
if types.first == :properties
|
|
83
|
-
return read_properties(*types)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
values = types.map do |type|
|
|
87
|
-
case type
|
|
88
|
-
when :octet
|
|
89
|
-
_read(1, 'C')
|
|
90
|
-
when :short
|
|
91
|
-
_read(2, 'n')
|
|
92
|
-
when :long
|
|
93
|
-
_read(4, 'N')
|
|
94
|
-
when :longlong
|
|
95
|
-
upper, lower = _read(8, 'NN')
|
|
96
|
-
upper << 32 | lower
|
|
97
|
-
when :shortstr
|
|
98
|
-
_read read(:octet)
|
|
99
|
-
when :longstr
|
|
100
|
-
_read read(:long)
|
|
101
|
-
when :timestamp
|
|
102
|
-
Time.at read(:longlong)
|
|
103
|
-
when :table
|
|
104
|
-
t = Hash.new
|
|
105
|
-
|
|
106
|
-
table = Buffer.new(read(:longstr))
|
|
107
|
-
until table.empty?
|
|
108
|
-
key, type = table.read(:shortstr, :octet)
|
|
109
|
-
key = key.intern
|
|
110
|
-
t[key] ||= case type
|
|
111
|
-
when 83 # 'S'
|
|
112
|
-
table.read(:longstr)
|
|
113
|
-
when 73 # 'I'
|
|
114
|
-
table.read(:long)
|
|
115
|
-
when 68 # 'D'
|
|
116
|
-
exp = table.read(:octet)
|
|
117
|
-
num = table.read(:long)
|
|
118
|
-
num / 10.0**exp
|
|
119
|
-
when 84 # 'T'
|
|
120
|
-
table.read(:timestamp)
|
|
121
|
-
when 70 # 'F'
|
|
122
|
-
table.read(:table)
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
t
|
|
127
|
-
when :bit
|
|
128
|
-
if (@bits ||= []).empty?
|
|
129
|
-
val = read(:octet)
|
|
130
|
-
@bits = (0..7).map{|i| (val & 1<<i) != 0 }
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
@bits.shift
|
|
134
|
-
else
|
|
135
|
-
raise API::InvalidTypeError, "Cannot read data of type #{type}"
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
types.size == 1 ? values.first : values
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def write type, data
|
|
143
|
-
case type
|
|
144
|
-
when :octet
|
|
145
|
-
_write(data, 'C')
|
|
146
|
-
when :short
|
|
147
|
-
_write(data, 'n')
|
|
148
|
-
when :long
|
|
149
|
-
_write(data, 'N')
|
|
150
|
-
when :longlong
|
|
151
|
-
lower = data & 0xffffffff
|
|
152
|
-
upper = (data & ~0xffffffff) >> 32
|
|
153
|
-
_write([upper, lower], 'NN')
|
|
154
|
-
when :shortstr
|
|
155
|
-
data = (data || '').to_s
|
|
156
|
-
_write([data.length, data], 'Ca*')
|
|
157
|
-
when :longstr
|
|
158
|
-
if data.is_a? Hash
|
|
159
|
-
write(:table, data)
|
|
160
|
-
else
|
|
161
|
-
data = (data || '').to_s
|
|
162
|
-
_write([data.length, data], 'Na*')
|
|
163
|
-
end
|
|
164
|
-
when :timestamp
|
|
165
|
-
write(:longlong, data.to_i)
|
|
166
|
-
when :table
|
|
167
|
-
data ||= {}
|
|
168
|
-
write :longstr, (data.inject(Buffer.new) do |table, (key, value)|
|
|
169
|
-
table.write(:shortstr, key.to_s)
|
|
170
|
-
|
|
171
|
-
case value
|
|
172
|
-
when String
|
|
173
|
-
table.write(:octet, 83) # 'S'
|
|
174
|
-
table.write(:longstr, value.to_s)
|
|
175
|
-
when Fixnum
|
|
176
|
-
table.write(:octet, 73) # 'I'
|
|
177
|
-
table.write(:long, value)
|
|
178
|
-
when Float
|
|
179
|
-
table.write(:octet, 68) # 'D'
|
|
180
|
-
# XXX there's gotta be a better way to do this..
|
|
181
|
-
exp = value.to_s.split('.').last.length
|
|
182
|
-
num = value * 10**exp
|
|
183
|
-
table.write(:octet, exp)
|
|
184
|
-
table.write(:long, num)
|
|
185
|
-
when Time
|
|
186
|
-
table.write(:octet, 84) # 'T'
|
|
187
|
-
table.write(:timestamp, value)
|
|
188
|
-
when Hash
|
|
189
|
-
table.write(:octet, 70) # 'F'
|
|
190
|
-
table.write(:table, value)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
table
|
|
194
|
-
end)
|
|
195
|
-
when :bit
|
|
196
|
-
[*data].to_enum(:each_slice, 8).each{|bits|
|
|
197
|
-
write(:octet, bits.enum_with_index.inject(0){ |byte, (bit, i)|
|
|
198
|
-
byte |= 1<<i if bit
|
|
199
|
-
byte
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
when :properties
|
|
203
|
-
values = []
|
|
204
|
-
data.enum_with_index.inject(0) do |short, ((type, value), i)|
|
|
205
|
-
n = i % 15
|
|
206
|
-
last = i+1 == data.size
|
|
207
|
-
|
|
208
|
-
if (n == 0 and i != 0) or last
|
|
209
|
-
if data.size > i+1
|
|
210
|
-
short |= 1<<0
|
|
211
|
-
elsif last and value
|
|
212
|
-
values << [type,value]
|
|
213
|
-
short |= 1<<(15-n)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
write(:short, short)
|
|
217
|
-
short = 0
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
if value and !last
|
|
221
|
-
values << [type,value]
|
|
222
|
-
short |= 1<<(15-n)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
short
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
values.each do |type, value|
|
|
229
|
-
write(type, value) unless type == :bit
|
|
230
|
-
end
|
|
231
|
-
else
|
|
232
|
-
raise API::InvalidTypeError, "Cannot write data of type #{type}"
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
self
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def extract
|
|
239
|
-
begin
|
|
240
|
-
cur_data, cur_pos = @data.clone, @pos
|
|
241
|
-
yield self
|
|
242
|
-
rescue API::BufferOverflowError
|
|
243
|
-
@data, @pos = cur_data, cur_pos
|
|
244
|
-
nil
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def _read(size, pack = nil)
|
|
249
|
-
if @data.is_a?(API::Client)
|
|
250
|
-
raw = @data.read(size)
|
|
251
|
-
return raw if raw.nil? or pack.nil?
|
|
252
|
-
return raw.unpack(pack).first
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
if @pos + size > length
|
|
256
|
-
raise API::BufferOverflowError
|
|
257
|
-
else
|
|
258
|
-
data = @data[@pos,size]
|
|
259
|
-
@data[@pos,size] = ''
|
|
260
|
-
if pack
|
|
261
|
-
data = data.unpack(pack)
|
|
262
|
-
data = data.pop if data.size == 1
|
|
263
|
-
end
|
|
264
|
-
data
|
|
265
|
-
end
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
def _write data, pack = nil
|
|
269
|
-
data = [*data].pack(pack) if pack
|
|
270
|
-
@data[@pos,0] = data
|
|
271
|
-
@pos += data.length
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
end
|
data/lib/engineroom/frame.rb
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
module Transport
|
|
2
|
-
|
|
3
|
-
class Frame #:nodoc: all
|
|
4
|
-
def initialize payload = nil, channel = 0
|
|
5
|
-
@channel, @payload = channel, payload
|
|
6
|
-
end
|
|
7
|
-
attr_accessor :channel, :payload
|
|
8
|
-
|
|
9
|
-
def id
|
|
10
|
-
self.class::ID
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def to_binary
|
|
14
|
-
buf = Buffer.new
|
|
15
|
-
buf.write :octet, id
|
|
16
|
-
buf.write :short, channel
|
|
17
|
-
buf.write :longstr, payload
|
|
18
|
-
buf.write :octet, FOOTER
|
|
19
|
-
buf.rewind
|
|
20
|
-
buf
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def to_s
|
|
24
|
-
to_binary.to_s
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def == frame
|
|
28
|
-
[ :id, :channel, :payload ].inject(true) do |eql, field|
|
|
29
|
-
eql and __send__(field) == frame.__send__(field)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
class Method
|
|
34
|
-
def initialize payload = nil, channel = 0
|
|
35
|
-
super
|
|
36
|
-
unless @payload.is_a? Protocol::Class::Method or @payload.nil?
|
|
37
|
-
@payload = Protocol.parse(@payload)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
class Header
|
|
43
|
-
def initialize payload = nil, channel = 0
|
|
44
|
-
super
|
|
45
|
-
unless @payload.is_a? Protocol::Header or @payload.nil?
|
|
46
|
-
@payload = Protocol::Header.new(@payload)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
class Body; end
|
|
52
|
-
|
|
53
|
-
def self.parse buf
|
|
54
|
-
buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
|
|
55
|
-
buf.extract do
|
|
56
|
-
id, channel, payload, footer = buf.read(:octet, :short, :longstr, :octet)
|
|
57
|
-
Transport::Frame.types[id].new(payload, channel) if footer == Transport::Frame::FOOTER
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
data/lib/engineroom/protocol.rb
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
module Protocol
|
|
2
|
-
#:stopdoc:
|
|
3
|
-
class Class::Method
|
|
4
|
-
def initialize *args
|
|
5
|
-
opts = args.pop if args.last.is_a? Hash
|
|
6
|
-
opts ||= {}
|
|
7
|
-
|
|
8
|
-
@debug = 1 # XXX hack, p(obj) == '' if no instance vars are set
|
|
9
|
-
|
|
10
|
-
if args.size == 1 and args.first.is_a? Transport::Buffer
|
|
11
|
-
buf = args.shift
|
|
12
|
-
else
|
|
13
|
-
buf = nil
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
self.class.arguments.each do |type, name|
|
|
17
|
-
val = buf ? buf.read(type) :
|
|
18
|
-
args.shift || opts[name] || opts[name.to_s]
|
|
19
|
-
instance_variable_set("@#{name}", val)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def arguments
|
|
24
|
-
self.class.arguments.inject({}) do |hash, (type, name)|
|
|
25
|
-
hash.update name => instance_variable_get("@#{name}")
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def to_binary
|
|
30
|
-
buf = Transport::Buffer.new
|
|
31
|
-
buf.write :short, self.class.parent.id
|
|
32
|
-
buf.write :short, self.class.id
|
|
33
|
-
|
|
34
|
-
bits = []
|
|
35
|
-
|
|
36
|
-
self.class.arguments.each do |type, name|
|
|
37
|
-
val = instance_variable_get("@#{name}")
|
|
38
|
-
if type == :bit
|
|
39
|
-
bits << (val || false)
|
|
40
|
-
else
|
|
41
|
-
unless bits.empty?
|
|
42
|
-
buf.write :bit, bits
|
|
43
|
-
bits = []
|
|
44
|
-
end
|
|
45
|
-
buf.write type, val
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
buf.write :bit, bits unless bits.empty?
|
|
50
|
-
buf.rewind
|
|
51
|
-
|
|
52
|
-
buf
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def to_s
|
|
56
|
-
to_binary.to_s
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def to_frame channel = 0
|
|
60
|
-
Transport::Frame::Method.new(self, channel)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
#:startdoc:
|
|
65
|
-
#
|
|
66
|
-
# Contains a properties hash that holds some potentially interesting
|
|
67
|
-
# information.
|
|
68
|
-
# * :delivery_mode
|
|
69
|
-
# 1 equals transient.
|
|
70
|
-
# 2 equals persistent. Unconsumed persistent messages will survive
|
|
71
|
-
# a server restart when they are stored in a durable queue.
|
|
72
|
-
# * :redelivered
|
|
73
|
-
# True or False
|
|
74
|
-
# * :routing_key
|
|
75
|
-
# The routing string used for matching this message to this queue.
|
|
76
|
-
# * :priority
|
|
77
|
-
# An integer in the range of 0 to 9 inclusive.
|
|
78
|
-
# * :content_type
|
|
79
|
-
# Always "application/octet-stream" (byte stream)
|
|
80
|
-
# * :exchange
|
|
81
|
-
# The source exchange which published this message.
|
|
82
|
-
# * :message_count
|
|
83
|
-
# The number of unconsumed messages contained in the queue.
|
|
84
|
-
# * :delivery_tag
|
|
85
|
-
# A monotonically increasing integer. This number should not be trusted
|
|
86
|
-
# as a sequence number. There is no guarantee it won't get reset.
|
|
87
|
-
class Header
|
|
88
|
-
def initialize *args
|
|
89
|
-
opts = args.pop if args.last.is_a? Hash
|
|
90
|
-
opts ||= {}
|
|
91
|
-
|
|
92
|
-
first = args.shift
|
|
93
|
-
|
|
94
|
-
if first.is_a? ::Class and first.ancestors.include? Protocol::Class
|
|
95
|
-
@klass = first
|
|
96
|
-
@size = args.shift || 0
|
|
97
|
-
@weight = args.shift || 0
|
|
98
|
-
@properties = opts
|
|
99
|
-
|
|
100
|
-
elsif first.is_a? Transport::Buffer or first.is_a? String
|
|
101
|
-
buf = first
|
|
102
|
-
buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
|
|
103
|
-
|
|
104
|
-
@klass = Protocol.classes[buf.read(:short)]
|
|
105
|
-
@weight = buf.read(:short)
|
|
106
|
-
@size = buf.read(:longlong)
|
|
107
|
-
|
|
108
|
-
props = buf.read(:properties, *klass.properties.map{|type,_| type })
|
|
109
|
-
@properties = Hash[*klass.properties.map{|_,name| name }.zip(props).reject{|k,v| v.nil? }.flatten]
|
|
110
|
-
|
|
111
|
-
else
|
|
112
|
-
raise ArgumentError, 'Invalid argument'
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
end
|
|
116
|
-
attr_accessor :klass, :size, :weight, :properties
|
|
117
|
-
|
|
118
|
-
def to_binary
|
|
119
|
-
buf = Transport::Buffer.new
|
|
120
|
-
buf.write :short, klass.id
|
|
121
|
-
buf.write :short, weight # XXX rabbitmq only supports weight == 0
|
|
122
|
-
buf.write :longlong, size
|
|
123
|
-
buf.write :properties, (klass.properties.map do |type, name|
|
|
124
|
-
[ type, properties[name] || properties[name.to_s] ]
|
|
125
|
-
end)
|
|
126
|
-
buf.rewind
|
|
127
|
-
buf
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def to_s
|
|
131
|
-
to_binary.to_s
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def to_frame channel = 0
|
|
135
|
-
Transport::Frame::Header.new(self, channel)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def == header
|
|
139
|
-
[ :klass, :size, :weight, :properties ].inject(true) do |eql, field|
|
|
140
|
-
eql and __send__(field) == header.__send__(field)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def method_missing meth, *args, &blk
|
|
145
|
-
@properties.has_key?(meth) || @klass.properties.find{|_,name| name == meth } ? @properties[meth] :
|
|
146
|
-
super
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def self.parse buf
|
|
151
|
-
buf = Transport::Buffer.new(buf) unless buf.is_a? Transport::Buffer
|
|
152
|
-
class_id, method_id = buf.read(:short, :short)
|
|
153
|
-
classes[class_id].methods[method_id].new(buf)
|
|
154
|
-
end
|
|
155
|
-
#:stopdoc:
|
|
156
|
-
end
|