amqp-client 0.3.0 → 1.1.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.
@@ -1,15 +1,111 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AMQP
4
- Message = Struct.new(:channel, :delivery_tag, :exchange_name, :routing_key, :properties, :body, :redelivered, :consumer_tag) do
5
- def ack
6
- channel.basic_ack(delivery_tag)
4
+ class Client
5
+ # A message delivered from the broker
6
+ class Message
7
+ # @api private
8
+ def initialize(channel, consumer_tag, delivery_tag, exchange, routing_key, redelivered)
9
+ @channel = channel
10
+ @consumer_tag = consumer_tag
11
+ @delivery_tag = delivery_tag
12
+ @exchange = exchange
13
+ @routing_key = routing_key
14
+ @redelivered = redelivered
15
+ @properties = nil
16
+ @body = ""
17
+ end
18
+
19
+ # The channel the message was deliviered to
20
+ # @return [Connection::Channel]
21
+ attr_reader :channel
22
+
23
+ # The tag of the consumer the message was deliviered to
24
+ # @return [String]
25
+ # @return [nil] If the message was polled and not deliviered to a consumer
26
+ attr_reader :consumer_tag
27
+
28
+ # The delivery tag of the message, used for acknowledge or reject the message
29
+ # @return [Integer]
30
+ attr_reader :delivery_tag
31
+
32
+ # Name of the exchange the message was published to
33
+ # @return [String]
34
+ attr_reader :exchange
35
+
36
+ # The routing key the message was published with
37
+ # @return [String]
38
+ attr_reader :routing_key
39
+
40
+ # True if the message have been delivered before
41
+ # @return [Boolean]
42
+ attr_reader :redelivered
43
+
44
+ # Message properties
45
+ # @return [Properties]
46
+ attr_accessor :properties
47
+
48
+ # The message body
49
+ # @return [String]
50
+ attr_accessor :body
51
+
52
+ # Acknowledge the message
53
+ # @return [nil]
54
+ def ack
55
+ @channel.basic_ack(@delivery_tag)
56
+ end
57
+
58
+ # Reject the message
59
+ # @param requeue [Boolean] If true the message will be put back into the queue again, ready to be redelivered
60
+ # @return [nil]
61
+ def reject(requeue: false)
62
+ @channel.basic_reject(@delivery_tag, requeue: requeue)
63
+ end
64
+
65
+ # @see #exchange
66
+ # @deprecated
67
+ # @!attribute [r] exchange_name
68
+ # @return [String]
69
+ def exchange_name
70
+ @exchange
71
+ end
7
72
  end
8
73
 
9
- def reject(requeue: false)
10
- channel.basic_reject(delivery_tag, requeue)
74
+ # A published message returned by the broker due to some error
75
+ class ReturnMessage
76
+ # @api private
77
+ def initialize(reply_code, reply_text, exchange, routing_key)
78
+ @reply_code = reply_code
79
+ @reply_text = reply_text
80
+ @exchange = exchange
81
+ @routing_key = routing_key
82
+ @properties = nil
83
+ @body = ""
84
+ end
85
+
86
+ # Error code
87
+ # @return [Integer]
88
+ attr_reader :reply_code
89
+
90
+ # Description on why the message was returned
91
+ # @return [String]
92
+ attr_reader :reply_text
93
+
94
+ # Name of the exchange the message was published to
95
+ # @return [String]
96
+ attr_reader :exchange
97
+
98
+ # The routing key the message was published with
99
+ # @return [String]
100
+ attr_reader :routing_key
101
+
102
+ # Message properties
103
+ # @return [Properties]
104
+ attr_accessor :properties
105
+
106
+ # The message body
107
+ # @return [String]
108
+ attr_accessor :body
11
109
  end
12
110
  end
13
-
14
- ReturnMessage = Struct.new(:reply_code, :reply_text, :exchange, :routing_key, :properties, :body)
15
111
  end
@@ -3,200 +3,264 @@
3
3
  require_relative "./table"
4
4
 
5
5
  module AMQP
6
- # Encode/decode AMQP Properties
7
- Properties = Struct.new(:content_type, :content_encoding, :headers, :delivery_mode, :priority, :correlation_id,
8
- :reply_to, :expiration, :message_id, :timestamp, :type, :user_id, :app_id,
9
- keyword_init: true) do
10
- def encode
11
- flags = 0
12
- arr = [flags]
13
- fmt = String.new("S>")
14
-
15
- if content_type
16
- content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
17
-
18
- flags |= (1 << 15)
19
- arr << content_type.bytesize << content_type
20
- fmt << "Ca*"
6
+ class Client
7
+ # Encode/decode AMQP Properties
8
+ class Properties
9
+ def initialize(content_type: nil, content_encoding: nil, headers: nil, delivery_mode: nil, priority: nil, correlation_id: nil,
10
+ reply_to: nil, expiration: nil, message_id: nil, timestamp: nil, type: nil, user_id: nil, app_id: nil)
11
+ @content_type = content_type
12
+ @content_encoding = content_encoding
13
+ @headers = headers
14
+ @delivery_mode = delivery_mode
15
+ @priority = priority
16
+ @correlation_id = correlation_id
17
+ @reply_to = reply_to
18
+ @expiration = expiration
19
+ @message_id = message_id
20
+ @timestamp = timestamp
21
+ @type = type
22
+ @user_id = user_id
23
+ @app_id = app_id
21
24
  end
22
25
 
23
- if content_encoding
24
- content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
26
+ # Content type of the message body
27
+ # @return [String, nil]
28
+ attr_accessor :content_type
29
+ # Content encoding of the body
30
+ # @return [String, nil]
31
+ attr_accessor :content_encoding
32
+ # Custom headers
33
+ # @return [Hash<String, Object>, nil]
34
+ attr_accessor :headers
35
+ # 2 for persisted message, transient messages for all other values
36
+ # @return [Integer, nil]
37
+ attr_accessor :delivery_mode
38
+ # A priority of the message (between 0 and 255)
39
+ # @return [Integer, nil]
40
+ attr_accessor :priority
41
+ # A correlation id, most often used used for RPC communication
42
+ # @return [Integer, nil]
43
+ attr_accessor :correlation_id
44
+ # Queue to reply RPC responses to
45
+ # @return [String, nil]
46
+ attr_accessor :reply_to
47
+ # Number of seconds the message will stay in the queue
48
+ # @return [Integer]
49
+ # @return [String]
50
+ # @return [nil]
51
+ attr_accessor :expiration
52
+ # @return [String, nil]
53
+ attr_accessor :message_id
54
+ # User-definable, but often used for the time the message was originally generated
55
+ # @return [Date, nil]
56
+ attr_accessor :timestamp
57
+ # User-definable, but can can indicate what kind of message this is
58
+ # @return [String, nil]
59
+ attr_accessor :type
60
+ # User-definable, but can be used to verify that this is the user that published the message
61
+ # @return [String, nil]
62
+ attr_accessor :user_id
63
+ # User-definable, but often indicates which app that generated the message
64
+ # @return [String, nil]
65
+ attr_accessor :app_id
25
66
 
26
- flags |= (1 << 14)
27
- arr << content_encoding.bytesize << content_encoding
28
- fmt << "Ca*"
29
- end
67
+ # Encode properties into a byte array
68
+ # @return [String] byte array
69
+ def encode
70
+ flags = 0
71
+ arr = [flags]
72
+ fmt = StringIO.new(String.new("S>", capacity: 35))
73
+ fmt.pos = 2
30
74
 
31
- if headers
32
- headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
75
+ if content_type
76
+ content_type.is_a?(String) || raise(ArgumentError, "content_type must be a string")
33
77
 
34
- flags |= (1 << 13)
35
- tbl = Table.encode(headers)
36
- arr << tbl.bytesize << tbl
37
- fmt << "L>a*"
38
- end
78
+ flags |= (1 << 15)
79
+ arr << content_type.bytesize << content_type
80
+ fmt << "Ca*"
81
+ end
39
82
 
40
- if delivery_mode
41
- headers.is_a?(Integer) || raise(ArgumentError, "delivery_mode must be an int")
83
+ if content_encoding
84
+ content_encoding.is_a?(String) || raise(ArgumentError, "content_encoding must be a string")
42
85
 
43
- flags |= (1 << 12)
44
- arr << delivery_mode
45
- fmt << "C"
46
- end
86
+ flags |= (1 << 14)
87
+ arr << content_encoding.bytesize << content_encoding
88
+ fmt << "Ca*"
89
+ end
47
90
 
48
- if priority
49
- priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
50
- flags |= (1 << 11)
51
- arr << priority
52
- fmt << "C"
53
- end
91
+ if headers
92
+ headers.is_a?(Hash) || raise(ArgumentError, "headers must be a hash")
54
93
 
55
- if correlation_id
56
- priority.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
94
+ flags |= (1 << 13)
95
+ tbl = Table.encode(headers)
96
+ arr << tbl.bytesize << tbl
97
+ fmt << "L>a*"
98
+ end
57
99
 
58
- flags |= (1 << 10)
59
- arr << correlation_id.bytesize << correlation_id
60
- fmt << "Ca*"
61
- end
100
+ if delivery_mode
101
+ 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")
62
103
 
63
- if reply_to
64
- reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
104
+ flags |= (1 << 12)
105
+ arr << delivery_mode
106
+ fmt << "C"
107
+ end
65
108
 
66
- flags |= (1 << 9)
67
- arr << reply_to.bytesize << reply_to
68
- fmt << "Ca*"
69
- end
109
+ if priority
110
+ priority.is_a?(Integer) || raise(ArgumentError, "priority must be an int")
111
+ flags |= (1 << 11)
112
+ arr << priority
113
+ fmt << "C"
114
+ end
70
115
 
71
- if expiration
72
- expiration = expiration.to_s if expiration.is_a?(Integer)
73
- expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
116
+ if correlation_id
117
+ priority.is_a?(String) || raise(ArgumentError, "correlation_id must be a string")
74
118
 
75
- flags |= (1 << 8)
76
- arr << expiration.bytesize << expiration
77
- fmt << "Ca*"
78
- end
119
+ flags |= (1 << 10)
120
+ arr << correlation_id.bytesize << correlation_id
121
+ fmt << "Ca*"
122
+ end
79
123
 
80
- if message_id
81
- message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
124
+ if reply_to
125
+ reply_to.is_a?(String) || raise(ArgumentError, "reply_to must be a string")
82
126
 
83
- flags |= (1 << 7)
84
- arr << message_id.bytesize << message_id
85
- fmt << "Ca*"
86
- end
127
+ flags |= (1 << 9)
128
+ arr << reply_to.bytesize << reply_to
129
+ fmt << "Ca*"
130
+ end
87
131
 
88
- if timestamp
89
- timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be a time")
132
+ if expiration
133
+ self.expiration = expiration.to_s if expiration.is_a?(Integer)
134
+ expiration.is_a?(String) || raise(ArgumentError, "expiration must be a string or integer")
90
135
 
91
- flags |= (1 << 6)
92
- arr << timestamp.to_i
93
- fmt << "Q>"
94
- end
136
+ flags |= (1 << 8)
137
+ arr << expiration.bytesize << expiration
138
+ fmt << "Ca*"
139
+ end
95
140
 
96
- if type
97
- type.is_a?(String) || raise(ArgumentError, "type must be a string")
141
+ if message_id
142
+ message_id.is_a?(String) || raise(ArgumentError, "message_id must be a string")
98
143
 
99
- flags |= (1 << 5)
100
- arr << type.bytesize << type
101
- fmt << "Ca*"
102
- end
144
+ flags |= (1 << 7)
145
+ arr << message_id.bytesize << message_id
146
+ fmt << "Ca*"
147
+ end
103
148
 
104
- if user_id
105
- user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
149
+ if timestamp
150
+ timestamp.is_a?(Integer) || timestamp.is_a?(Time) || raise(ArgumentError, "timestamp must be an Integer or a Time")
106
151
 
107
- flags |= (1 << 4)
108
- arr << user_id.bytesize << user_id
109
- fmt << "Ca*"
110
- end
152
+ flags |= (1 << 6)
153
+ arr << timestamp.to_i
154
+ fmt << "Q>"
155
+ end
111
156
 
112
- if app_id
113
- app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
157
+ if type
158
+ type.is_a?(String) || raise(ArgumentError, "type must be a string")
114
159
 
115
- flags |= (1 << 3)
116
- arr << app_id.bytesize << app_id
117
- fmt << "Ca*"
118
- end
160
+ flags |= (1 << 5)
161
+ arr << type.bytesize << type
162
+ fmt << "Ca*"
163
+ end
119
164
 
120
- arr[0] = flags
121
- arr.pack(fmt)
122
- end
165
+ if user_id
166
+ user_id.is_a?(String) || raise(ArgumentError, "user_id must be a string")
123
167
 
124
- def self.decode(bytes)
125
- h = new
126
- flags = bytes.unpack1("S>")
127
- pos = 2
128
- if (flags & 0x8000).positive?
129
- len = bytes[pos].ord
130
- pos += 1
131
- h[:content_type] = bytes.byteslice(pos, len).force_encoding("utf-8")
132
- pos += len
133
- end
134
- if (flags & 0x4000).positive?
135
- len = bytes[pos].ord
136
- pos += 1
137
- h[:content_encoding] = bytes.byteslice(pos, len).force_encoding("utf-8")
138
- pos += len
139
- end
140
- if (flags & 0x2000).positive?
141
- len = bytes.byteslice(pos, 4).unpack1("L>")
142
- pos += 4
143
- h[:headers] = Table.decode(bytes.byteslice(pos, len))
144
- pos += len
145
- end
146
- if (flags & 0x1000).positive?
147
- h[:delivery_mode] = bytes[pos].ord
148
- pos += 1
149
- end
150
- if (flags & 0x0800).positive?
151
- h[:priority] = bytes[pos].ord
152
- pos += 1
153
- end
154
- if (flags & 0x0400).positive?
155
- len = bytes[pos].ord
156
- pos += 1
157
- h[:correlation_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
158
- pos += len
159
- end
160
- if (flags & 0x0200).positive?
161
- len = bytes[pos].ord
162
- pos += 1
163
- h[:reply_to] = bytes.byteslice(pos, len).force_encoding("utf-8")
164
- pos += len
165
- end
166
- if (flags & 0x0100).positive?
167
- len = bytes[pos].ord
168
- pos += 1
169
- h[:expiration] = bytes.byteslice(pos, len).force_encoding("utf-8")
170
- pos += len
171
- end
172
- if (flags & 0x0080).positive?
173
- len = bytes[pos].ord
174
- pos += 1
175
- h[:message_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
176
- pos += len
177
- end
178
- if (flags & 0x0040).positive?
179
- h[:timestamp] = Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))
180
- pos += 8
181
- end
182
- if (flags & 0x0020).positive?
183
- len = bytes[pos].ord
184
- pos += 1
185
- h[:type] = bytes.byteslice(pos, len).force_encoding("utf-8")
186
- pos += len
187
- end
188
- if (flags & 0x0010).positive?
189
- len = bytes[pos].ord
190
- pos += 1
191
- h[:user_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
192
- pos += len
168
+ flags |= (1 << 4)
169
+ arr << user_id.bytesize << user_id
170
+ fmt << "Ca*"
171
+ end
172
+
173
+ if app_id
174
+ app_id.is_a?(String) || raise(ArgumentError, "app_id must be a string")
175
+
176
+ flags |= (1 << 3)
177
+ arr << app_id.bytesize << app_id
178
+ fmt << "Ca*"
179
+ end
180
+
181
+ arr[0] = flags
182
+ arr.pack(fmt.string)
193
183
  end
194
- if (flags & 0x0008).positive?
195
- len = bytes[pos].ord
196
- pos += 1
197
- h[:app_id] = bytes.byteslice(pos, len).force_encoding("utf-8")
184
+
185
+ # Decode a byte array
186
+ # @return [Properties]
187
+ def self.decode(bytes)
188
+ p = new
189
+ flags = bytes.unpack1("S>")
190
+ pos = 2
191
+ if (flags & 0x8000).positive?
192
+ len = bytes.getbyte(pos)
193
+ pos += 1
194
+ p.content_type = bytes.byteslice(pos, len).force_encoding("utf-8")
195
+ pos += len
196
+ end
197
+ if (flags & 0x4000).positive?
198
+ len = bytes.getbyte(pos)
199
+ pos += 1
200
+ p.content_encoding = bytes.byteslice(pos, len).force_encoding("utf-8")
201
+ pos += len
202
+ end
203
+ if (flags & 0x2000).positive?
204
+ len = bytes.byteslice(pos, 4).unpack1("L>")
205
+ pos += 4
206
+ p.headers = Table.decode(bytes.byteslice(pos, len))
207
+ pos += len
208
+ end
209
+ if (flags & 0x1000).positive?
210
+ p.delivery_mode = bytes.getbyte(pos)
211
+ pos += 1
212
+ end
213
+ if (flags & 0x0800).positive?
214
+ p.priority = bytes.getbyte(pos)
215
+ pos += 1
216
+ end
217
+ if (flags & 0x0400).positive?
218
+ len = bytes.getbyte(pos)
219
+ pos += 1
220
+ p.correlation_id = bytes.byteslice(pos, len).force_encoding("utf-8")
221
+ pos += len
222
+ end
223
+ if (flags & 0x0200).positive?
224
+ len = bytes.getbyte(pos)
225
+ pos += 1
226
+ p.reply_to = bytes.byteslice(pos, len).force_encoding("utf-8")
227
+ pos += len
228
+ end
229
+ if (flags & 0x0100).positive?
230
+ len = bytes.getbyte(pos)
231
+ pos += 1
232
+ p.expiration = bytes.byteslice(pos, len).force_encoding("utf-8")
233
+ pos += len
234
+ end
235
+ if (flags & 0x0080).positive?
236
+ len = bytes.getbyte(pos)
237
+ pos += 1
238
+ p.message_id = bytes.byteslice(pos, len).force_encoding("utf-8")
239
+ pos += len
240
+ end
241
+ if (flags & 0x0040).positive?
242
+ p.timestamp = Time.at(bytes.byteslice(pos, 8).unpack1("Q>"))
243
+ pos += 8
244
+ end
245
+ if (flags & 0x0020).positive?
246
+ len = bytes.getbyte(pos)
247
+ pos += 1
248
+ p.type = bytes.byteslice(pos, len).force_encoding("utf-8")
249
+ pos += len
250
+ end
251
+ if (flags & 0x0010).positive?
252
+ len = bytes.getbyte(pos)
253
+ pos += 1
254
+ p.user_id = bytes.byteslice(pos, len).force_encoding("utf-8")
255
+ pos += len
256
+ end
257
+ if (flags & 0x0008).positive?
258
+ len = bytes.getbyte(pos)
259
+ pos += 1
260
+ p.app_id = bytes.byteslice(pos, len).force_encoding("utf-8")
261
+ end
262
+ p
198
263
  end
199
- h
200
264
  end
201
265
  end
202
266
  end