amqp-client 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +7 -3
- data/lib/amqp/client/connection.rb +2 -2
- data/lib/amqp/client/{frames.rb → frame_bytes.rb} +1 -1
- data/lib/amqp/client/properties.rb +69 -35
- data/lib/amqp/client/table.rb +46 -25
- data/lib/amqp/client/version.rb +1 -1
- data/lib/amqp/client.rb +15 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e248898d176ad9c3d785226ed37c0335052ef701ee16a16bff483089be65dd8
|
4
|
+
data.tar.gz: 89b58399edb11b30b05db86e9e4b0a617438e3ed52e63b3e76ae3a4c8cb361fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4500bec819ea9de9546f001eed682ac040ea3c2f93904dabb24afdbbda966d3352d8b1e33687a82c482ce835e7d650172dd906d9ef4ec2efc17f1dea31947314
|
7
|
+
data.tar.gz: 62ea49dc2782122146dd2613cd51b1c4132b3eef9d48c5573e9369b0af96042018f104a84cfe2aa2f173cf6d10f5ca3e427baa3ef61faed7029cd7328148a762
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.1.1] - 2021-09-15
|
4
|
+
|
5
|
+
- Added: Examples in the documentation
|
6
|
+
- Added: Faster Properties and Table encoding and decoding
|
7
|
+
|
3
8
|
## [1.1.0] - 2021-09-08
|
4
9
|
|
5
10
|
- Fixed: Due to a race condition publishers could get stuck waiting for publish confirms
|
data/README.md
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
A modern AMQP 0-9-1 Ruby client. Very fast (just as fast as the Java client, and >4x than other Ruby clients), fully thread-safe, blocking operations and straight-forward error handling.
|
4
4
|
|
5
|
+
It's small, only ~1800 lines of code, and without any dependencies. Other Ruby clients are about 4 times bigger. But without trading functionallity.
|
6
|
+
|
7
|
+
It's safe by default, messages are published as persistent, and is waiting for confirmation from the broker. That can of course be disabled if performance is a priority.
|
8
|
+
|
5
9
|
## Support
|
6
10
|
|
7
11
|
The library is fully supported by [CloudAMQP](https://www.cloudamqp.com), the largest RabbitMQ hosting provider in the world. Open [an issue](https://github.com/cloudamqp/amqp-client.rb/issues) or [email our support](mailto:support@cloudamqp.com) if you have problems or questions.
|
@@ -31,10 +35,10 @@ ch = conn.channel
|
|
31
35
|
q = ch.queue_declare
|
32
36
|
|
33
37
|
# Publish a message to said queue
|
34
|
-
ch.
|
38
|
+
ch.basic_publish_confirm "Hello World!", "", q.queue_name, persistent: true
|
35
39
|
|
36
40
|
# Poll the queue for a message
|
37
|
-
msg = ch.basic_get
|
41
|
+
msg = ch.basic_get(q.queue_name)
|
38
42
|
|
39
43
|
# Print the message's body to STDOUT
|
40
44
|
puts msg.body
|
@@ -104,7 +108,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
104
108
|
|
105
109
|
## Contributing
|
106
110
|
|
107
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/cloudamqp/amqp-client.rb
|
111
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/cloudamqp/amqp-client.rb](https://github.com/cloudamqp/amqp-client.rb/)
|
108
112
|
|
109
113
|
## License
|
110
114
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "socket"
|
4
4
|
require "uri"
|
5
5
|
require "openssl"
|
6
|
-
require_relative "./
|
6
|
+
require_relative "./frame_bytes"
|
7
7
|
require_relative "./channel"
|
8
8
|
require_relative "./errors"
|
9
9
|
|
@@ -350,7 +350,7 @@ module AMQP
|
|
350
350
|
end
|
351
351
|
when 2 # header
|
352
352
|
body_size = buf.unpack1("@4 Q>")
|
353
|
-
properties = Properties.decode(buf
|
353
|
+
properties = Properties.decode(buf, 12)
|
354
354
|
@channels[channel_id].header_delivered body_size, properties
|
355
355
|
when 3 # body
|
356
356
|
@channels[channel_id].body_delivered buf
|
@@ -23,56 +23,90 @@ module AMQP
|
|
23
23
|
@app_id = app_id
|
24
24
|
end
|
25
25
|
|
26
|
+
# Properties as a Hash
|
27
|
+
# @return [Hash] Properties
|
28
|
+
def to_h
|
29
|
+
{
|
30
|
+
content_type: content_type,
|
31
|
+
content_encoding: content_encoding,
|
32
|
+
headers: headers,
|
33
|
+
delivery_mode: delivery_mode,
|
34
|
+
priority: priority,
|
35
|
+
correlation_id: correlation_id,
|
36
|
+
reply_to: reply_to,
|
37
|
+
expiration: expiration,
|
38
|
+
message_id: message_id,
|
39
|
+
timestamp: timestamp,
|
40
|
+
type: type,
|
41
|
+
user_id: user_id,
|
42
|
+
app_id: app_id
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
alias to_hash to_h
|
47
|
+
|
48
|
+
# Returns true if two Property objects holds the same information
|
49
|
+
# @return [Boolean]
|
50
|
+
def ==(other)
|
51
|
+
return false unless other.is_a? self.class
|
52
|
+
|
53
|
+
instance_variables.all? { |v| instance_variable_get(v) == other.instance_variable_get(v) }
|
54
|
+
end
|
55
|
+
|
26
56
|
# Content type of the message body
|
27
57
|
# @return [String, nil]
|
28
58
|
attr_accessor :content_type
|
29
59
|
# Content encoding of the body
|
30
60
|
# @return [String, nil]
|
31
61
|
attr_accessor :content_encoding
|
32
|
-
#
|
62
|
+
# Headers, for applications and header exchange routing
|
33
63
|
# @return [Hash<String, Object>, nil]
|
34
64
|
attr_accessor :headers
|
35
|
-
#
|
36
|
-
# @
|
65
|
+
# Message persistent level
|
66
|
+
# @note The exchange and queue have to durable as well for the message to be persistent
|
67
|
+
# @return [1] Transient message
|
68
|
+
# @return [2] Persistent message
|
69
|
+
# @return [nil] Not specified (implicitly transient)
|
37
70
|
attr_accessor :delivery_mode
|
38
71
|
# A priority of the message (between 0 and 255)
|
39
72
|
# @return [Integer, nil]
|
40
73
|
attr_accessor :priority
|
41
|
-
#
|
42
|
-
# @return [
|
74
|
+
# Message correlation id, commonly used to correlate RPC requests and responses
|
75
|
+
# @return [String, nil]
|
43
76
|
attr_accessor :correlation_id
|
44
77
|
# Queue to reply RPC responses to
|
45
78
|
# @return [String, nil]
|
46
79
|
attr_accessor :reply_to
|
47
80
|
# Number of seconds the message will stay in the queue
|
48
|
-
# @return [
|
49
|
-
# @return [String]
|
50
|
-
# @return [nil]
|
81
|
+
# @return [String, nil]
|
51
82
|
attr_accessor :expiration
|
83
|
+
# Application message identifier
|
52
84
|
# @return [String, nil]
|
53
85
|
attr_accessor :message_id
|
54
|
-
#
|
86
|
+
# Message timestamp, often indicates when the message was originally generated
|
55
87
|
# @return [Date, nil]
|
56
88
|
attr_accessor :timestamp
|
57
|
-
#
|
89
|
+
# Message type name
|
58
90
|
# @return [String, nil]
|
59
91
|
attr_accessor :type
|
60
|
-
#
|
92
|
+
# The user that published the message
|
61
93
|
# @return [String, nil]
|
62
94
|
attr_accessor :user_id
|
63
|
-
#
|
95
|
+
# Name of application that generated the message
|
64
96
|
# @return [String, nil]
|
65
97
|
attr_accessor :app_id
|
66
98
|
|
67
99
|
# Encode properties into a byte array
|
100
|
+
# @param properties [Hash]
|
68
101
|
# @return [String] byte array
|
69
|
-
def encode
|
102
|
+
def self.encode(properties)
|
103
|
+
return "\x00\x00" if properties.empty?
|
104
|
+
|
70
105
|
flags = 0
|
71
106
|
arr = [flags]
|
72
|
-
fmt =
|
73
|
-
fmt.pos = 2
|
107
|
+
fmt = String.new("S>", capacity: 37)
|
74
108
|
|
75
|
-
if content_type
|
109
|
+
if (content_type = properties[:content_type])
|
76
110
|
content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
|
77
111
|
|
78
112
|
flags |= (1 << 15)
|
@@ -80,7 +114,7 @@ module AMQP
|
|
80
114
|
fmt << "Ca*"
|
81
115
|
end
|
82
116
|
|
83
|
-
if content_encoding
|
117
|
+
if (content_encoding = properties[:content_encoding])
|
84
118
|
content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
|
85
119
|
|
86
120
|
flags |= (1 << 14)
|
@@ -88,7 +122,7 @@ module AMQP
|
|
88
122
|
fmt << "Ca*"
|
89
123
|
end
|
90
124
|
|
91
|
-
if headers
|
125
|
+
if (headers = properties[:headers])
|
92
126
|
headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
|
93
127
|
|
94
128
|
flags |= (1 << 13)
|
@@ -97,31 +131,31 @@ module AMQP
|
|
97
131
|
fmt << "L>a*"
|
98
132
|
end
|
99
133
|
|
100
|
-
if delivery_mode
|
134
|
+
if (delivery_mode = properties[:delivery_mode])
|
101
135
|
delivery_mode.is_a?(Integer) || raise(ArgumentError, "delivery_mode must be an int")
|
102
|
-
delivery_mode.between?(0, 2) || raise(ArgumentError, "delivery_mode must be be between 0 and 2")
|
103
136
|
|
104
137
|
flags |= (1 << 12)
|
105
138
|
arr << delivery_mode
|
106
139
|
fmt << "C"
|
107
140
|
end
|
108
141
|
|
109
|
-
if priority
|
142
|
+
if (priority = properties[:priority])
|
110
143
|
priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
|
144
|
+
|
111
145
|
flags |= (1 << 11)
|
112
146
|
arr << priority
|
113
147
|
fmt << "C"
|
114
148
|
end
|
115
149
|
|
116
|
-
if correlation_id
|
117
|
-
|
150
|
+
if (correlation_id = properties[:correlation_id])
|
151
|
+
correlation_id.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
|
118
152
|
|
119
153
|
flags |= (1 << 10)
|
120
154
|
arr << correlation_id.bytesize << correlation_id
|
121
155
|
fmt << "Ca*"
|
122
156
|
end
|
123
157
|
|
124
|
-
if reply_to
|
158
|
+
if (reply_to = properties[:reply_to])
|
125
159
|
reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
|
126
160
|
|
127
161
|
flags |= (1 << 9)
|
@@ -129,8 +163,8 @@ module AMQP
|
|
129
163
|
fmt << "Ca*"
|
130
164
|
end
|
131
165
|
|
132
|
-
if expiration
|
133
|
-
|
166
|
+
if (expiration = properties[:expiration])
|
167
|
+
expiration = expiration.to_s if expiration.is_a?(Integer)
|
134
168
|
expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
|
135
169
|
|
136
170
|
flags |= (1 << 8)
|
@@ -138,7 +172,7 @@ module AMQP
|
|
138
172
|
fmt << "Ca*"
|
139
173
|
end
|
140
174
|
|
141
|
-
if message_id
|
175
|
+
if (message_id = properties[:message_id])
|
142
176
|
message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
|
143
177
|
|
144
178
|
flags |= (1 << 7)
|
@@ -146,7 +180,7 @@ module AMQP
|
|
146
180
|
fmt << "Ca*"
|
147
181
|
end
|
148
182
|
|
149
|
-
if timestamp
|
183
|
+
if (timestamp = properties[:timestamp])
|
150
184
|
timestamp.is_a?(Integer) || timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be an Integer or a Time")
|
151
185
|
|
152
186
|
flags |= (1 << 6)
|
@@ -154,7 +188,7 @@ module AMQP
|
|
154
188
|
fmt << "Q>"
|
155
189
|
end
|
156
190
|
|
157
|
-
if type
|
191
|
+
if (type = properties[:type])
|
158
192
|
type.is_a?(String) || raise(ArgumentError, "type must be a string")
|
159
193
|
|
160
194
|
flags |= (1 << 5)
|
@@ -162,7 +196,7 @@ module AMQP
|
|
162
196
|
fmt << "Ca*"
|
163
197
|
end
|
164
198
|
|
165
|
-
if user_id
|
199
|
+
if (user_id = properties[:user_id])
|
166
200
|
user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
|
167
201
|
|
168
202
|
flags |= (1 << 4)
|
@@ -170,7 +204,7 @@ module AMQP
|
|
170
204
|
fmt << "Ca*"
|
171
205
|
end
|
172
206
|
|
173
|
-
if app_id
|
207
|
+
if (app_id = properties[:app_id])
|
174
208
|
app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
|
175
209
|
|
176
210
|
flags |= (1 << 3)
|
@@ -179,15 +213,15 @@ module AMQP
|
|
179
213
|
end
|
180
214
|
|
181
215
|
arr[0] = flags
|
182
|
-
arr.pack(fmt
|
216
|
+
arr.pack(fmt)
|
183
217
|
end
|
184
218
|
|
185
219
|
# Decode a byte array
|
186
220
|
# @return [Properties]
|
187
|
-
def self.decode(bytes)
|
221
|
+
def self.decode(bytes, pos = 0)
|
188
222
|
p = new
|
189
|
-
flags = bytes.unpack1("S>")
|
190
|
-
pos
|
223
|
+
flags = bytes.byteslice(pos, 2).unpack1("S>")
|
224
|
+
pos += 2
|
191
225
|
if (flags & 0x8000).positive?
|
192
226
|
len = bytes.getbyte(pos)
|
193
227
|
pos += 1
|
data/lib/amqp/client/table.rb
CHANGED
@@ -6,15 +6,20 @@ module AMQP
|
|
6
6
|
# @api private
|
7
7
|
module Table
|
8
8
|
# Encodes a hash into a byte array
|
9
|
+
# @param hash [Hash]
|
9
10
|
# @return [String] Byte array
|
10
11
|
def self.encode(hash)
|
11
|
-
|
12
|
-
|
12
|
+
return "" if hash.empty?
|
13
|
+
|
14
|
+
arr = []
|
15
|
+
fmt = String.new
|
16
|
+
hash.each do |k, value|
|
13
17
|
key = k.to_s
|
14
|
-
|
15
|
-
|
18
|
+
arr.push(key.bytesize, key)
|
19
|
+
fmt << "Ca*"
|
20
|
+
encode_field(value, arr, fmt)
|
16
21
|
end
|
17
|
-
|
22
|
+
arr.pack(fmt)
|
18
23
|
end
|
19
24
|
|
20
25
|
# Decodes an AMQP table into a hash
|
@@ -27,8 +32,7 @@ module AMQP
|
|
27
32
|
pos += 1
|
28
33
|
key = bytes.byteslice(pos, key_len).force_encoding("utf-8")
|
29
34
|
pos += key_len
|
30
|
-
|
31
|
-
len, value = decode_field(rest)
|
35
|
+
len, value = decode_field(bytes, pos)
|
32
36
|
pos += len + 1
|
33
37
|
hash[key] = value
|
34
38
|
end
|
@@ -36,43 +40,58 @@ module AMQP
|
|
36
40
|
end
|
37
41
|
|
38
42
|
# Encoding a single value in a table
|
43
|
+
# @return [nil]
|
39
44
|
# @api private
|
40
|
-
def self.encode_field(value)
|
45
|
+
def self.encode_field(value, arr, fmt)
|
41
46
|
case value
|
42
47
|
when Integer
|
43
48
|
if value > 2**31
|
44
|
-
|
49
|
+
arr.push("l", value)
|
50
|
+
fmt << "aq>"
|
45
51
|
else
|
46
|
-
|
52
|
+
arr.push("I", value)
|
53
|
+
fmt << "al>"
|
47
54
|
end
|
48
55
|
when Float
|
49
|
-
|
56
|
+
arr.push("d", value)
|
57
|
+
fmt << "aG"
|
50
58
|
when String
|
51
|
-
|
59
|
+
arr.push("S", value.bytesize, value)
|
60
|
+
fmt << "aL>a*"
|
52
61
|
when Time
|
53
|
-
|
62
|
+
arr.push("T", value.to_i)
|
63
|
+
fmt << "aQ>"
|
54
64
|
when Array
|
55
|
-
|
56
|
-
|
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*"
|
57
71
|
when Hash
|
58
72
|
bytes = Table.encode(value)
|
59
|
-
|
73
|
+
arr.push("F", bytes.bytesize, bytes)
|
74
|
+
fmt << "aL>a*"
|
60
75
|
when true
|
61
|
-
|
76
|
+
arr.push("t", 1)
|
77
|
+
fmt << "aC"
|
62
78
|
when false
|
63
|
-
|
79
|
+
arr.push("t", 0)
|
80
|
+
fmt << "aC"
|
64
81
|
when nil
|
65
|
-
|
82
|
+
arr << "V"
|
83
|
+
fmt << "a"
|
66
84
|
else raise ArgumentError, "unsupported table field type: #{value.class}"
|
67
85
|
end
|
86
|
+
nil
|
68
87
|
end
|
69
88
|
|
70
89
|
# Decodes a single value
|
71
|
-
# @return [Array<Integer, Object>] Bytes read and the parsed
|
90
|
+
# @return [Array<Integer, Object>] Bytes read and the parsed value
|
72
91
|
# @api private
|
73
|
-
def self.decode_field(bytes)
|
74
|
-
type = bytes[
|
75
|
-
pos
|
92
|
+
def self.decode_field(bytes, pos)
|
93
|
+
type = bytes[pos]
|
94
|
+
pos += 1
|
76
95
|
case type
|
77
96
|
when "S"
|
78
97
|
len = bytes.byteslice(pos, 4).unpack1("L>")
|
@@ -84,9 +103,11 @@ module AMQP
|
|
84
103
|
[4 + len, decode(bytes.byteslice(pos, len))]
|
85
104
|
when "A"
|
86
105
|
len = bytes.byteslice(pos, 4).unpack1("L>")
|
106
|
+
pos += 4
|
107
|
+
array_end = pos + len
|
87
108
|
a = []
|
88
|
-
while pos <
|
89
|
-
length, value = decode_field(bytes
|
109
|
+
while pos < array_end
|
110
|
+
length, value = decode_field(bytes, pos)
|
90
111
|
pos += length + 1
|
91
112
|
a << value
|
92
113
|
end
|
data/lib/amqp/client/version.rb
CHANGED
data/lib/amqp/client.rb
CHANGED
@@ -38,12 +38,18 @@ module AMQP
|
|
38
38
|
# Establishes and returns a new AMQP connection
|
39
39
|
# @see Connection#initialize
|
40
40
|
# @return [Connection]
|
41
|
+
# @example
|
42
|
+
# connection = AMQP::Client.new("amqps://server.rmq.cloudamqp.com", connection_name: "My connection").connect
|
41
43
|
def connect(read_loop_thread: true)
|
42
44
|
Connection.new(@uri, read_loop_thread: read_loop_thread, **@options)
|
43
45
|
end
|
44
46
|
|
45
47
|
# Opens an AMQP connection using the high level API, will try to reconnect if successfully connected at first
|
46
48
|
# @return [self]
|
49
|
+
# @example
|
50
|
+
# amqp = AMQP::Client.new("amqps://server.rmq.cloudamqp.com")
|
51
|
+
# amqp.start
|
52
|
+
# amqp.queue("foobar")
|
47
53
|
def start
|
48
54
|
@stopped = false
|
49
55
|
Thread.new(connect(read_loop_thread: false)) do |conn|
|
@@ -95,6 +101,10 @@ module AMQP
|
|
95
101
|
# (it won't be deleted until at least one consumer has consumed from it)
|
96
102
|
# @param arguments [Hash] Custom arguments, such as queue-ttl etc.
|
97
103
|
# @return [Queue]
|
104
|
+
# @example
|
105
|
+
# amqp = AMQP::Client.new.start
|
106
|
+
# q = amqp.queue("foobar")
|
107
|
+
# q.publish("body")
|
98
108
|
def queue(name, durable: true, auto_delete: false, arguments: {})
|
99
109
|
raise ArgumentError, "Currently only supports named, durable queues" if name.empty?
|
100
110
|
|
@@ -108,6 +118,10 @@ module AMQP
|
|
108
118
|
|
109
119
|
# Declare an exchange and return a high level Exchange object
|
110
120
|
# @return [Exchange]
|
121
|
+
# @example
|
122
|
+
# amqp = AMQP::Client.new.start
|
123
|
+
# x = amqp.exchange("my.hash.exchange", "x-consistent-hash")
|
124
|
+
# x.publish("body", "routing-key")
|
111
125
|
def exchange(name, type, durable: true, auto_delete: false, internal: false, arguments: {})
|
112
126
|
@exchanges.fetch(name) do
|
113
127
|
with_connection do |conn|
|
@@ -172,9 +186,7 @@ module AMQP
|
|
172
186
|
with_connection do |conn|
|
173
187
|
ch = conn.channel
|
174
188
|
ch.basic_qos(prefetch)
|
175
|
-
ch.basic_consume(queue, no_ack: no_ack, worker_threads: worker_threads, arguments: arguments)
|
176
|
-
blk.call(msg)
|
177
|
-
end
|
189
|
+
ch.basic_consume(queue, no_ack: no_ack, worker_threads: worker_threads, arguments: arguments, &blk)
|
178
190
|
end
|
179
191
|
end
|
180
192
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amqp-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl Hörberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Work in progress
|
14
14
|
email:
|
@@ -37,7 +37,7 @@ files:
|
|
37
37
|
- lib/amqp/client/connection.rb
|
38
38
|
- lib/amqp/client/errors.rb
|
39
39
|
- lib/amqp/client/exchange.rb
|
40
|
-
- lib/amqp/client/
|
40
|
+
- lib/amqp/client/frame_bytes.rb
|
41
41
|
- lib/amqp/client/message.rb
|
42
42
|
- lib/amqp/client/properties.rb
|
43
43
|
- lib/amqp/client/queue.rb
|