sidekiq 4.2.4 → 6.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (134) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +445 -0
  3. data/LICENSE +1 -1
  4. data/README.md +21 -34
  5. data/bin/sidekiq +26 -2
  6. data/bin/sidekiqload +28 -38
  7. data/bin/sidekiqmon +8 -0
  8. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
  9. data/lib/generators/sidekiq/templates/worker_test.rb.erb +2 -2
  10. data/lib/generators/sidekiq/worker_generator.rb +21 -13
  11. data/lib/sidekiq/api.rb +347 -213
  12. data/lib/sidekiq/cli.rb +221 -212
  13. data/lib/sidekiq/client.rb +75 -52
  14. data/lib/sidekiq/delay.rb +41 -0
  15. data/lib/sidekiq/exception_handler.rb +12 -16
  16. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  17. data/lib/sidekiq/extensions/active_record.rb +13 -10
  18. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  19. data/lib/sidekiq/extensions/generic_proxy.rb +10 -4
  20. data/lib/sidekiq/fetch.rb +38 -31
  21. data/lib/sidekiq/job_logger.rb +63 -0
  22. data/lib/sidekiq/job_retry.rb +263 -0
  23. data/lib/sidekiq/launcher.rb +169 -70
  24. data/lib/sidekiq/logger.rb +166 -0
  25. data/lib/sidekiq/manager.rb +17 -20
  26. data/lib/sidekiq/middleware/chain.rb +15 -5
  27. data/lib/sidekiq/middleware/i18n.rb +5 -7
  28. data/lib/sidekiq/monitor.rb +133 -0
  29. data/lib/sidekiq/paginator.rb +18 -14
  30. data/lib/sidekiq/processor.rb +161 -70
  31. data/lib/sidekiq/rails.rb +30 -73
  32. data/lib/sidekiq/redis_connection.rb +67 -20
  33. data/lib/sidekiq/scheduled.rb +61 -35
  34. data/lib/sidekiq/sd_notify.rb +149 -0
  35. data/lib/sidekiq/systemd.rb +24 -0
  36. data/lib/sidekiq/testing/inline.rb +2 -1
  37. data/lib/sidekiq/testing.rb +54 -26
  38. data/lib/sidekiq/util.rb +48 -15
  39. data/lib/sidekiq/version.rb +2 -1
  40. data/lib/sidekiq/web/action.rb +15 -15
  41. data/lib/sidekiq/web/application.rb +112 -89
  42. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  43. data/lib/sidekiq/web/helpers.rb +153 -73
  44. data/lib/sidekiq/web/router.rb +27 -19
  45. data/lib/sidekiq/web.rb +64 -109
  46. data/lib/sidekiq/worker.rb +164 -41
  47. data/lib/sidekiq.rb +86 -60
  48. data/sidekiq.gemspec +24 -22
  49. data/web/assets/images/apple-touch-icon.png +0 -0
  50. data/web/assets/javascripts/application.js +25 -27
  51. data/web/assets/javascripts/dashboard.js +34 -38
  52. data/web/assets/stylesheets/application-dark.css +160 -0
  53. data/web/assets/stylesheets/application-rtl.css +246 -0
  54. data/web/assets/stylesheets/application.css +402 -12
  55. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  56. data/web/assets/stylesheets/bootstrap.css +2 -2
  57. data/web/locales/ar.yml +81 -0
  58. data/web/locales/de.yml +14 -2
  59. data/web/locales/en.yml +4 -0
  60. data/web/locales/es.yml +4 -3
  61. data/web/locales/fa.yml +80 -0
  62. data/web/locales/fr.yml +3 -3
  63. data/web/locales/he.yml +79 -0
  64. data/web/locales/ja.yml +9 -4
  65. data/web/locales/lt.yml +83 -0
  66. data/web/locales/pl.yml +4 -4
  67. data/web/locales/ru.yml +4 -0
  68. data/web/locales/ur.yml +80 -0
  69. data/web/locales/vi.yml +83 -0
  70. data/web/views/_footer.erb +5 -2
  71. data/web/views/_job_info.erb +3 -2
  72. data/web/views/_nav.erb +4 -18
  73. data/web/views/_paging.erb +1 -1
  74. data/web/views/busy.erb +57 -19
  75. data/web/views/dashboard.erb +3 -3
  76. data/web/views/dead.erb +2 -2
  77. data/web/views/layout.erb +13 -2
  78. data/web/views/morgue.erb +19 -12
  79. data/web/views/queue.erb +22 -12
  80. data/web/views/queues.erb +13 -3
  81. data/web/views/retries.erb +22 -13
  82. data/web/views/retry.erb +3 -3
  83. data/web/views/scheduled.erb +7 -4
  84. metadata +42 -194
  85. data/.github/contributing.md +0 -32
  86. data/.github/issue_template.md +0 -4
  87. data/.gitignore +0 -12
  88. data/.travis.yml +0 -12
  89. data/3.0-Upgrade.md +0 -70
  90. data/4.0-Upgrade.md +0 -53
  91. data/COMM-LICENSE +0 -95
  92. data/Ent-Changes.md +0 -146
  93. data/Gemfile +0 -29
  94. data/Pro-2.0-Upgrade.md +0 -138
  95. data/Pro-3.0-Upgrade.md +0 -44
  96. data/Pro-Changes.md +0 -585
  97. data/Rakefile +0 -9
  98. data/bin/sidekiqctl +0 -99
  99. data/code_of_conduct.md +0 -50
  100. data/lib/sidekiq/core_ext.rb +0 -106
  101. data/lib/sidekiq/logging.rb +0 -106
  102. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  103. data/lib/sidekiq/middleware/server/logging.rb +0 -40
  104. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  105. data/test/config.yml +0 -9
  106. data/test/env_based_config.yml +0 -11
  107. data/test/fake_env.rb +0 -1
  108. data/test/fixtures/en.yml +0 -2
  109. data/test/helper.rb +0 -75
  110. data/test/test_actors.rb +0 -138
  111. data/test/test_api.rb +0 -528
  112. data/test/test_cli.rb +0 -418
  113. data/test/test_client.rb +0 -266
  114. data/test/test_exception_handler.rb +0 -56
  115. data/test/test_extensions.rb +0 -127
  116. data/test/test_fetch.rb +0 -50
  117. data/test/test_launcher.rb +0 -95
  118. data/test/test_logging.rb +0 -35
  119. data/test/test_manager.rb +0 -50
  120. data/test/test_middleware.rb +0 -158
  121. data/test/test_processor.rb +0 -235
  122. data/test/test_rails.rb +0 -22
  123. data/test/test_redis_connection.rb +0 -132
  124. data/test/test_retry.rb +0 -326
  125. data/test/test_retry_exhausted.rb +0 -149
  126. data/test/test_scheduled.rb +0 -115
  127. data/test/test_scheduling.rb +0 -58
  128. data/test/test_sidekiq.rb +0 -107
  129. data/test/test_testing.rb +0 -143
  130. data/test/test_testing_fake.rb +0 -357
  131. data/test/test_testing_inline.rb +0 -94
  132. data/test/test_util.rb +0 -13
  133. data/test/test_web.rb +0 -726
  134. data/test/test_web_helpers.rb +0 -54
@@ -1,33 +1,49 @@
1
1
  # frozen_string_literal: true
2
- require 'uri'
3
- require 'yaml'
2
+
3
+ require "uri"
4
+ require "set"
5
+ require "yaml"
6
+ require "cgi"
4
7
 
5
8
  module Sidekiq
6
9
  # This is not a public API
7
10
  module WebHelpers
8
11
  def strings(lang)
9
- @@strings ||= {}
10
- @@strings[lang] ||= begin
12
+ @strings ||= {}
13
+ @strings[lang] ||= begin
11
14
  # Allow sidekiq-web extensions to add locale paths
12
15
  # so extensions can be localized
13
16
  settings.locales.each_with_object({}) do |path, global|
14
17
  find_locale_files(lang).each do |file|
15
18
  strs = YAML.load(File.open(file))
16
- global.deep_merge!(strs[lang])
19
+ global.merge!(strs[lang])
17
20
  end
18
21
  end
19
22
  end
20
23
  end
21
24
 
25
+ def singularize(str, count)
26
+ if count == 1 && str.respond_to?(:singularize) # rails
27
+ str.singularize
28
+ else
29
+ str
30
+ end
31
+ end
32
+
22
33
  def clear_caches
23
- @@strings = nil
24
- @@locale_files = nil
34
+ @strings = nil
35
+ @locale_files = nil
36
+ @available_locales = nil
25
37
  end
26
38
 
27
39
  def locale_files
28
- @@locale_files ||= settings.locales.flat_map do |path|
40
+ @locale_files ||= settings.locales.flat_map { |path|
29
41
  Dir["#{path}/*.yml"]
30
- end
42
+ }
43
+ end
44
+
45
+ def available_locales
46
+ @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
31
47
  end
32
48
 
33
49
  def find_locale_files(lang)
@@ -56,36 +72,75 @@ module Sidekiq
56
72
  end
57
73
 
58
74
  def poll_path
59
- if current_path != '' && params['poll']
60
- root_path + current_path
75
+ if current_path != "" && params["poll"]
76
+ path = root_path + current_path
77
+ query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
78
+ path += "?#{query_string}" unless query_string.empty?
79
+ path
61
80
  else
62
81
  ""
63
82
  end
64
83
  end
65
84
 
66
- # Given a browser request Accept-Language header like
67
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
68
- # will return "fr" since that's the first code with a matching
69
- # locale in web/locales
85
+ def text_direction
86
+ get_locale["TextDirection"] || "ltr"
87
+ end
88
+
89
+ def rtl?
90
+ text_direction == "rtl"
91
+ end
92
+
93
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
94
+ def user_preferred_languages
95
+ languages = env["HTTP_ACCEPT_LANGUAGE"]
96
+ languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
97
+ locale, quality = language.split(";q=", 2)
98
+ locale = nil if locale == "*" # Ignore wildcards
99
+ quality = quality ? quality.to_f : 1.0
100
+ [locale, quality]
101
+ }.sort { |(_, left), (_, right)|
102
+ right <=> left
103
+ }.map(&:first).compact
104
+ end
105
+
106
+ # Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
107
+ # this method will try to best match the available locales to the user's preferred languages.
108
+ #
109
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
70
110
  def locale
71
111
  @locale ||= begin
72
- locale = 'en'.freeze
73
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
74
- languages.downcase.split(','.freeze).each do |lang|
75
- next if lang == '*'.freeze
76
- lang = lang.split(';'.freeze)[0]
77
- break locale = lang if find_locale_files(lang).any?
78
- end
79
- locale
112
+ matched_locale = user_preferred_languages.map { |preferred|
113
+ preferred_language = preferred.split("-", 2).first
114
+
115
+ lang_group = available_locales.select { |available|
116
+ preferred_language == available.split("-", 2).first
117
+ }
118
+
119
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
120
+ }.compact.first
121
+
122
+ matched_locale || "en"
80
123
  end
81
124
  end
82
125
 
126
+ # within is used by Sidekiq Pro
127
+ def display_tags(job, within = nil)
128
+ job.tags.map { |tag|
129
+ "<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
130
+ }.join(" ")
131
+ end
132
+
133
+ # mperham/sidekiq#3243
134
+ def unfiltered?
135
+ yield unless env["PATH_INFO"].start_with?("/filter/")
136
+ end
137
+
83
138
  def get_locale
84
139
  strings(locale)
85
140
  end
86
141
 
87
- def t(msg, options={})
88
- string = get_locale[msg] || msg
142
+ def t(msg, options = {})
143
+ string = get_locale[msg] || strings("en")[msg] || msg
89
144
  if options.empty?
90
145
  string
91
146
  else
@@ -93,6 +148,10 @@ module Sidekiq
93
148
  end
94
149
  end
95
150
 
151
+ def sort_direction_label
152
+ params[:direction] == "asc" ? "&uarr;" : "&darr;"
153
+ end
154
+
96
155
  def workers
97
156
  @workers ||= Sidekiq::Workers.new
98
157
  end
@@ -105,22 +164,14 @@ module Sidekiq
105
164
  @stats ||= Sidekiq::Stats.new
106
165
  end
107
166
 
108
- def retries_with_score(score)
109
- Sidekiq.redis do |conn|
110
- conn.zrangebyscore('retry', score, score)
111
- end.map { |msg| Sidekiq.load_json(msg) }
112
- end
113
-
114
- def location
115
- Sidekiq.redis { |conn| conn.client.location }
116
- end
117
-
118
167
  def redis_connection
119
- Sidekiq.redis { |conn| conn.client.id }
168
+ Sidekiq.redis do |conn|
169
+ conn.connection[:id]
170
+ end
120
171
  end
121
172
 
122
173
  def namespace
123
- @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
174
+ @ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
124
175
  end
125
176
 
126
177
  def redis_info
@@ -128,38 +179,44 @@ module Sidekiq
128
179
  end
129
180
 
130
181
  def root_path
131
- "#{env['SCRIPT_NAME']}/"
182
+ "#{env["SCRIPT_NAME"]}/"
132
183
  end
133
184
 
134
185
  def current_path
135
- @current_path ||= request.path_info.gsub(/^\//,'')
186
+ @current_path ||= request.path_info.gsub(/^\//, "")
136
187
  end
137
188
 
138
189
  def current_status
139
- workers.size == 0 ? 'idle' : 'active'
190
+ workers.size == 0 ? "idle" : "active"
140
191
  end
141
192
 
142
193
  def relative_time(time)
143
- %{<time datetime="#{time.getutc.iso8601}">#{time}</time>}
194
+ stamp = time.getutc.iso8601
195
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
144
196
  end
145
197
 
146
198
  def job_params(job, score)
147
- "#{score}-#{job['jid']}"
199
+ "#{score}-#{job["jid"]}"
148
200
  end
149
201
 
150
202
  def parse_params(params)
151
- score, jid = params.split("-")
203
+ score, jid = params.split("-", 2)
152
204
  [score.to_f, jid]
153
205
  end
154
206
 
155
- SAFE_QPARAMS = %w(page poll)
207
+ SAFE_QPARAMS = %w[page poll direction]
156
208
 
157
209
  # Merge options with current params, filter safe params, and stringify to query string
158
210
  def qparams(options)
159
- options = options.stringify_keys
160
- params.merge(options).map do |key, value|
161
- SAFE_QPARAMS.include?(key) ? "#{key}=#{value}" : next
162
- end.compact.join("&")
211
+ stringified_options = options.transform_keys(&:to_s)
212
+
213
+ to_query_string(params.merge(stringified_options))
214
+ end
215
+
216
+ def to_query_string(params)
217
+ params.map { |key, value|
218
+ SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
219
+ }.compact.join("&")
163
220
  end
164
221
 
165
222
  def truncate(text, truncate_after_chars = 2000)
@@ -167,33 +224,38 @@ module Sidekiq
167
224
  end
168
225
 
169
226
  def display_args(args, truncate_after_chars = 2000)
170
- args.map do |arg|
171
- h(truncate(to_display(arg), truncate_after_chars))
172
- end.join(", ")
227
+ return "Invalid job payload, args is nil" if args.nil?
228
+ return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
229
+
230
+ begin
231
+ args.map { |arg|
232
+ h(truncate(to_display(arg), truncate_after_chars))
233
+ }.join(", ")
234
+ rescue
235
+ "Illegal job arguments: #{h args.inspect}"
236
+ end
173
237
  end
174
238
 
175
239
  def csrf_tag
176
- "<input type='hidden' name='authenticity_token' value='#{session[:csrf]}'/>"
240
+ "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
177
241
  end
178
242
 
179
243
  def to_display(arg)
244
+ arg.inspect
245
+ rescue
180
246
  begin
181
- arg.inspect
182
- rescue
183
- begin
184
- arg.to_s
185
- rescue => ex
186
- "Cannot display argument: [#{ex.class.name}] #{ex.message}"
187
- end
247
+ arg.to_s
248
+ rescue => ex
249
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
188
250
  end
189
251
  end
190
252
 
191
- RETRY_JOB_KEYS = Set.new(%w(
253
+ RETRY_JOB_KEYS = Set.new(%w[
192
254
  queue class args retry_count retried_at failed_at
193
255
  jid error_message error_class backtrace
194
256
  error_backtrace enqueued_at retry wrapped
195
- created_at
196
- ))
257
+ created_at tags
258
+ ])
197
259
 
198
260
  def retry_extra_items(retry_job)
199
261
  @retry_extra_items ||= {}.tap do |extra|
@@ -203,15 +265,29 @@ module Sidekiq
203
265
  end
204
266
  end
205
267
 
268
+ def format_memory(rss_kb)
269
+ return "0" if rss_kb.nil? || rss_kb == 0
270
+
271
+ if rss_kb < 100_000
272
+ "#{number_with_delimiter(rss_kb)} KB"
273
+ elsif rss_kb < 10_000_000
274
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
275
+ else
276
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
277
+ end
278
+ end
279
+
206
280
  def number_with_delimiter(number)
281
+ return "" if number.nil?
282
+
207
283
  begin
208
284
  Float(number)
209
285
  rescue ArgumentError, TypeError
210
286
  return number
211
287
  end
212
288
 
213
- options = {delimiter: ',', separator: '.'}
214
- parts = number.to_s.to_str.split('.')
289
+ options = {delimiter: ",", separator: "."}
290
+ parts = number.to_s.to_str.split(".")
215
291
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
216
292
  parts.join(options[:separator])
217
293
  end
@@ -219,8 +295,8 @@ module Sidekiq
219
295
  def h(text)
220
296
  ::Rack::Utils.escape_html(text)
221
297
  rescue ArgumentError => e
222
- raise unless e.message.eql?('invalid byte sequence in UTF-8')
223
- text.encode!('UTF-16', 'UTF-8', invalid: :replace, replace: '').encode!('UTF-8', 'UTF-16')
298
+ raise unless e.message.eql?("invalid byte sequence in UTF-8")
299
+ text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
224
300
  retry
225
301
  end
226
302
 
@@ -237,7 +313,7 @@ module Sidekiq
237
313
  end
238
314
 
239
315
  def environment_title_prefix
240
- environment = Sidekiq.options[:environment] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
316
+ environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
241
317
 
242
318
  "[#{environment.upcase}] " unless environment == "production"
243
319
  end
@@ -246,27 +322,31 @@ module Sidekiq
246
322
  "Sidekiq v#{Sidekiq::VERSION}"
247
323
  end
248
324
 
325
+ def server_utc_time
326
+ Time.now.utc.strftime("%H:%M:%S UTC")
327
+ end
328
+
249
329
  def redis_connection_and_namespace
250
330
  @redis_connection_and_namespace ||= begin
251
- namespace_suffix = namespace == nil ? '' : "##{namespace}"
331
+ namespace_suffix = namespace.nil? ? "" : "##{namespace}"
252
332
  "#{redis_connection}#{namespace_suffix}"
253
333
  end
254
334
  end
255
335
 
256
336
  def retry_or_delete_or_kill(job, params)
257
- if params['retry']
337
+ if params["retry"]
258
338
  job.retry
259
- elsif params['delete']
339
+ elsif params["delete"]
260
340
  job.delete
261
- elsif params['kill']
341
+ elsif params["kill"]
262
342
  job.kill
263
343
  end
264
344
  end
265
345
 
266
346
  def delete_or_add_queue(job, params)
267
- if params['delete']
347
+ if params["delete"]
268
348
  job.delete
269
- elsif params['add_to_queue']
349
+ elsif params["add_to_queue"]
270
350
  job.add_to_queue
271
351
  end
272
352
  end
@@ -1,18 +1,23 @@
1
1
  # frozen_string_literal: true
2
- require 'rack'
2
+
3
+ require "rack"
3
4
 
4
5
  module Sidekiq
5
6
  module WebRouter
6
- GET = 'GET'.freeze
7
- DELETE = 'DELETE'.freeze
8
- POST = 'POST'.freeze
9
- PUT = 'PUT'.freeze
10
- PATCH = 'PATCH'.freeze
11
- HEAD = 'HEAD'.freeze
12
-
13
- ROUTE_PARAMS = 'rack.route_params'.freeze
14
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
15
- PATH_INFO = 'PATH_INFO'.freeze
7
+ GET = "GET"
8
+ DELETE = "DELETE"
9
+ POST = "POST"
10
+ PUT = "PUT"
11
+ PATCH = "PATCH"
12
+ HEAD = "HEAD"
13
+
14
+ ROUTE_PARAMS = "rack.route_params"
15
+ REQUEST_METHOD = "REQUEST_METHOD"
16
+ PATH_INFO = "PATH_INFO"
17
+
18
+ def head(path, &block)
19
+ route(HEAD, path, &block)
20
+ end
16
21
 
17
22
  def get(path, &block)
18
23
  route(GET, path, &block)
@@ -35,18 +40,22 @@ module Sidekiq
35
40
  end
36
41
 
37
42
  def route(method, path, &block)
38
- @routes ||= { GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => [] }
43
+ @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
39
44
 
40
45
  @routes[method] << WebRoute.new(method, path, block)
41
- @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
42
46
  end
43
47
 
44
48
  def match(env)
45
49
  request_method = env[REQUEST_METHOD]
46
50
  path_info = ::Rack::Utils.unescape env[PATH_INFO]
47
51
 
52
+ # There are servers which send an empty string when requesting the root.
53
+ # These servers should be ashamed of themselves.
54
+ path_info = "/" if path_info == ""
55
+
48
56
  @routes[request_method].each do |route|
49
- if params = route.match(request_method, path_info)
57
+ params = route.match(request_method, path_info)
58
+ if params
50
59
  env[ROUTE_PARAMS] = params
51
60
 
52
61
  return WebAction.new(env, route.block)
@@ -60,7 +69,7 @@ module Sidekiq
60
69
  class WebRoute
61
70
  attr_accessor :request_method, :pattern, :block, :name
62
71
 
63
- NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)/.freeze
72
+ NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
64
73
 
65
74
  def initialize(request_method, pattern, block)
66
75
  @request_method = request_method
@@ -73,7 +82,7 @@ module Sidekiq
73
82
  end
74
83
 
75
84
  def compile
76
- if pattern.match(NAMED_SEGMENTS_PATTERN)
85
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
77
86
  p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
78
87
 
79
88
  Regexp.new("\\A#{p}\\Z")
@@ -87,9 +96,8 @@ module Sidekiq
87
96
  when String
88
97
  {} if path == matcher
89
98
  else
90
- if path_match = path.match(matcher)
91
- Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
92
- end
99
+ path_match = path.match(matcher)
100
+ path_match&.named_captures&.transform_keys(&:to_sym)
93
101
  end
94
102
  end
95
103
  end