aws-sdk-sqs 1.0.0.rc1

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.
@@ -0,0 +1,2 @@
1
+ # utility classes
2
+ require 'aws-sdk-sqs/queue_poller'
@@ -0,0 +1,23 @@
1
+ # WARNING ABOUT GENERATED CODE
2
+ #
3
+ # This file is generated. See the contributing for info on making contributions:
4
+ # https://github.com/aws/aws-sdk-ruby/blob/master/CONTRIBUTING.md
5
+ #
6
+ # WARNING ABOUT GENERATED CODE
7
+
8
+ module Aws
9
+ module SQS
10
+ module Errors
11
+
12
+ extend Aws::Errors::DynamicErrors
13
+
14
+ # Raised when calling #load or #data on a resource class that can not be
15
+ # loaded. This can happen when:
16
+ #
17
+ # * A resource class has identifiers, but no data attributes.
18
+ # * Resource data is only available when making an API call that
19
+ # enumerates all resources of that type.
20
+ class ResourceNotLoadable < RuntimeError; end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,166 @@
1
+ require 'openssl'
2
+
3
+ module Aws
4
+ module SQS
5
+ module Plugins
6
+ class Md5s < Seahorse::Client::Plugin
7
+
8
+ # @api private
9
+ class Handler < Seahorse::Client::Handler
10
+ def call(context)
11
+ @handler.call(context).on_success do |response|
12
+ case context.operation_name
13
+ when :send_message
14
+ validate_send_message(context, response)
15
+ when :send_message_batch
16
+ validate_send_message_batch(context, response)
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ TRANSPORT_TYPE_ENCODINGS = {
24
+ 'String' => 1,
25
+ 'Binary' => 2,
26
+ 'Number' => 1
27
+ }
28
+
29
+ DATA_TYPE = /\A(String|Binary|Number)(\..+)?\z/
30
+
31
+ NORMALIZED_ENCODING = Encoding::UTF_8
32
+
33
+ def validate_send_message(context, response)
34
+ body = context.params[:message_body]
35
+ attributes = context.params[:message_attributes]
36
+ validate_single_message(body, attributes, response)
37
+ end
38
+
39
+ def validate_send_message_batch(context, response)
40
+ context.params[:entries].each do |entry|
41
+ id = entry[:id]
42
+ body = entry[:message_body]
43
+ attributes = entry[:message_attributes]
44
+ message_response = response.successful.select { |r| r.id == id }[0]
45
+ unless message_response.nil?
46
+ validate_single_message(body, attributes, message_response)
47
+ end
48
+ end
49
+ end
50
+
51
+ def validate_single_message(body, attributes, response)
52
+ validate_body(body, response)
53
+ validate_attributes(attributes, response) unless attributes.nil?
54
+ end
55
+
56
+ def validate_body(body, response)
57
+ calculated_md5 = md5_of_message_body(body)
58
+ returned_md5 = response.md5_of_message_body
59
+ if calculated_md5 != returned_md5
60
+ error_message = mismatch_error_message(
61
+ 'message body',
62
+ calculated_md5,
63
+ returned_md5,
64
+ response)
65
+ raise Aws::Errors::ChecksumError, error_message
66
+ end
67
+ end
68
+
69
+ def validate_attributes(attributes, response)
70
+ calculated_md5 = md5_of_message_attributes(attributes)
71
+ returned_md5 = response.md5_of_message_attributes
72
+ if returned_md5 != calculated_md5
73
+ error_message = mismatch_error_message(
74
+ 'message atributes',
75
+ calculated_md5,
76
+ returned_md5,
77
+ response)
78
+ raise Aws::Errors::ChecksumError, error_message
79
+ end
80
+ end
81
+
82
+ def md5_of_message_body(message_body)
83
+ OpenSSL::Digest::MD5.hexdigest(message_body)
84
+ end
85
+
86
+ def md5_of_message_attributes(message_attributes)
87
+ encoded = { }
88
+ message_attributes.each do |name, attribute|
89
+ name = name.to_s
90
+ encoded[name] = String.new
91
+ data_type_without_label = DATA_TYPE.match(attribute[:data_type])[1]
92
+ encoded[name] << encode_length_and_bytes(name) <<
93
+ encode_length_and_bytes(attribute[:data_type]) <<
94
+ [TRANSPORT_TYPE_ENCODINGS[data_type_without_label]].pack('C'.freeze)
95
+
96
+ if attribute[:string_value] != nil
97
+ encoded[name] << encode_length_and_string(attribute[:string_value])
98
+ elsif attribute[:binary_value] != nil
99
+ encoded[name] << encode_length_and_bytes(attribute[:binary_value])
100
+ end
101
+ end
102
+
103
+ buffer = encoded.keys.sort.reduce(String.new) do |string, name|
104
+ string << encoded[name]
105
+ end
106
+ OpenSSL::Digest::MD5.hexdigest(buffer)
107
+ end
108
+
109
+ def encode_length_and_string(string)
110
+ string = String.new(string)
111
+ string.encode!(NORMALIZED_ENCODING)
112
+ encode_length_and_bytes(string)
113
+ end
114
+
115
+ def encode_length_and_bytes(bytes)
116
+ [bytes.bytesize, bytes].pack('L>a*'.freeze)
117
+ end
118
+
119
+ def mismatch_error_message(section, local_md5, returned_md5, response)
120
+ m = "MD5 returned by SQS does not match " <<
121
+ "the calculation on the original request. ("
122
+
123
+ if response.respond_to?(:id) && !response.id.nil?
124
+ m << "Message ID: #{response.id}, "
125
+ end
126
+
127
+ m << "MD5 calculated by the #{section}: " <<
128
+ "'#{local_md5}', MD5 checksum returned: '#{returned_md5}')"
129
+ end
130
+ end
131
+
132
+ option(:verify_checksums,
133
+ doc_default: true,
134
+ doc_type: 'Boolean',
135
+ docstring: <<-DOCS
136
+ When `true` MD5 checksums will be computed for messages sent to
137
+ an SQS queue and matched against MD5 checksums returned by Amazon SQS.
138
+ `Aws::Errors::Checksum` errors are raised for cases where checksums do
139
+ not match.
140
+ DOCS
141
+ ) do |config|
142
+ # By default, we will disable checksum verification when response
143
+ # stubbing is enable. If a user decides to enable both features,
144
+ # then they will need to stub the MD5s in the response.
145
+ # See the spec/aws/sqs/client/verify_checksums_spec.rb for
146
+ # examples of how to do this.
147
+ if config.respond_to?(:stub_responses)
148
+ !config.stub_responses
149
+ else
150
+ config.verify_checksums
151
+ end
152
+ end
153
+
154
+ def add_handlers(handlers, config)
155
+ if config.verify_checksums
156
+ handlers.add(Handler, {
157
+ priority: 10 ,
158
+ step: :validate,
159
+ operations: [:send_message, :send_message_batch]
160
+ })
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,43 @@
1
+ module Aws
2
+ module SQS
3
+ module Plugins
4
+ # @api private
5
+ class QueueUrls < Seahorse::Client::Plugin
6
+
7
+ class Handler < Seahorse::Client::Handler
8
+
9
+ def call(context)
10
+ if queue_url = context.params[:queue_url]
11
+ update_endpoint(context, queue_url)
12
+ update_region(context, queue_url)
13
+ end
14
+ @handler.call(context)
15
+ end
16
+
17
+ def update_endpoint(context, url)
18
+ context.http_request.endpoint = url
19
+ end
20
+
21
+ # If the region in the queue url is not the configured
22
+ # region, then we will modify the request to have
23
+ # a sigv4 signer for the proper region.
24
+ def update_region(context, queue_url)
25
+ if queue_region = queue_url.to_s.split('.')[1]
26
+ if queue_region != context.config.region
27
+ config = context.config.dup
28
+ config.region = queue_region
29
+ config.sigv4_region = queue_region
30
+ config.sigv4_signer = Aws::Plugins::SignatureV4.build_signer(config)
31
+ context.config = config
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ handler(Handler)
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,521 @@
1
+ require 'set'
2
+
3
+ module Aws
4
+ module SQS
5
+
6
+ # A utility class for long polling messages in a loop. **Messages are
7
+ # automatically deleted from the queue at the end of the given block.**
8
+ #
9
+ # poller = Aws::SQS::QueuePoller.new(queue_url)
10
+ #
11
+ # poller.poll do |msg|
12
+ # puts msg.body
13
+ # end
14
+ #
15
+ # ## Long Polling
16
+ #
17
+ # By default, messages are received using long polling. This
18
+ # method will force a default `:wait_time_seconds` of 20 seconds.
19
+ # If you prefer to use the queue default wait time, then pass
20
+ # a `nil` value for `:wait_time_seconds`.
21
+ #
22
+ # # disables 20 second default, use queue ReceiveMessageWaitTimeSeconds
23
+ # poller.poll(wait_time_seconds:nil) do |msg|
24
+ # # ...
25
+ # end
26
+ #
27
+ # When disabling `:wait_time_seconds` by passing `nil`, you must
28
+ # ensure the queue `ReceiveMessageWaitTimeSeconds` attribute is
29
+ # set to a non-zero value, or you will be short-polling.
30
+ # This will trigger significantly more API calls.
31
+ #
32
+ # ## Batch Receiving Messages
33
+ #
34
+ # You can specify a maximum number of messages to receive with
35
+ # each polling attempt via `:max_number_of_messages`. When this is
36
+ # set to a positive value, greater than 1, the block will receive
37
+ # an array of messages, instead of a single message.
38
+ #
39
+ # # receives and yields 1 message at a time
40
+ # poller.poll do |msg|
41
+ # # ...
42
+ # end
43
+ #
44
+ # # receives and yields up to 10 messages at a time
45
+ # poller.poll(max_number_of_messages:10) do |messages|
46
+ # messages.each do |msg|
47
+ # # ...
48
+ # end
49
+ # end
50
+ #
51
+ # The maximum value for `:max_number_of_messages` is enforced by
52
+ # Amazon SQS.
53
+ #
54
+ # ## Visibility Timeouts
55
+ #
56
+ # When receiving messages, you have a fixed amount of time to process
57
+ # and delete the message before it is added back into the queue. This
58
+ # is the visibility timeout. By default, the queue's `VisibilityTimeout`
59
+ # attribute is used. You can provide an alternative visibility timeout
60
+ # when polling.
61
+ #
62
+ # # queue default VisibilityTimeout
63
+ # poller.poll do |msg|
64
+ # end
65
+ #
66
+ # # custom visibility timeout
67
+ # poller.poll(visibility_timeout:10) do |msg|
68
+ # end
69
+ #
70
+ #
71
+ # You can reset the visibility timeout of a single message by calling
72
+ # {#change_message_visibility_timeout}. This is useful when you need
73
+ # more time to finish processing the message.
74
+ #
75
+ # poller.poll do |msg|
76
+ #
77
+ # # do work ...
78
+ #
79
+ # # need more time for processing
80
+ # poller.change_message_visibility_timeout(msg, 60)
81
+ #
82
+ # # finish work ...
83
+ #
84
+ # end
85
+ #
86
+ # If you change the visibility timeout of a message to zero, it will
87
+ # return to the queue immediately.
88
+ #
89
+ # ## Deleting Messages
90
+ #
91
+ # Messages are deleted from the queue when the block returns normally.
92
+ #
93
+ # poller.poll do |msg|
94
+ # # do work
95
+ # end # messages deleted here
96
+ #
97
+ # You can skip message deletion by passing `skip_delete: true`.
98
+ # This allows you to manually delete the messages using
99
+ # {#delete_message}, or {#delete_messages}.
100
+ #
101
+ # # single message
102
+ # poller.poll(skip_delete: true) do |msg|
103
+ # poller.delete_message(msg) # if successful
104
+ # end
105
+ #
106
+ # # batch delete messages
107
+ # poller.poll(skip_delete: true, max_number_of_messages:10) do |messages|
108
+ # poller.delete_messages(messages)
109
+ # end
110
+ #
111
+ # Another way to manage message deletion is to throw `:skip_delete`
112
+ # from the poll block. You can use this to choose when a message, or
113
+ # message batch is deleted on an individual basis. This can be very
114
+ # useful when you are capturing temporal errors and wish for the
115
+ # message to timeout.
116
+ #
117
+ # poller.poll do |msg|
118
+ # begin
119
+ # # do work
120
+ # rescue
121
+ # # unexpected error occurred while processing messages,
122
+ # # log it, and skip delete so it can be re-processed later
123
+ # throw :skip_delete
124
+ # end
125
+ # end
126
+ #
127
+ # ## Terminating the Polling Loop
128
+ #
129
+ # By default, polling will continue indefinitely. You can stop
130
+ # the poller by providing an idle timeout or by throwing `:stop_polling`
131
+ # from the {#before_request} callback.
132
+ #
133
+ # ### `:idle_timeout` Option
134
+ #
135
+ # This is a configurable, maximum number of seconds to wait for a
136
+ # new message before the polling loop exists. By default, there is
137
+ # no idle timeout.
138
+ #
139
+ # # stops polling after a minute of no received messages
140
+ # poller.poll(idle_timeout: 60) do |msg|
141
+ # # ...
142
+ # end
143
+ #
144
+ # ### Throw `:stop_polling`
145
+ #
146
+ # If you want more fine grained control, you can configure a
147
+ # before request callback to trigger before each long poll. Throwing
148
+ # `:stop_polling` from this callback will cause the poller to exit
149
+ # normally without making the next request.
150
+ #
151
+ # # stop after processing 100 messages
152
+ # poller.before_request do |stats|
153
+ # throw :stop_polling if stats.received_message_count >= 100
154
+ # end
155
+ #
156
+ # poller.poll do |msg|
157
+ # # do work ...
158
+ # end
159
+ #
160
+ # ## Tracking Progress
161
+ #
162
+ # The poller will automatically track a few statistics client-side in
163
+ # a {PollerStats} object. You can access the poller stats
164
+ # three ways:
165
+ #
166
+ # * The first block argument of {#before_request}
167
+ # * The second block argument of {#poll}.
168
+ # * The return value from {#poll}.
169
+ #
170
+ # Here are examples of accessing the statistics.
171
+ #
172
+ # * Configure a {#before_request} callback.
173
+ #
174
+ # ```
175
+ # poller.before_request do |stats|
176
+ # logger.info("requests: #{stats.request_count}")
177
+ # logger.info("messages: #{stats.received_message_count}")
178
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
179
+ # end
180
+ # ```
181
+ #
182
+ # * Accept a 2nd argument in the poll block, for example:
183
+ #
184
+ # ```
185
+ # poller.poll do |msg, stats|
186
+ # logger.info("requests: #{stats.request_count}")
187
+ # logger.info("messages: #{stats.received_message_count}")
188
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
189
+ # end
190
+ # ```
191
+ #
192
+ # * Return value:
193
+ #
194
+ # ```
195
+ # stats = poller.poll(idle_timeout:10) do |msg|
196
+ # # do work ...
197
+ # end
198
+ # logger.info("requests: #{stats.request_count}")
199
+ # logger.info("messages: #{stats.received_message_count}")
200
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
201
+ # ```
202
+ #
203
+ class QueuePoller
204
+
205
+ # @param [String] queue_url
206
+ # @option options [Client] :client
207
+ # @option (see #poll)
208
+ def initialize(queue_url, options = {})
209
+ @queue_url = queue_url
210
+ @client = options.delete(:client) || Client.new
211
+ @default_config = PollerConfig.new(options)
212
+ end
213
+
214
+ # @return [String]
215
+ attr_reader :queue_url
216
+
217
+ # @return [Client]
218
+ attr_reader :client
219
+
220
+ # @return [PollerConfig]
221
+ attr_reader :default_config
222
+
223
+ # Registers a callback that is invoked once before every polling
224
+ # attempt.
225
+ #
226
+ # poller.before_request do |stats|
227
+ # logger.info("requests: #{stats.request_count}")
228
+ # logger.info("messages: #{stats.received_message_count}")
229
+ # logger.info("last-timestamp: #{stats.last_message_received_at}")
230
+ # end
231
+ #
232
+ # poller.poll do |msg|
233
+ # # do work ...
234
+ # end
235
+ #
236
+ # ## `:stop_polling`
237
+ #
238
+ # If you throw `:stop_polling` from the {#before_request} callback,
239
+ # then the poller will exit normally before making the next long
240
+ # poll request.
241
+ #
242
+ # poller.before_request do |stats|
243
+ # throw :stop_polling if stats.received_messages >= 100
244
+ # end
245
+ #
246
+ # # at most 100 messages will be yielded
247
+ # poller.poll do |msg|
248
+ # # do work ...
249
+ # end
250
+ #
251
+ # @yieldparam [PollerStats] stats An object that tracks a few
252
+ # client-side statistics about the queue polling.
253
+ #
254
+ # @return [void]
255
+ def before_request(&block)
256
+ @default_config = @default_config.with(before_request: Proc.new)
257
+ end
258
+
259
+ # Polls the queue, yielded a message, or an array of messages.
260
+ # Messages are automatically deleted from the queue at the
261
+ # end of the given block. See the class documentation on
262
+ # {QueuePoller} for more examples.
263
+ #
264
+ # @example Basic example, loops indefinitely
265
+ #
266
+ # poller.poll do |msg|
267
+ # # ...
268
+ # end
269
+ #
270
+ # @example Receives and deletes messages as a batch
271
+ #
272
+ # poller.poll(max_number_of_messages:10) do |messages|
273
+ # messages.each do |msg|
274
+ # # ...
275
+ # end
276
+ # end
277
+ #
278
+ # @option options [Integer] :wait_time_seconds (20) The
279
+ # long polling interval. Messages are yielded as soon as they are
280
+ # received. The `:wait_time_seconds` option specifies the max
281
+ # duration for each polling attempt before a new request is
282
+ # sent to receive messages.
283
+ #
284
+ # @option options [Integer] :max_number_of_messages (1) The maximum
285
+ # number of messages to yield from each polling attempt.
286
+ # Values can be from 1 to 10.
287
+ #
288
+ # @option options [Integer] :visibility_timeout (nil)
289
+ # The number of seconds you have to process a message before
290
+ # it is put back into the queue and can be received again.
291
+ # By default, the queue's
292
+ #
293
+ # @option options [Array<String>] :attribute_names ([])
294
+ # The list of attributes that need to be returned along with each
295
+ # message. Valid attribute names include:
296
+ #
297
+ # * `All` - All attributes.
298
+ # * `ApproximateFirstReceiveTimestamp` - The time when the message
299
+ # was first received from the queue (epoch time in milliseconds).
300
+ # * `ApproximateReceiveCount` - The number of times a message has
301
+ # been received from the queue but not deleted.
302
+ # * `SenderId` - The AWS account number (or the IP address, if
303
+ # anonymous access is allowed) of the sender.
304
+ # * `SentTimestamp` - The time when the message was sent to the
305
+ # queue (epoch time in milliseconds).
306
+ #
307
+ # @option options [Array<String>] :message_attribute_names ([])
308
+ # A list of message attributes to receive. You can receive
309
+ # all messages by using `All` or `.*`. You can also use
310
+ # `foo.*` to return all message attributes starting with the
311
+ # `foo` prefix.
312
+ #
313
+ # @option options [Integer] :idle_timeout (nil) Polling terminates
314
+ # gracefully when `:idle_timeout` seconds have passed without
315
+ # receiving any messages.
316
+ #
317
+ # @option options [Boolean] :skip_delete (false) When `true`, messages
318
+ # are not deleted after polling block. If you wish to delete
319
+ # received messages, you will need to call `#delete_message` or
320
+ # `#delete_messages` manually.
321
+ #
322
+ # @option options [Proc] :before_request (nil) Called before each
323
+ # polling attempt. This proc receives a single argument, an
324
+ # instance of {PollerStats}.
325
+ #
326
+ # @return [PollerStats]
327
+ def poll(options = {}, &block)
328
+ config = @default_config.with(options)
329
+ stats = PollerStats.new
330
+ catch(:stop_polling) do
331
+ loop do
332
+ messages = get_messages(config, stats)
333
+ if messages.empty?
334
+ check_idle_timeout(config, stats, messages)
335
+ else
336
+ process_messages(config, stats, messages, &block)
337
+ end
338
+ end
339
+ end
340
+ stats.polling_stopped_at = Time.now
341
+ stats
342
+ end
343
+
344
+ # @note This method should be called from inside a {#poll} block.
345
+ # @param [#receipt_handle] message An object that responds to
346
+ # `#receipt_handle`.
347
+ # @param [Integer] seconds
348
+ def change_message_visibility_timeout(message, seconds)
349
+ @client.change_message_visibility({
350
+ queue_url: @queue_url,
351
+ receipt_handle: message.receipt_handle,
352
+ visibility_timeout: seconds,
353
+ })
354
+ end
355
+
356
+ # @note This method should be called from inside a {#poll} block.
357
+ # @param [#receipt_handle] message An object that responds to
358
+ # `#receipt_handle`.
359
+ def delete_message(message)
360
+ @client.delete_message({
361
+ queue_url: @queue_url,
362
+ receipt_handle: message.receipt_handle,
363
+ })
364
+ end
365
+
366
+ # @note This method should be called from inside a {#poll} block.
367
+ # @param [Array<#message_id, #receipt_handle>] messages An array of received
368
+ # messages. Each object must respond to `#message_id` and
369
+ # `#receipt_handle`.
370
+ def delete_messages(messages)
371
+ @client.delete_message_batch(
372
+ queue_url: @queue_url,
373
+ entries: messages.map { |msg|
374
+ { id: msg.message_id, receipt_handle: msg.receipt_handle }
375
+ }
376
+ )
377
+ end
378
+
379
+ private
380
+
381
+ def get_messages(config, stats)
382
+ config.before_request.call(stats) if config.before_request
383
+ messages = send_request(config).messages
384
+ stats.request_count += 1
385
+ messages
386
+ end
387
+
388
+ def send_request(config)
389
+ params = config.request_params.merge(queue_url: @queue_url)
390
+ @client.receive_message(params)
391
+ end
392
+
393
+ def check_idle_timeout(config, stats, messages)
394
+ if config.idle_timeout
395
+ since = stats.last_message_received_at || stats.polling_started_at
396
+ idle_time = Time.now - since
397
+ throw :stop_polling if idle_time > config.idle_timeout
398
+ end
399
+ end
400
+
401
+ def process_messages(config, stats, messages, &block)
402
+ stats.received_message_count += messages.count
403
+ stats.last_message_received_at = Time.now
404
+ catch(:skip_delete) do
405
+ yield_messages(config, messages, stats, &block)
406
+ delete_messages(messages) unless config.skip_delete
407
+ end
408
+ end
409
+
410
+ def yield_messages(config, messages, stats, &block)
411
+ if config.request_params[:max_number_of_messages] == 1
412
+ messages.each do |msg|
413
+ yield(msg, stats)
414
+ end
415
+ else
416
+ yield(messages, stats)
417
+ end
418
+ end
419
+
420
+ # Statistics tracked client-side by the {QueuePoller}.
421
+ class PollerStats
422
+
423
+ def initialize
424
+ @request_count = 0
425
+ @received_message_count = 0
426
+ @last_message_received_at = nil
427
+ @polling_started_at = Time.now
428
+ @polling_stopped_at = nil
429
+ end
430
+
431
+ # @return [Integer]
432
+ attr_accessor :request_count
433
+
434
+ # @return [Integer]
435
+ attr_accessor :received_message_count
436
+
437
+ # @return [Time,nil]
438
+ attr_accessor :last_message_received_at
439
+
440
+ # @return [Time]
441
+ attr_accessor :polling_started_at
442
+
443
+ # @return [Time,nil]
444
+ attr_accessor :polling_stopped_at
445
+
446
+ end
447
+
448
+ # A read-only set of configuration used by the QueuePoller.
449
+ class PollerConfig
450
+
451
+ # @api private
452
+ CONFIG_OPTIONS = Set.new([
453
+ :idle_timeout,
454
+ :skip_delete,
455
+ :before_request,
456
+ ])
457
+
458
+ # @api private
459
+ PARAM_OPTIONS = Set.new([
460
+ :wait_time_seconds,
461
+ :max_number_of_messages,
462
+ :visibility_timeout,
463
+ :attribute_names,
464
+ :message_attribute_names,
465
+ ])
466
+
467
+ # @return [Integer,nil]
468
+ attr_reader :idle_timeout
469
+
470
+ # @return [Boolean]
471
+ attr_reader :skip_delete
472
+
473
+ # @return [Proc,nil]
474
+ attr_reader :before_request
475
+
476
+ # @return [Hash]
477
+ attr_reader :request_params
478
+
479
+ def initialize(options)
480
+ @idle_timeout = nil
481
+ @skip_delete = false
482
+ @before_request = nil
483
+ @request_params = {
484
+ wait_time_seconds: 20,
485
+ max_number_of_messages: 1,
486
+ visibility_timeout: nil,
487
+ attribute_names: ['All'],
488
+ message_attribute_names: ['All'],
489
+ }
490
+ options.each do |opt_name, value|
491
+ if CONFIG_OPTIONS.include?(opt_name)
492
+ instance_variable_set("@#{opt_name}", value)
493
+ elsif PARAM_OPTIONS.include?(opt_name)
494
+ @request_params[opt_name] = value
495
+ else
496
+ raise ArgumentError, "invalid option #{opt_name.inspect}"
497
+ end
498
+ end
499
+ @request_params.freeze
500
+ freeze
501
+ end
502
+
503
+ # @return [PollerConfig] Returns a new {PollerConfig} instance
504
+ # with the given options applied.
505
+ def with(options)
506
+ self.class.new(to_h.merge(options))
507
+ end
508
+
509
+ private
510
+
511
+ def to_h
512
+ hash = {}
513
+ CONFIG_OPTIONS.each { |key| hash[key] = send(key) }
514
+ PARAM_OPTIONS.each { |key| hash[key] = @request_params[key] }
515
+ hash
516
+ end
517
+
518
+ end
519
+ end
520
+ end
521
+ end