ox-tender-abstract 0.9.3 → 0.9.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9924cb49a0b35703d8e0093b3ae40b8b8556f7da398990b3ff5f84f185c7d944
4
- data.tar.gz: 2598c2bb4af463aa3cbf1bde9c1cc76aac352f8e58a193152305dccc4a2cef8a
3
+ metadata.gz: 29f0092ce09cc7fd85cfdea5489dbe645e1d28f99a12a2f98e57a48761bf2f13
4
+ data.tar.gz: abb88a96187a2a0e11e16efdf067368e5e4cd20504e080384789504a8a530c21
5
5
  SHA512:
6
- metadata.gz: 3c0a783ab40ca1f45be2d0d5c32db63345a40502bdf6140b446018686b73482afba84ce3097d7ab7afe821b106915e06ba32b03b66a35a19adf95fdc4ad2c404
7
- data.tar.gz: 53a15a50dad376969b8ff1869e88197a669288bdd57b3931ce59105c8288b25f6f9a9768e03b9211f8284570e4bbf219daa98f2702270c39c967c5e1737c4ead
6
+ metadata.gz: 7351bf939d3e42d07ae65f313e5cb0621732cfa5c9c4cc3d0bfa77581fd9c4b51d284844901dcd50eed4e93d1dddff6a79a8055b4f79c45c1fbbeca2efc24129
7
+ data.tar.gz: 8df04c15cd6381544847fce45624f148b1ea824812aa7717ff52a1fcf304e030cc8beb910b02bbfd294d84411397d10cd1e03fac7a90d061b3c600f41c76276c
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.4.4
1
+ 3.4.5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## [0.9.5] - 2025-08-06
2
+
3
+ ### Fixed
4
+
5
+ - Fixed encoding compatibility error when processing API blocking messages
6
+ - Fixed missing `include_attachments` parameter in `search_tenders` and `search_tenders_with_auto_wait` methods
7
+ - Improved error handling for binary response data from API
8
+
9
+ ### Changed
10
+
11
+ - Enhanced encoding handling in `ArchiveProcessor` to safely handle UTF-8 and BINARY responses
12
+ - Restored `include_attachments` parameter to all search methods with default value `true`
13
+
14
+ ### Added
15
+
16
+ - Better error logging for encoding issues
17
+ - Safe encoding conversion using `force_encoding('UTF-8').scrub`
18
+
1
19
  ## [0.9.3] - 2025-07-27
2
20
 
3
21
  - Added support for parsing tender documents
data/SIDEKIQ_USAGE.md ADDED
@@ -0,0 +1,341 @@
1
+ # Использование OxTenderAbstract с Sidekiq
2
+
3
+ ## Обработка блокировок API в отложенных задачах
4
+
5
+ При частых запросах к API zakupki.gov.ru сервер может заблокировать загрузку архивов на 10 минут. Библиотека теперь правильно обрабатывает такие блокировки и возвращает специальные результаты.
6
+
7
+ ## Рекомендуемый Sidekiq Worker с автоматическим ожиданием
8
+
9
+ ```ruby
10
+ class TenderImportWorker
11
+ include Sidekiq::Worker
12
+
13
+ # Простая настройка - библиотека сама управляет блокировками
14
+ sidekiq_options retry: 3
15
+
16
+ def perform(region, date, subsystem_type = 'PRIZ', document_type = 'epNotificationEF2020', resume_state = nil)
17
+ # Используем новый метод с автоматическим ожиданием
18
+ result = OxTenderAbstract.search_tenders_with_auto_wait(
19
+ org_region: region,
20
+ exact_date: date,
21
+ subsystem_type: subsystem_type,
22
+ document_type: document_type,
23
+ resume_state: resume_state
24
+ )
25
+
26
+ if result.failure?
27
+ # Обрабатываем только критические ошибки
28
+ handle_failure(result, region, date, subsystem_type, document_type)
29
+ else
30
+ process_tenders(result.data[:tenders])
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def handle_failure(result, region, date, subsystem_type, document_type)
37
+ # С автоматическим ожиданием блокировки обрабатываются автоматически
38
+ # Нужно обрабатывать только реальные ошибки
39
+ logger.error "Tender import failed: #{result.error}"
40
+ raise StandardError, result.error
41
+ end
42
+
43
+ def process_tenders(tenders)
44
+ tenders.each do |tender|
45
+ save_tender_to_database(tender)
46
+ end
47
+
48
+ logger.info "Processed #{tenders.size} tenders"
49
+ end
50
+ end
51
+ ```
52
+
53
+ ## Альтернативный Worker с ручным управлением
54
+
55
+ ```ruby
56
+ class TenderImportWorkerManual
57
+ include Sidekiq::Worker
58
+
59
+ # Настраиваем повторные попытки с увеличенной задержкой для блокировок
60
+ sidekiq_options retry: 5
61
+
62
+ def perform(region, date, subsystem_type = 'PRIZ', document_type = 'epNotificationEF2020', resume_state = nil)
63
+ # Отключаем автоматическое ожидание для ручного управления
64
+ OxTenderAbstract.configure do |config|
65
+ config.auto_wait_on_block = false
66
+ end
67
+
68
+ result = OxTenderAbstract.search_tenders_with_auto_wait(
69
+ org_region: region,
70
+ exact_date: date,
71
+ subsystem_type: subsystem_type,
72
+ document_type: document_type,
73
+ resume_state: resume_state
74
+ )
75
+
76
+ if result.failure?
77
+ handle_failure(result, region, date, subsystem_type, document_type)
78
+ else
79
+ process_tenders(result.data[:tenders])
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def handle_failure(result, region, date, subsystem_type, document_type)
86
+ # Проверяем тип ошибки
87
+ if result.metadata[:error_type] == :blocked
88
+ # API заблокировал доступ на 10 минут
89
+ retry_after = result.metadata[:retry_after] || 600
90
+
91
+ logger.warn "Archive download blocked, retrying in #{retry_after} seconds"
92
+
93
+ # Перепланируем задачу через указанное время
94
+ TenderImportWorker.perform_in(
95
+ retry_after.seconds + 30, # +30 секунд для гарантии
96
+ region, date, subsystem_type, document_type
97
+ )
98
+ else
99
+ # Обычная ошибка - логируем и возможно повторяем стандартно
100
+ logger.error "Tender import failed: #{result.error}"
101
+ raise StandardError, result.error
102
+ end
103
+ end
104
+
105
+ def process_tenders(tenders)
106
+ tenders.each do |tender|
107
+ # Обработка каждого тендера
108
+ save_tender_to_database(tender)
109
+ end
110
+
111
+ logger.info "Processed #{tenders.size} tenders"
112
+ end
113
+ end
114
+ ```
115
+
116
+ ## Конфигурация автоматического ожидания
117
+
118
+ Библиотека теперь поддерживает встроенное автоматическое ожидание при блокировках:
119
+
120
+ ```ruby
121
+ # config/initializers/ox_tender_abstract.rb
122
+ OxTenderAbstract.configure do |config|
123
+ config.token = ENV['ZAKUPKI_API_TOKEN']
124
+
125
+ # Настройки автоматического ожидания
126
+ config.auto_wait_on_block = true # Автоматически ждать при блокировке (по умолчанию true)
127
+ config.block_wait_time = 610 # Время ожидания в секундах (10 мин + 10 сек)
128
+ config.max_wait_time = 900 # Максимальное время ожидания (15 мин)
129
+ end
130
+ ```
131
+
132
+ ### Режимы работы
133
+
134
+ 1. **Автоматическое ожидание** (`auto_wait_on_block = true`) - библиотека сама ждет и продолжает
135
+ 2. **Ручное управление** (`auto_wait_on_block = false`) - возвращает состояние для продолжения в Sidekiq
136
+
137
+ ## Настройка Sidekiq для обработки блокировок
138
+
139
+ ### 1. Кастомная стратегия повторов
140
+
141
+ ```ruby
142
+ # config/initializers/sidekiq.rb
143
+
144
+ # Кастомная стратегия повторов для API блокировок
145
+ class TenderRetryStrategy
146
+ def call(worker, job, queue)
147
+ # Извлекаем информацию об ошибке
148
+ exception = job['error_message']
149
+
150
+ if exception&.include?('blocked')
151
+ # Для блокировок используем фиксированную задержку
152
+ return 600 # 10 минут
153
+ else
154
+ # Стандартная экспоненциальная задержка
155
+ return (job['retry_count'] ** 4) + 15
156
+ end
157
+ end
158
+ end
159
+
160
+ Sidekiq.configure_server do |config|
161
+ config.death_handlers << lambda do |job, ex|
162
+ # Логируем окончательно проваленные задачи
163
+ Rails.logger.error "Sidekiq job #{job['class']} failed permanently: #{ex.message}"
164
+ end
165
+ end
166
+ ```
167
+
168
+ ### 2. Настройка очередей с приоритетами
169
+
170
+ ```ruby
171
+ # config/sidekiq.yml
172
+ :queues:
173
+ - [critical, 2]
174
+ - [tenders_import, 1]
175
+ - [tenders_retry, 1]
176
+ - [default, 1]
177
+ ```
178
+
179
+ ### 3. Worker с интеллектуальными повторами
180
+
181
+ ```ruby
182
+ class SmartTenderImportWorker
183
+ include Sidekiq::Worker
184
+
185
+ sidekiq_options queue: 'tenders_import', retry: 3
186
+
187
+ # Кастомная логика повторов
188
+ sidekiq_retry_in do |count, exception|
189
+ case exception.message
190
+ when /blocked/
191
+ # Для блокировок ждем 10 минут
192
+ 600
193
+ when /network error/i
194
+ # Для сетевых ошибок короткая задержка
195
+ 30 * (count + 1)
196
+ else
197
+ # Стандартная задержка
198
+ 60 * (count + 1)
199
+ end
200
+ end
201
+
202
+ def perform(params)
203
+ with_error_handling do
204
+ import_tenders(params)
205
+ end
206
+ end
207
+
208
+ private
209
+
210
+ def with_error_handling
211
+ yield
212
+ rescue => e
213
+ if e.message.include?('blocked')
214
+ # Перемещаем в специальную очередь для повторов
215
+ SmartTenderImportWorker.set(queue: 'tenders_retry')
216
+ .perform_in(610.seconds, { retry: true }.merge(params))
217
+ else
218
+ raise e
219
+ end
220
+ end
221
+ end
222
+ ```
223
+
224
+ ## Мониторинг и отладка
225
+
226
+ ### 1. Логирование блокировок
227
+
228
+ ```ruby
229
+ class TenderImportLogger
230
+ def self.log_blocked_request(region, date, retry_after)
231
+ Rails.logger.warn {
232
+ "[TENDER_BLOCKED] Region: #{region}, Date: #{date}, Retry after: #{retry_after}s"
233
+ }
234
+
235
+ # Отправка в системы мониторинга
236
+ StatsD.increment('tender_import.blocked')
237
+ StatsD.histogram('tender_import.retry_delay', retry_after)
238
+ end
239
+ end
240
+ ```
241
+
242
+ ### 2. Метрики для мониторинга
243
+
244
+ ```ruby
245
+ # В worker'е
246
+ def perform(params)
247
+ start_time = Time.current
248
+
249
+ begin
250
+ result = import_tenders(params)
251
+ StatsD.increment('tender_import.success')
252
+ StatsD.histogram('tender_import.duration', Time.current - start_time)
253
+ rescue => e
254
+ StatsD.increment('tender_import.error')
255
+ StatsD.increment("tender_import.error.#{error_type(e)}")
256
+ raise
257
+ end
258
+ end
259
+
260
+ def error_type(exception)
261
+ case exception.message
262
+ when /blocked/ then 'blocked'
263
+ when /network/ then 'network'
264
+ when /parse/ then 'parse'
265
+ else 'unknown'
266
+ end
267
+ end
268
+ ```
269
+
270
+ ## Рекомендации
271
+
272
+ 1. **Используйте разные очереди** для обычных и повторных задач
273
+ 2. **Мониторьте частоту блокировок** - если они частые, уменьшите нагрузку
274
+ 3. **Настройте алерты** на высокий процент блокировок
275
+ 4. **Кэшируйте результаты** где возможно, чтобы уменьшить количество запросов
276
+ 5. **Используйте rate limiting** на уровне приложения
277
+
278
+ ## Пример полной настройки
279
+
280
+ ```ruby
281
+ # app/workers/tender_import_worker.rb
282
+ class TenderImportWorker
283
+ include Sidekiq::Worker
284
+ include Sidekiq::Throttled::Worker
285
+
286
+ # Ограничиваем количество одновременных запросов
287
+ sidekiq_throttle(
288
+ threshold: { limit: 5, period: 1.minute },
289
+ key: ->(region, date) { "tender_import:#{region}" }
290
+ )
291
+
292
+ sidekiq_options queue: 'tenders', retry: 5
293
+
294
+ def perform(region, date, options = {})
295
+ TenderImportService.new(region, date, options).call
296
+ end
297
+ end
298
+
299
+ # app/services/tender_import_service.rb
300
+ class TenderImportService
301
+ def initialize(region, date, options = {})
302
+ @region = region
303
+ @date = date
304
+ @options = options
305
+ end
306
+
307
+ def call
308
+ result = OxTenderAbstract.search_tenders(
309
+ org_region: @region,
310
+ exact_date: @date,
311
+ subsystem_type: @options[:subsystem_type] || 'PRIZ'
312
+ )
313
+
314
+ if result.failure?
315
+ handle_error(result)
316
+ else
317
+ process_success(result)
318
+ end
319
+ end
320
+
321
+ private
322
+
323
+ def handle_error(result)
324
+ case result.metadata[:error_type]
325
+ when :blocked
326
+ schedule_retry(result.metadata[:retry_after])
327
+ when :network
328
+ raise NetworkError, result.error
329
+ else
330
+ raise StandardError, result.error
331
+ end
332
+ end
333
+
334
+ def schedule_retry(retry_after)
335
+ TenderImportWorker.perform_in(
336
+ (retry_after + 30).seconds,
337
+ @region, @date, @options.merge(retry: true)
338
+ )
339
+ end
340
+ end
341
+ ```
@@ -29,18 +29,19 @@ module OxTenderAbstract
29
29
 
30
30
  # Convenience method for searching tenders in specific subsystem
31
31
  def search_tenders(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
32
- document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE)
32
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE, include_attachments: true)
33
33
  client = Client.new
34
34
  client.search_tenders(
35
35
  org_region: org_region,
36
36
  exact_date: exact_date,
37
37
  subsystem_type: subsystem_type,
38
- document_type: document_type
38
+ document_type: document_type,
39
+ include_attachments: include_attachments
39
40
  )
40
41
  end
41
42
 
42
43
  # Enhanced method for searching tenders across multiple subsystems
43
- def search_all_tenders(org_region:, exact_date:, subsystems: nil, document_types: nil)
44
+ def search_all_tenders(org_region:, exact_date:, subsystems: nil, document_types: nil, include_attachments: true)
44
45
  # Default subsystems to search
45
46
  subsystems ||= %w[PRIZ RPEC RPGZ BTK UR RGK OD223 RD223]
46
47
 
@@ -67,7 +68,8 @@ module OxTenderAbstract
67
68
  org_region: org_region,
68
69
  exact_date: exact_date,
69
70
  subsystem_type: subsystem_type,
70
- document_type: doc_type
71
+ document_type: doc_type,
72
+ include_attachments: include_attachments
71
73
  )
72
74
 
73
75
  if result.success?
@@ -134,7 +136,8 @@ module OxTenderAbstract
134
136
 
135
137
  # Search tenders with automatic wait on API blocks and resume capability
136
138
  def search_tenders_with_auto_wait(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
137
- document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE, resume_state: nil)
139
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE, resume_state: nil,
140
+ include_attachments: true)
138
141
  client = Client.new
139
142
 
140
143
  # Если есть состояние для продолжения
@@ -146,7 +149,8 @@ module OxTenderAbstract
146
149
  subsystem_type: subsystem_type,
147
150
  document_type: document_type,
148
151
  start_from_archive: start_from,
149
- resume_state: resume_state
152
+ resume_state: resume_state,
153
+ include_attachments: include_attachments
150
154
  )
151
155
  else
152
156
  # Используем обычный метод если авто-ожидание включено
@@ -155,7 +159,8 @@ module OxTenderAbstract
155
159
  org_region: org_region,
156
160
  exact_date: exact_date,
157
161
  subsystem_type: subsystem_type,
158
- document_type: document_type
162
+ document_type: document_type,
163
+ include_attachments: include_attachments
159
164
  )
160
165
  else
161
166
  # Используем метод с возможностью продолжения
@@ -163,7 +168,8 @@ module OxTenderAbstract
163
168
  org_region: org_region,
164
169
  exact_date: exact_date,
165
170
  subsystem_type: subsystem_type,
166
- document_type: document_type
171
+ document_type: document_type,
172
+ include_attachments: include_attachments
167
173
  )
168
174
  end
169
175
  end
@@ -88,8 +88,12 @@ module OxTenderAbstract
88
88
  log_warn "Download attempt #{attempt} failed: #{last_error}"
89
89
  end
90
90
  rescue StandardError => e
91
- last_error = e.message
92
- log_error "Download attempt #{attempt} exception: #{last_error}"
91
+ last_error = begin
92
+ e.message.force_encoding('UTF-8').scrub
93
+ rescue StandardError
94
+ e.message.to_s
95
+ end
96
+ log_error "Download error details: #{e.class} - #{last_error}"
93
97
  end
94
98
 
95
99
  if attempt < MAX_RETRY_ATTEMPTS
@@ -128,34 +132,42 @@ module OxTenderAbstract
128
132
  unless response.is_a?(Net::HTTPSuccess)
129
133
  error_msg = "HTTP error: #{response.code} #{response.message}"
130
134
  if response.body && !response.body.empty?
131
- # Log first part of response body for debugging
132
- body_preview = response.body[0..500]
133
- log_error "Response body preview: #{body_preview}"
134
- error_msg += ". Response: #{body_preview[0..100]}"
135
+ # Log first part of response body for debugging - safely handle encoding
136
+ begin
137
+ body_preview = response.body.force_encoding('UTF-8').scrub[0..500]
138
+ log_error "Response body preview: #{body_preview}"
139
+ error_msg += ". Response: #{body_preview[0..100]}"
140
+ rescue StandardError => e
141
+ log_error "Response body encoding error: #{e.message}"
142
+ error_msg += '. Response body unreadable (encoding issue)'
143
+ end
135
144
  end
136
145
  return Result.failure(error_msg)
137
146
  end
138
147
 
139
- # Check for download blocking message in successful response
140
- if response.body&.include?('Скачивание архива по данной ссылке заблокировано')
141
- if OxTenderAbstract.configuration.auto_wait_on_block
142
- wait_time = OxTenderAbstract.configuration.block_wait_time
143
- log_error "Archive download blocked. Auto-waiting for #{wait_time} seconds..."
144
-
145
- # Показываем прогресс ожидания
146
- show_wait_progress(wait_time)
147
-
148
- log_info 'Wait completed, retrying download...'
149
- # Рекурсивно повторяем попытку после ожидания
150
- return download_to_memory(url)
151
- else
152
- log_error 'Archive download blocked for 10 minutes'
153
- return Result.failure(
154
- 'Archive download blocked for 10 minutes',
155
- error_type: :blocked,
156
- retry_after: 600
157
- )
148
+ # Check for download blocking message in successful response - safely handle encoding
149
+ begin
150
+ response_text = response.body&.force_encoding('UTF-8')&.scrub
151
+ if response_text&.include?('Скачивание архива по данной ссылке заблокировано')
152
+ if OxTenderAbstract.configuration.auto_wait_on_block
153
+ wait_time = OxTenderAbstract.configuration.block_wait_time
154
+ log_error "Archive download blocked. Auto-waiting for #{wait_time} seconds..."
155
+
156
+ # Показываем прогресс ожидания
157
+ show_wait_progress(wait_time)
158
+
159
+ log_info 'Wait completed, retrying download...'
160
+ # Рекурсивно повторяем попытку после ожидания
161
+ return download_to_memory(url)
162
+ else
163
+ # Возвращаем специальную ошибку блокировки для ручной обработки
164
+ return Result.failure('Archive download blocked for 10 minutes',
165
+ ArchiveBlockedError.new('Archive download blocked', 600))
166
+ end
158
167
  end
168
+ rescue StandardError => e
169
+ log_error "Encoding error when checking for blocking message: #{e.message}"
170
+ # Продолжаем обработку, так как это может быть просто архив
159
171
  end
160
172
 
161
173
  content = response.body
@@ -76,7 +76,7 @@ module OxTenderAbstract
76
76
 
77
77
  # Search tenders with full workflow: API -> Archive -> Parse
78
78
  def search_tenders(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
79
- document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE)
79
+ document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE, include_attachments: true)
80
80
  log_info "Starting tender search for region #{org_region}, date #{exact_date}, subsystem: #{subsystem_type}, type: #{document_type}"
81
81
 
82
82
  # Step 1: Get archive URLs from API
@@ -133,6 +133,15 @@ module OxTenderAbstract
133
133
  tender_data = parse_result.data[:content]
134
134
  next if tender_data[:reestr_number].nil? || tender_data[:reestr_number].empty?
135
135
 
136
+ # Extract attachments if requested
137
+ if include_attachments
138
+ attachments_result = extract_attachments_from_xml(file_data[:content])
139
+ if attachments_result.success?
140
+ tender_data[:attachments] = attachments_result.data[:attachments]
141
+ tender_data[:attachments_count] = attachments_result.data[:total_count]
142
+ end
143
+ end
144
+
136
145
  # Add metadata
137
146
  tender_data[:source_file] = file_name
138
147
  tender_data[:archive_url] = archive_url
@@ -240,7 +249,7 @@ module OxTenderAbstract
240
249
  # Позволяет продолжить загрузку с места паузы при блокировках API
241
250
  def search_tenders_with_resume(org_region:, exact_date:, subsystem_type: DocumentTypes::DEFAULT_SUBSYSTEM,
242
251
  document_type: DocumentTypes::DEFAULT_DOCUMENT_TYPE,
243
- start_from_archive: 0, resume_state: nil)
252
+ start_from_archive: 0, resume_state: nil, include_attachments: true)
244
253
  log_info "Starting tender search with resume capability for region #{org_region}, date #{exact_date}"
245
254
  log_info "Starting from archive #{start_from_archive}" if start_from_archive > 0
246
255
 
@@ -330,6 +339,15 @@ module OxTenderAbstract
330
339
  tender_data = parse_result.data[:content]
331
340
  next if tender_data[:reestr_number].nil? || tender_data[:reestr_number].empty?
332
341
 
342
+ # Extract attachments if requested
343
+ if include_attachments
344
+ attachments_result = extract_attachments_from_xml(file_data[:content])
345
+ if attachments_result.success?
346
+ tender_data[:attachments] = attachments_result.data[:attachments]
347
+ tender_data[:attachments_count] = attachments_result.data[:total_count]
348
+ end
349
+ end
350
+
333
351
  # Add metadata
334
352
  tender_data[:source_file] = file_name
335
353
  tender_data[:archive_url] = archive_url
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OxTenderAbstract
4
- VERSION = '0.9.3'
4
+ VERSION = '0.9.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ox-tender-abstract
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - smolev
@@ -187,6 +187,7 @@ files:
187
187
  - LICENSE
188
188
  - README.md
189
189
  - Rakefile
190
+ - SIDEKIQ_USAGE.md
190
191
  - lib/ox-tender-abstract.rb
191
192
  - lib/oxtenderabstract/archive_processor.rb
192
193
  - lib/oxtenderabstract/client.rb
@@ -222,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
222
223
  - !ruby/object:Gem::Version
223
224
  version: '0'
224
225
  requirements: []
225
- rubygems_version: 3.6.9
226
+ rubygems_version: 3.7.1
226
227
  specification_version: 4
227
228
  summary: Ruby library for working with Russian tender system (zakupki.gov.ru) SOAP
228
229
  API