amqp-client 1.0.1 → 1.1.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +2 -2
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +29 -12
- data/CHANGELOG.md +21 -0
- data/Gemfile +4 -0
- data/README.md +63 -28
- data/Rakefile +3 -1
- data/amqp-client.gemspec +1 -1
- data/lib/amqp/client/channel.rb +39 -32
- data/lib/amqp/client/connection.rb +107 -64
- data/lib/amqp/client/errors.rb +18 -1
- data/lib/amqp/client/{frames.rb → frame_bytes.rb} +34 -36
- data/lib/amqp/client/message.rb +101 -44
- data/lib/amqp/client/properties.rb +143 -76
- data/lib/amqp/client/queue.rb +15 -21
- data/lib/amqp/client/table.rb +51 -32
- data/lib/amqp/client/version.rb +1 -1
- data/lib/amqp/client.rb +27 -9
- data/sig/amqp-client.rbs +264 -0
- metadata +5 -4
@@ -5,44 +5,111 @@ require_relative "./table"
|
|
5
5
|
module AMQP
|
6
6
|
class Client
|
7
7
|
# Encode/decode AMQP Properties
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
8
|
+
class Properties
|
9
|
+
# rubocop:disable Metrics/ParameterLists
|
10
|
+
def initialize(content_type: nil, content_encoding: nil, headers: nil, delivery_mode: nil,
|
11
|
+
priority: nil, correlation_id: nil, reply_to: nil, expiration: nil,
|
12
|
+
message_id: nil, timestamp: nil, type: nil, user_id: nil, app_id: nil)
|
13
|
+
@content_type = content_type
|
14
|
+
@content_encoding = content_encoding
|
15
|
+
@headers = headers
|
16
|
+
@delivery_mode = delivery_mode
|
17
|
+
@priority = priority
|
18
|
+
@correlation_id = correlation_id
|
19
|
+
@reply_to = reply_to
|
20
|
+
@expiration = expiration
|
21
|
+
@message_id = message_id
|
22
|
+
@timestamp = timestamp
|
23
|
+
@type = type
|
24
|
+
@user_id = user_id
|
25
|
+
@app_id = app_id
|
26
|
+
end
|
27
|
+
# rubocop:enable Metrics/ParameterLists
|
28
|
+
|
29
|
+
# Properties as a Hash
|
30
|
+
# @return [Hash] Properties
|
31
|
+
def to_h
|
32
|
+
{
|
33
|
+
content_type: content_type,
|
34
|
+
content_encoding: content_encoding,
|
35
|
+
headers: headers,
|
36
|
+
delivery_mode: delivery_mode,
|
37
|
+
priority: priority,
|
38
|
+
correlation_id: correlation_id,
|
39
|
+
reply_to: reply_to,
|
40
|
+
expiration: expiration,
|
41
|
+
message_id: message_id,
|
42
|
+
timestamp: timestamp,
|
43
|
+
type: type,
|
44
|
+
user_id: user_id,
|
45
|
+
app_id: app_id
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
alias to_hash to_h
|
50
|
+
|
51
|
+
# Returns true if two Property objects holds the same information
|
52
|
+
# @return [Boolean]
|
53
|
+
def ==(other)
|
54
|
+
return false unless other.is_a? self.class
|
55
|
+
|
56
|
+
instance_variables.all? { |v| instance_variable_get(v) == other.instance_variable_get(v) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Content type of the message body
|
60
|
+
# @return [String, nil]
|
61
|
+
attr_accessor :content_type
|
62
|
+
# Content encoding of the body
|
63
|
+
# @return [String, nil]
|
64
|
+
attr_accessor :content_encoding
|
65
|
+
# Headers, for applications and header exchange routing
|
66
|
+
# @return [Hash<String, Object>, nil]
|
67
|
+
attr_accessor :headers
|
68
|
+
# Message persistent level
|
69
|
+
# @note The exchange and queue have to durable as well for the message to be persistent
|
70
|
+
# @return [1] Transient message
|
71
|
+
# @return [2] Persistent message
|
72
|
+
# @return [nil] Not specified (implicitly transient)
|
73
|
+
attr_accessor :delivery_mode
|
74
|
+
# A priority of the message (between 0 and 255)
|
75
|
+
# @return [Integer, nil]
|
76
|
+
attr_accessor :priority
|
77
|
+
# Message correlation id, commonly used to correlate RPC requests and responses
|
78
|
+
# @return [String, nil]
|
79
|
+
attr_accessor :correlation_id
|
80
|
+
# Queue to reply RPC responses to
|
81
|
+
# @return [String, nil]
|
82
|
+
attr_accessor :reply_to
|
83
|
+
# Number of seconds the message will stay in the queue
|
84
|
+
# @return [String, nil]
|
85
|
+
attr_accessor :expiration
|
86
|
+
# Application message identifier
|
87
|
+
# @return [String, nil]
|
88
|
+
attr_accessor :message_id
|
89
|
+
# Message timestamp, often indicates when the message was originally generated
|
90
|
+
# @return [Date, nil]
|
91
|
+
attr_accessor :timestamp
|
92
|
+
# Message type name
|
93
|
+
# @return [String, nil]
|
94
|
+
attr_accessor :type
|
95
|
+
# The user that published the message
|
96
|
+
# @return [String, nil]
|
97
|
+
attr_accessor :user_id
|
98
|
+
# Name of application that generated the message
|
99
|
+
# @return [String, nil]
|
100
|
+
attr_accessor :app_id
|
101
|
+
|
37
102
|
# Encode properties into a byte array
|
103
|
+
# @param properties [Hash]
|
38
104
|
# @return [String] byte array
|
39
|
-
def encode
|
105
|
+
def self.encode(properties)
|
106
|
+
return "\x00\x00" if properties.empty?
|
107
|
+
|
40
108
|
flags = 0
|
41
109
|
arr = [flags]
|
42
|
-
fmt =
|
43
|
-
fmt.pos = 2
|
110
|
+
fmt = String.new("S>", capacity: 37)
|
44
111
|
|
45
|
-
if content_type
|
112
|
+
if (content_type = properties[:content_type])
|
46
113
|
content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
|
47
114
|
|
48
115
|
flags |= (1 << 15)
|
@@ -50,7 +117,7 @@ module AMQP
|
|
50
117
|
fmt << "Ca*"
|
51
118
|
end
|
52
119
|
|
53
|
-
if content_encoding
|
120
|
+
if (content_encoding = properties[:content_encoding])
|
54
121
|
content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
|
55
122
|
|
56
123
|
flags |= (1 << 14)
|
@@ -58,7 +125,7 @@ module AMQP
|
|
58
125
|
fmt << "Ca*"
|
59
126
|
end
|
60
127
|
|
61
|
-
if headers
|
128
|
+
if (headers = properties[:headers])
|
62
129
|
headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
|
63
130
|
|
64
131
|
flags |= (1 << 13)
|
@@ -67,31 +134,31 @@ module AMQP
|
|
67
134
|
fmt << "L>a*"
|
68
135
|
end
|
69
136
|
|
70
|
-
if delivery_mode
|
137
|
+
if (delivery_mode = properties[:delivery_mode])
|
71
138
|
delivery_mode.is_a?(Integer) || raise(ArgumentError, "delivery_mode must be an int")
|
72
|
-
delivery_mode.between?(0, 2) || raise(ArgumentError, "delivery_mode must be be between 0 and 2")
|
73
139
|
|
74
140
|
flags |= (1 << 12)
|
75
141
|
arr << delivery_mode
|
76
142
|
fmt << "C"
|
77
143
|
end
|
78
144
|
|
79
|
-
if priority
|
145
|
+
if (priority = properties[:priority])
|
80
146
|
priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
|
147
|
+
|
81
148
|
flags |= (1 << 11)
|
82
149
|
arr << priority
|
83
150
|
fmt << "C"
|
84
151
|
end
|
85
152
|
|
86
|
-
if correlation_id
|
87
|
-
|
153
|
+
if (correlation_id = properties[:correlation_id])
|
154
|
+
correlation_id.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
|
88
155
|
|
89
156
|
flags |= (1 << 10)
|
90
157
|
arr << correlation_id.bytesize << correlation_id
|
91
158
|
fmt << "Ca*"
|
92
159
|
end
|
93
160
|
|
94
|
-
if reply_to
|
161
|
+
if (reply_to = properties[:reply_to])
|
95
162
|
reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
|
96
163
|
|
97
164
|
flags |= (1 << 9)
|
@@ -99,8 +166,8 @@ module AMQP
|
|
99
166
|
fmt << "Ca*"
|
100
167
|
end
|
101
168
|
|
102
|
-
if expiration
|
103
|
-
|
169
|
+
if (expiration = properties[:expiration])
|
170
|
+
expiration = expiration.to_s if expiration.is_a?(Integer)
|
104
171
|
expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
|
105
172
|
|
106
173
|
flags |= (1 << 8)
|
@@ -108,7 +175,7 @@ module AMQP
|
|
108
175
|
fmt << "Ca*"
|
109
176
|
end
|
110
177
|
|
111
|
-
if message_id
|
178
|
+
if (message_id = properties[:message_id])
|
112
179
|
message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
|
113
180
|
|
114
181
|
flags |= (1 << 7)
|
@@ -116,7 +183,7 @@ module AMQP
|
|
116
183
|
fmt << "Ca*"
|
117
184
|
end
|
118
185
|
|
119
|
-
if timestamp
|
186
|
+
if (timestamp = properties[:timestamp])
|
120
187
|
timestamp.is_a?(Integer) || timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be an Integer or a Time")
|
121
188
|
|
122
189
|
flags |= (1 << 6)
|
@@ -124,7 +191,7 @@ module AMQP
|
|
124
191
|
fmt << "Q>"
|
125
192
|
end
|
126
193
|
|
127
|
-
if type
|
194
|
+
if (type = properties[:type])
|
128
195
|
type.is_a?(String) || raise(ArgumentError, "type must be a string")
|
129
196
|
|
130
197
|
flags |= (1 << 5)
|
@@ -132,7 +199,7 @@ module AMQP
|
|
132
199
|
fmt << "Ca*"
|
133
200
|
end
|
134
201
|
|
135
|
-
if user_id
|
202
|
+
if (user_id = properties[:user_id])
|
136
203
|
user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
|
137
204
|
|
138
205
|
flags |= (1 << 4)
|
@@ -140,7 +207,7 @@ module AMQP
|
|
140
207
|
fmt << "Ca*"
|
141
208
|
end
|
142
209
|
|
143
|
-
if app_id
|
210
|
+
if (app_id = properties[:app_id])
|
144
211
|
app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
|
145
212
|
|
146
213
|
flags |= (1 << 3)
|
@@ -149,87 +216,87 @@ module AMQP
|
|
149
216
|
end
|
150
217
|
|
151
218
|
arr[0] = flags
|
152
|
-
arr.pack(fmt
|
219
|
+
arr.pack(fmt)
|
153
220
|
end
|
154
221
|
|
155
222
|
# Decode a byte array
|
156
223
|
# @return [Properties]
|
157
|
-
def self.decode(bytes)
|
158
|
-
|
159
|
-
flags = bytes.unpack1("S>")
|
160
|
-
pos
|
224
|
+
def self.decode(bytes, pos = 0)
|
225
|
+
p = new
|
226
|
+
flags = bytes.byteslice(pos, 2).unpack1("S>")
|
227
|
+
pos += 2
|
161
228
|
if (flags & 0x8000).positive?
|
162
|
-
len = bytes
|
229
|
+
len = bytes.getbyte(pos)
|
163
230
|
pos += 1
|
164
|
-
|
231
|
+
p.content_type = bytes.byteslice(pos, len).force_encoding("utf-8")
|
165
232
|
pos += len
|
166
233
|
end
|
167
234
|
if (flags & 0x4000).positive?
|
168
|
-
len = bytes
|
235
|
+
len = bytes.getbyte(pos)
|
169
236
|
pos += 1
|
170
|
-
|
237
|
+
p.content_encoding = bytes.byteslice(pos, len).force_encoding("utf-8")
|
171
238
|
pos += len
|
172
239
|
end
|
173
240
|
if (flags & 0x2000).positive?
|
174
241
|
len = bytes.byteslice(pos, 4).unpack1("L>")
|
175
242
|
pos += 4
|
176
|
-
|
243
|
+
p.headers = Table.decode(bytes.byteslice(pos, len))
|
177
244
|
pos += len
|
178
245
|
end
|
179
246
|
if (flags & 0x1000).positive?
|
180
|
-
|
247
|
+
p.delivery_mode = bytes.getbyte(pos)
|
181
248
|
pos += 1
|
182
249
|
end
|
183
250
|
if (flags & 0x0800).positive?
|
184
|
-
|
251
|
+
p.priority = bytes.getbyte(pos)
|
185
252
|
pos += 1
|
186
253
|
end
|
187
254
|
if (flags & 0x0400).positive?
|
188
|
-
len = bytes
|
255
|
+
len = bytes.getbyte(pos)
|
189
256
|
pos += 1
|
190
|
-
|
257
|
+
p.correlation_id = bytes.byteslice(pos, len).force_encoding("utf-8")
|
191
258
|
pos += len
|
192
259
|
end
|
193
260
|
if (flags & 0x0200).positive?
|
194
|
-
len = bytes
|
261
|
+
len = bytes.getbyte(pos)
|
195
262
|
pos += 1
|
196
|
-
|
263
|
+
p.reply_to = bytes.byteslice(pos, len).force_encoding("utf-8")
|
197
264
|
pos += len
|
198
265
|
end
|
199
266
|
if (flags & 0x0100).positive?
|
200
|
-
len = bytes
|
267
|
+
len = bytes.getbyte(pos)
|
201
268
|
pos += 1
|
202
|
-
|
269
|
+
p.expiration = bytes.byteslice(pos, len).force_encoding("utf-8")
|
203
270
|
pos += len
|
204
271
|
end
|
205
272
|
if (flags & 0x0080).positive?
|
206
|
-
len = bytes
|
273
|
+
len = bytes.getbyte(pos)
|
207
274
|
pos += 1
|
208
|
-
|
275
|
+
p.message_id = bytes.byteslice(pos, len).force_encoding("utf-8")
|
209
276
|
pos += len
|
210
277
|
end
|
211
278
|
if (flags & 0x0040).positive?
|
212
|
-
|
279
|
+
p.timestamp = Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))
|
213
280
|
pos += 8
|
214
281
|
end
|
215
282
|
if (flags & 0x0020).positive?
|
216
|
-
len = bytes
|
283
|
+
len = bytes.getbyte(pos)
|
217
284
|
pos += 1
|
218
|
-
|
285
|
+
p.type = bytes.byteslice(pos, len).force_encoding("utf-8")
|
219
286
|
pos += len
|
220
287
|
end
|
221
288
|
if (flags & 0x0010).positive?
|
222
|
-
len = bytes
|
289
|
+
len = bytes.getbyte(pos)
|
223
290
|
pos += 1
|
224
|
-
|
291
|
+
p.user_id = bytes.byteslice(pos, len).force_encoding("utf-8")
|
225
292
|
pos += len
|
226
293
|
end
|
227
294
|
if (flags & 0x0008).positive?
|
228
|
-
len = bytes
|
295
|
+
len = bytes.getbyte(pos)
|
229
296
|
pos += 1
|
230
|
-
|
297
|
+
p.app_id = bytes.byteslice(pos, len).force_encoding("utf-8")
|
231
298
|
end
|
232
|
-
|
299
|
+
p
|
233
300
|
end
|
234
301
|
end
|
235
302
|
end
|
data/lib/amqp/client/queue.rb
CHANGED
@@ -11,30 +11,24 @@ module AMQP
|
|
11
11
|
@name = name
|
12
12
|
end
|
13
13
|
|
14
|
-
# Publish to the queue
|
15
|
-
# @param
|
16
|
-
# @
|
17
|
-
# @
|
18
|
-
# @
|
19
|
-
# @option properties [Hash<String, Object>] headers Custom headers
|
20
|
-
# @option properties [Integer] delivery_mode 2 for persisted message, transient messages for all other values
|
21
|
-
# @option properties [Integer] priority A priority of the message (between 0 and 255)
|
22
|
-
# @option properties [Integer] correlation_id A correlation id, most often used used for RPC communication
|
23
|
-
# @option properties [String] reply_to Queue to reply RPC responses to
|
24
|
-
# @option properties [Integer, String] expiration Number of seconds the message will stay in the queue
|
25
|
-
# @option properties [String] message_id Can be used to uniquely identify the message, e.g. for deduplication
|
26
|
-
# @option properties [Date] timestamp Often used for the time the message was originally generated
|
27
|
-
# @option properties [String] type Can indicate what kind of message this is
|
28
|
-
# @option properties [String] user_id Can be used to verify that this is the user that published the message
|
29
|
-
# @option properties [String] app_id Can be used to indicates which app that generated the message
|
30
|
-
# @return [Queue] self
|
14
|
+
# Publish to the queue, wait for confirm
|
15
|
+
# @param (see Client#publish)
|
16
|
+
# @option (see Client#publish)
|
17
|
+
# @raise (see Client#publish)
|
18
|
+
# @return [self]
|
31
19
|
def publish(body, **properties)
|
32
20
|
@client.publish(body, "", @name, **properties)
|
33
21
|
self
|
34
22
|
end
|
35
23
|
|
36
24
|
# Subscribe/consume from the queue
|
37
|
-
# @
|
25
|
+
# @param no_ack [Boolean] When false messages have to be manually acknowledged (or rejected)
|
26
|
+
# @param prefetch [Integer] Specify how many messages to prefetch for consumers with no_ack is false
|
27
|
+
# @param worker_threads [Integer] Number of threads processing messages,
|
28
|
+
# 0 means that the thread calling this method will be blocked
|
29
|
+
# @param arguments [Hash] Custom arguments to the consumer
|
30
|
+
# @yield [Message] Delivered message from the queue
|
31
|
+
# @return [self]
|
38
32
|
def subscribe(no_ack: false, prefetch: 1, worker_threads: 1, arguments: {}, &blk)
|
39
33
|
@client.subscribe(@name, no_ack: no_ack, prefetch: prefetch, worker_threads: worker_threads, arguments: arguments, &blk)
|
40
34
|
self
|
@@ -44,7 +38,7 @@ module AMQP
|
|
44
38
|
# @param exchange [String] Name of the exchange to bind to
|
45
39
|
# @param binding_key [String] Binding key on which messages that match might be routed (depending on exchange type)
|
46
40
|
# @param arguments [Hash] Message headers to match on (only relevant for header exchanges)
|
47
|
-
# @return [
|
41
|
+
# @return [self]
|
48
42
|
def bind(exchange, binding_key, arguments: {})
|
49
43
|
@client.bind(@name, exchange, binding_key, arguments: arguments)
|
50
44
|
self
|
@@ -54,14 +48,14 @@ module AMQP
|
|
54
48
|
# @param exchange [String] Name of the exchange to unbind from
|
55
49
|
# @param binding_key [String] Binding key which the queue is bound to the exchange with
|
56
50
|
# @param arguments [Hash] Arguments matching the binding that's being removed
|
57
|
-
# @return [
|
51
|
+
# @return [self]
|
58
52
|
def unbind(exchange, binding_key, arguments: {})
|
59
53
|
@client.unbind(@name, exchange, binding_key, arguments: arguments)
|
60
54
|
self
|
61
55
|
end
|
62
56
|
|
63
57
|
# Purge/empty the queue
|
64
|
-
# @return [
|
58
|
+
# @return [self]
|
65
59
|
def purge
|
66
60
|
@client.purge(@name)
|
67
61
|
self
|
data/lib/amqp/client/table.rb
CHANGED
@@ -5,32 +5,34 @@ module AMQP
|
|
5
5
|
# Encode and decode an AMQP table to/from hash, only used internally
|
6
6
|
# @api private
|
7
7
|
module Table
|
8
|
-
module_function
|
9
|
-
|
10
8
|
# Encodes a hash into a byte array
|
9
|
+
# @param hash [Hash]
|
11
10
|
# @return [String] Byte array
|
12
|
-
def encode(hash)
|
13
|
-
|
14
|
-
|
11
|
+
def self.encode(hash)
|
12
|
+
return "" if hash.empty?
|
13
|
+
|
14
|
+
arr = []
|
15
|
+
fmt = String.new
|
16
|
+
hash.each do |k, value|
|
15
17
|
key = k.to_s
|
16
|
-
|
17
|
-
|
18
|
+
arr.push(key.bytesize, key)
|
19
|
+
fmt << "Ca*"
|
20
|
+
encode_field(value, arr, fmt)
|
18
21
|
end
|
19
|
-
|
22
|
+
arr.pack(fmt)
|
20
23
|
end
|
21
24
|
|
22
25
|
# Decodes an AMQP table into a hash
|
23
26
|
# @return [Hash<String, Object>]
|
24
|
-
def decode(bytes)
|
27
|
+
def self.decode(bytes)
|
25
28
|
hash = {}
|
26
29
|
pos = 0
|
27
30
|
while pos < bytes.bytesize
|
28
|
-
key_len = bytes
|
31
|
+
key_len = bytes.getbyte(pos)
|
29
32
|
pos += 1
|
30
33
|
key = bytes.byteslice(pos, key_len).force_encoding("utf-8")
|
31
34
|
pos += key_len
|
32
|
-
|
33
|
-
len, value = decode_field(rest)
|
35
|
+
len, value = decode_field(bytes, pos)
|
34
36
|
pos += len + 1
|
35
37
|
hash[key] = value
|
36
38
|
end
|
@@ -38,43 +40,58 @@ module AMQP
|
|
38
40
|
end
|
39
41
|
|
40
42
|
# Encoding a single value in a table
|
43
|
+
# @return [nil]
|
41
44
|
# @api private
|
42
|
-
def encode_field(value)
|
45
|
+
def self.encode_field(value, arr, fmt)
|
43
46
|
case value
|
44
47
|
when Integer
|
45
48
|
if value > 2**31
|
46
|
-
|
49
|
+
arr.push("l", value)
|
50
|
+
fmt << "aq>"
|
47
51
|
else
|
48
|
-
|
52
|
+
arr.push("I", value)
|
53
|
+
fmt << "al>"
|
49
54
|
end
|
50
55
|
when Float
|
51
|
-
|
56
|
+
arr.push("d", value)
|
57
|
+
fmt << "aG"
|
52
58
|
when String
|
53
|
-
|
59
|
+
arr.push("S", value.bytesize, value)
|
60
|
+
fmt << "aL>a*"
|
54
61
|
when Time
|
55
|
-
|
62
|
+
arr.push("T", value.to_i)
|
63
|
+
fmt << "aQ>"
|
56
64
|
when Array
|
57
|
-
|
58
|
-
|
65
|
+
value_arr = []
|
66
|
+
value_fmt = String.new
|
67
|
+
value.each { |e| encode_field(e, value_arr, value_fmt) }
|
68
|
+
bytes = value_arr.pack(value_fmt)
|
69
|
+
arr.push("A", bytes.bytesize, bytes)
|
70
|
+
fmt << "aL>a*"
|
59
71
|
when Hash
|
60
72
|
bytes = Table.encode(value)
|
61
|
-
|
73
|
+
arr.push("F", bytes.bytesize, bytes)
|
74
|
+
fmt << "aL>a*"
|
62
75
|
when true
|
63
|
-
|
76
|
+
arr.push("t", 1)
|
77
|
+
fmt << "aC"
|
64
78
|
when false
|
65
|
-
|
79
|
+
arr.push("t", 0)
|
80
|
+
fmt << "aC"
|
66
81
|
when nil
|
67
|
-
|
82
|
+
arr << "V"
|
83
|
+
fmt << "a"
|
68
84
|
else raise ArgumentError, "unsupported table field type: #{value.class}"
|
69
85
|
end
|
86
|
+
nil
|
70
87
|
end
|
71
88
|
|
72
89
|
# Decodes a single value
|
73
|
-
# @return [Array<Integer, Object>] Bytes read and the parsed
|
90
|
+
# @return [Array<Integer, Object>] Bytes read and the parsed value
|
74
91
|
# @api private
|
75
|
-
def decode_field(bytes)
|
76
|
-
type = bytes[
|
77
|
-
pos
|
92
|
+
def self.decode_field(bytes, pos)
|
93
|
+
type = bytes[pos]
|
94
|
+
pos += 1
|
78
95
|
case type
|
79
96
|
when "S"
|
80
97
|
len = bytes.byteslice(pos, 4).unpack1("L>")
|
@@ -86,15 +103,17 @@ module AMQP
|
|
86
103
|
[4 + len, decode(bytes.byteslice(pos, len))]
|
87
104
|
when "A"
|
88
105
|
len = bytes.byteslice(pos, 4).unpack1("L>")
|
106
|
+
pos += 4
|
107
|
+
array_end = pos + len
|
89
108
|
a = []
|
90
|
-
while pos <
|
91
|
-
length, value = decode_field(bytes
|
109
|
+
while pos < array_end
|
110
|
+
length, value = decode_field(bytes, pos)
|
92
111
|
pos += length + 1
|
93
112
|
a << value
|
94
113
|
end
|
95
114
|
[4 + len, a]
|
96
115
|
when "t"
|
97
|
-
[1, bytes
|
116
|
+
[1, bytes.getbyte(pos) == 1]
|
98
117
|
when "b"
|
99
118
|
[1, bytes.byteslice(pos, 1).unpack1("c")]
|
100
119
|
when "B"
|
@@ -114,7 +133,7 @@ module AMQP
|
|
114
133
|
when "d"
|
115
134
|
[8, bytes.byteslice(pos, 8).unpack1("G")]
|
116
135
|
when "D"
|
117
|
-
scale = bytes
|
136
|
+
scale = bytes.getbyte(pos)
|
118
137
|
pos += 1
|
119
138
|
value = bytes.byteslice(pos, 4).unpack1("L>")
|
120
139
|
d = value / 10**scale
|