sidekiq 4.2.10 → 6.5.7

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 (131) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +573 -1
  3. data/LICENSE +3 -3
  4. data/README.md +25 -34
  5. data/bin/sidekiq +27 -3
  6. data/bin/sidekiqload +81 -74
  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/job_spec.rb.erb +6 -0
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +585 -285
  13. data/lib/sidekiq/cli.rb +256 -233
  14. data/lib/sidekiq/client.rb +86 -83
  15. data/lib/sidekiq/component.rb +65 -0
  16. data/lib/sidekiq/delay.rb +43 -0
  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 +13 -5
  21. data/lib/sidekiq/fetch.rb +50 -40
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +51 -0
  24. data/lib/sidekiq/job_retry.rb +282 -0
  25. data/lib/sidekiq/job_util.rb +71 -0
  26. data/lib/sidekiq/launcher.rb +184 -90
  27. data/lib/sidekiq/logger.rb +156 -0
  28. data/lib/sidekiq/manager.rb +43 -45
  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 +176 -91
  40. data/lib/sidekiq/rails.rb +41 -96
  41. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  42. data/lib/sidekiq/redis_connection.rb +117 -48
  43. data/lib/sidekiq/ring_buffer.rb +29 -0
  44. data/lib/sidekiq/scheduled.rb +134 -44
  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 +80 -61
  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 -15
  52. data/lib/sidekiq/web/application.rb +129 -86
  53. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  54. data/lib/sidekiq/web/helpers.rb +170 -83
  55. data/lib/sidekiq/web/router.rb +23 -19
  56. data/lib/sidekiq/web.rb +69 -109
  57. data/lib/sidekiq/worker.rb +290 -41
  58. data/lib/sidekiq.rb +185 -77
  59. data/sidekiq.gemspec +23 -27
  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 +70 -91
  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 +242 -0
  69. data/web/assets/stylesheets/application.css +364 -144
  70. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  71. data/web/assets/stylesheets/bootstrap.css +2 -2
  72. data/web/locales/ar.yml +87 -0
  73. data/web/locales/de.yml +14 -2
  74. data/web/locales/el.yml +43 -19
  75. data/web/locales/en.yml +15 -1
  76. data/web/locales/es.yml +22 -5
  77. data/web/locales/fa.yml +1 -0
  78. data/web/locales/fr.yml +10 -3
  79. data/web/locales/he.yml +79 -0
  80. data/web/locales/ja.yml +19 -4
  81. data/web/locales/lt.yml +83 -0
  82. data/web/locales/pl.yml +4 -4
  83. data/web/locales/pt-br.yml +27 -9
  84. data/web/locales/ru.yml +4 -0
  85. data/web/locales/ur.yml +80 -0
  86. data/web/locales/vi.yml +83 -0
  87. data/web/locales/zh-cn.yml +36 -11
  88. data/web/locales/zh-tw.yml +32 -7
  89. data/web/views/_footer.erb +5 -2
  90. data/web/views/_job_info.erb +3 -2
  91. data/web/views/_nav.erb +5 -19
  92. data/web/views/_paging.erb +1 -1
  93. data/web/views/_poll_link.erb +2 -5
  94. data/web/views/_summary.erb +7 -7
  95. data/web/views/busy.erb +62 -24
  96. data/web/views/dashboard.erb +24 -15
  97. data/web/views/dead.erb +3 -3
  98. data/web/views/layout.erb +14 -3
  99. data/web/views/metrics.erb +69 -0
  100. data/web/views/metrics_for_job.erb +87 -0
  101. data/web/views/morgue.erb +9 -6
  102. data/web/views/queue.erb +26 -12
  103. data/web/views/queues.erb +12 -2
  104. data/web/views/retries.erb +14 -7
  105. data/web/views/retry.erb +3 -3
  106. data/web/views/scheduled.erb +7 -4
  107. metadata +66 -206
  108. data/.github/contributing.md +0 -32
  109. data/.github/issue_template.md +0 -9
  110. data/.gitignore +0 -12
  111. data/.travis.yml +0 -18
  112. data/3.0-Upgrade.md +0 -70
  113. data/4.0-Upgrade.md +0 -53
  114. data/COMM-LICENSE +0 -95
  115. data/Ent-Changes.md +0 -173
  116. data/Gemfile +0 -29
  117. data/Pro-2.0-Upgrade.md +0 -138
  118. data/Pro-3.0-Upgrade.md +0 -44
  119. data/Pro-Changes.md +0 -628
  120. data/Rakefile +0 -12
  121. data/bin/sidekiqctl +0 -99
  122. data/code_of_conduct.md +0 -50
  123. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  124. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  125. data/lib/sidekiq/core_ext.rb +0 -119
  126. data/lib/sidekiq/exception_handler.rb +0 -31
  127. data/lib/sidekiq/logging.rb +0 -106
  128. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  129. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  130. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  131. data/lib/sidekiq/util.rb +0 -63
@@ -1,35 +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.deep_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
33
+ @strings = nil
34
+ @locale_files = nil
35
+ @available_locales = nil
27
36
  end
28
37
 
29
38
  def locale_files
30
- @@locale_files ||= settings.locales.flat_map do |path|
39
+ @locale_files ||= settings.locales.flat_map { |path|
31
40
  Dir["#{path}/*.yml"]
32
- end
41
+ }
42
+ end
43
+
44
+ def available_locales
45
+ @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
33
46
  end
34
47
 
35
48
  def find_locale_files(lang)
@@ -57,42 +70,65 @@ module Sidekiq
57
70
  @head_html.join if defined?(@head_html)
58
71
  end
59
72
 
60
- def poll_path
61
- if current_path != '' && params['poll']
62
- root_path + current_path
63
- else
64
- ""
65
- end
73
+ def text_direction
74
+ get_locale["TextDirection"] || "ltr"
75
+ end
76
+
77
+ def rtl?
78
+ text_direction == "rtl"
79
+ end
80
+
81
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
82
+ def user_preferred_languages
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
+ quality = quality ? quality.to_f : 1.0
88
+ [locale, quality]
89
+ }.sort { |(_, left), (_, right)|
90
+ right <=> left
91
+ }.map(&:first).compact
66
92
  end
67
93
 
68
- # Given a browser request Accept-Language header like
69
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
70
- # will return "fr" since that's the first code with a matching
71
- # locale in web/locales
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"
95
+ # this method will try to best match the available locales to the user's preferred languages.
96
+ #
97
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
72
98
  def locale
73
99
  @locale ||= begin
74
- locale = 'en'.freeze
75
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
76
- languages.downcase.split(','.freeze).each do |lang|
77
- next if lang == '*'.freeze
78
- lang = lang.split(';'.freeze)[0]
79
- break locale = lang if find_locale_files(lang).any?
80
- end
81
- locale
100
+ matched_locale = user_preferred_languages.map { |preferred|
101
+ preferred_language = preferred.split("-", 2).first
102
+
103
+ lang_group = available_locales.select { |available|
104
+ preferred_language == available.split("-", 2).first
105
+ }
106
+
107
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
108
+ }.compact.first
109
+
110
+ matched_locale || "en"
82
111
  end
83
112
  end
84
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
+
85
121
  # mperham/sidekiq#3243
86
122
  def unfiltered?
87
- yield unless env['PATH_INFO'].start_with?("/filter/")
123
+ yield unless env["PATH_INFO"].start_with?("/filter/")
88
124
  end
89
125
 
90
126
  def get_locale
91
127
  strings(locale)
92
128
  end
93
129
 
94
- def t(msg, options={})
95
- string = get_locale[msg] || msg
130
+ def t(msg, options = {})
131
+ string = get_locale[msg] || strings("en")[msg] || msg
96
132
  if options.empty?
97
133
  string
98
134
  else
@@ -100,30 +136,53 @@ module Sidekiq
100
136
  end
101
137
  end
102
138
 
103
- def workers
104
- @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
105
145
  end
106
146
 
107
147
  def processes
108
148
  @processes ||= Sidekiq::ProcessSet.new
109
149
  end
110
150
 
111
- def stats
112
- @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
113
172
  end
114
173
 
115
- def retries_with_score(score)
116
- Sidekiq.redis do |conn|
117
- conn.zrangebyscore('retry', score, score)
118
- end.map { |msg| Sidekiq.load_json(msg) }
174
+ def stats
175
+ @stats ||= Sidekiq::Stats.new
119
176
  end
120
177
 
121
178
  def redis_connection
122
- Sidekiq.redis { |conn| conn.client.id }
179
+ Sidekiq.redis do |conn|
180
+ conn.connection[:id]
181
+ end
123
182
  end
124
183
 
125
184
  def namespace
126
- @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
185
+ @ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
127
186
  end
128
187
 
129
188
  def redis_info
@@ -131,39 +190,44 @@ module Sidekiq
131
190
  end
132
191
 
133
192
  def root_path
134
- "#{env['SCRIPT_NAME']}/"
193
+ "#{env["SCRIPT_NAME"]}/"
135
194
  end
136
195
 
137
196
  def current_path
138
- @current_path ||= request.path_info.gsub(/^\//,'')
197
+ @current_path ||= request.path_info.gsub(/^\//, "")
139
198
  end
140
199
 
141
200
  def current_status
142
- workers.size == 0 ? 'idle' : 'active'
201
+ workset.size == 0 ? "idle" : "active"
143
202
  end
144
203
 
145
204
  def relative_time(time)
146
205
  stamp = time.getutc.iso8601
147
- %{<time title="#{stamp}" datetime="#{stamp}">#{time}</time>}
206
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
148
207
  end
149
208
 
150
209
  def job_params(job, score)
151
- "#{score}-#{job['jid']}"
210
+ "#{score}-#{job["jid"]}"
152
211
  end
153
212
 
154
213
  def parse_params(params)
155
- score, jid = params.split("-")
214
+ score, jid = params.split("-", 2)
156
215
  [score.to_f, jid]
157
216
  end
158
217
 
159
- SAFE_QPARAMS = %w(page poll)
218
+ SAFE_QPARAMS = %w[page direction]
160
219
 
161
220
  # Merge options with current params, filter safe params, and stringify to query string
162
221
  def qparams(options)
163
- options = options.stringify_keys
164
- params.merge(options).map do |key, value|
222
+ stringified_options = options.transform_keys(&:to_s)
223
+
224
+ to_query_string(params.merge(stringified_options))
225
+ end
226
+
227
+ def to_query_string(params)
228
+ params.map { |key, value|
165
229
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
166
- end.compact.join("&")
230
+ }.compact.join("&")
167
231
  end
168
232
 
169
233
  def truncate(text, truncate_after_chars = 2000)
@@ -171,33 +235,38 @@ module Sidekiq
171
235
  end
172
236
 
173
237
  def display_args(args, truncate_after_chars = 2000)
174
- args.map do |arg|
175
- h(truncate(to_display(arg), truncate_after_chars))
176
- end.join(", ")
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)
240
+
241
+ begin
242
+ args.map { |arg|
243
+ h(truncate(to_display(arg), truncate_after_chars))
244
+ }.join(", ")
245
+ rescue
246
+ "Illegal job arguments: #{h args.inspect}"
247
+ end
177
248
  end
178
249
 
179
250
  def csrf_tag
180
- "<input type='hidden' name='authenticity_token' value='#{session[:csrf]}'/>"
251
+ "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
181
252
  end
182
253
 
183
254
  def to_display(arg)
255
+ arg.inspect
256
+ rescue
184
257
  begin
185
- arg.inspect
186
- rescue
187
- begin
188
- arg.to_s
189
- rescue => ex
190
- "Cannot display argument: [#{ex.class.name}] #{ex.message}"
191
- end
258
+ arg.to_s
259
+ rescue => ex
260
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
192
261
  end
193
262
  end
194
263
 
195
- RETRY_JOB_KEYS = Set.new(%w(
264
+ RETRY_JOB_KEYS = Set.new(%w[
196
265
  queue class args retry_count retried_at failed_at
197
266
  jid error_message error_class backtrace
198
267
  error_backtrace enqueued_at retry wrapped
199
- created_at
200
- ))
268
+ created_at tags display_class
269
+ ])
201
270
 
202
271
  def retry_extra_items(retry_job)
203
272
  @retry_extra_items ||= {}.tap do |extra|
@@ -207,15 +276,29 @@ module Sidekiq
207
276
  end
208
277
  end
209
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
+
210
291
  def number_with_delimiter(number)
292
+ return "" if number.nil?
293
+
211
294
  begin
212
295
  Float(number)
213
296
  rescue ArgumentError, TypeError
214
297
  return number
215
298
  end
216
299
 
217
- options = {delimiter: ',', separator: '.'}
218
- parts = number.to_s.to_str.split('.')
300
+ options = {delimiter: ",", separator: "."}
301
+ parts = number.to_s.to_str.split(".")
219
302
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
220
303
  parts.join(options[:separator])
221
304
  end
@@ -223,8 +306,8 @@ module Sidekiq
223
306
  def h(text)
224
307
  ::Rack::Utils.escape_html(text)
225
308
  rescue ArgumentError => e
226
- raise unless e.message.eql?('invalid byte sequence in UTF-8')
227
- 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")
228
311
  retry
229
312
  end
230
313
 
@@ -241,7 +324,7 @@ module Sidekiq
241
324
  end
242
325
 
243
326
  def environment_title_prefix
244
- 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"
245
328
 
246
329
  "[#{environment.upcase}] " unless environment == "production"
247
330
  end
@@ -250,27 +333,31 @@ module Sidekiq
250
333
  "Sidekiq v#{Sidekiq::VERSION}"
251
334
  end
252
335
 
336
+ def server_utc_time
337
+ Time.now.utc.strftime("%H:%M:%S UTC")
338
+ end
339
+
253
340
  def redis_connection_and_namespace
254
341
  @redis_connection_and_namespace ||= begin
255
- namespace_suffix = namespace == nil ? '' : "##{namespace}"
342
+ namespace_suffix = namespace.nil? ? "" : "##{namespace}"
256
343
  "#{redis_connection}#{namespace_suffix}"
257
344
  end
258
345
  end
259
346
 
260
347
  def retry_or_delete_or_kill(job, params)
261
- if params['retry']
348
+ if params["retry"]
262
349
  job.retry
263
- elsif params['delete']
350
+ elsif params["delete"]
264
351
  job.delete
265
- elsif params['kill']
352
+ elsif params["kill"]
266
353
  job.kill
267
354
  end
268
355
  end
269
356
 
270
357
  def delete_or_add_queue(job, params)
271
- if params['delete']
358
+ if params["delete"]
272
359
  job.delete
273
- elsif params['add_to_queue']
360
+ elsif params["add_to_queue"]
274
361
  job.add_to_queue
275
362
  end
276
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'.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,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 = /\/([^\/]*):([^\.:$\/]+)/.freeze
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