sidekiq 5.2.10 → 6.5.6

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