sidekiq 5.2.9 → 6.2.1

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.

Potentially problematic release.


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

Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +232 -0
  3. data/README.md +18 -34
  4. data/bin/sidekiq +26 -2
  5. data/bin/sidekiqload +32 -24
  6. data/bin/sidekiqmon +8 -0
  7. data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
  8. data/lib/generators/sidekiq/worker_generator.rb +21 -13
  9. data/lib/sidekiq/api.rb +257 -219
  10. data/lib/sidekiq/cli.rb +144 -180
  11. data/lib/sidekiq/client.rb +64 -48
  12. data/lib/sidekiq/delay.rb +5 -6
  13. data/lib/sidekiq/exception_handler.rb +10 -12
  14. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  15. data/lib/sidekiq/extensions/active_record.rb +13 -10
  16. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  17. data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
  18. data/lib/sidekiq/fetch.rb +38 -31
  19. data/lib/sidekiq/job_logger.rb +45 -7
  20. data/lib/sidekiq/job_retry.rb +62 -61
  21. data/lib/sidekiq/launcher.rb +142 -52
  22. data/lib/sidekiq/logger.rb +166 -0
  23. data/lib/sidekiq/manager.rb +11 -13
  24. data/lib/sidekiq/middleware/chain.rb +15 -5
  25. data/lib/sidekiq/middleware/i18n.rb +5 -7
  26. data/lib/sidekiq/monitor.rb +133 -0
  27. data/lib/sidekiq/paginator.rb +18 -14
  28. data/lib/sidekiq/processor.rb +71 -70
  29. data/lib/sidekiq/rails.rb +29 -37
  30. data/lib/sidekiq/redis_connection.rb +50 -48
  31. data/lib/sidekiq/scheduled.rb +28 -29
  32. data/lib/sidekiq/sd_notify.rb +149 -0
  33. data/lib/sidekiq/systemd.rb +24 -0
  34. data/lib/sidekiq/testing/inline.rb +2 -1
  35. data/lib/sidekiq/testing.rb +35 -24
  36. data/lib/sidekiq/util.rb +45 -16
  37. data/lib/sidekiq/version.rb +2 -1
  38. data/lib/sidekiq/web/action.rb +15 -11
  39. data/lib/sidekiq/web/application.rb +84 -74
  40. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  41. data/lib/sidekiq/web/helpers.rb +108 -79
  42. data/lib/sidekiq/web/router.rb +23 -19
  43. data/lib/sidekiq/web.rb +60 -105
  44. data/lib/sidekiq/worker.rb +126 -102
  45. data/lib/sidekiq.rb +69 -44
  46. data/sidekiq.gemspec +23 -16
  47. data/web/assets/images/apple-touch-icon.png +0 -0
  48. data/web/assets/javascripts/application.js +25 -27
  49. data/web/assets/javascripts/dashboard.js +4 -23
  50. data/web/assets/stylesheets/application-dark.css +160 -0
  51. data/web/assets/stylesheets/application.css +33 -8
  52. data/web/locales/de.yml +14 -2
  53. data/web/locales/en.yml +2 -0
  54. data/web/locales/fr.yml +3 -3
  55. data/web/locales/ja.yml +4 -1
  56. data/web/locales/lt.yml +83 -0
  57. data/web/locales/pl.yml +4 -4
  58. data/web/locales/ru.yml +4 -0
  59. data/web/locales/vi.yml +83 -0
  60. data/web/views/_job_info.erb +2 -1
  61. data/web/views/busy.erb +51 -17
  62. data/web/views/dead.erb +2 -2
  63. data/web/views/layout.erb +2 -0
  64. data/web/views/morgue.erb +5 -2
  65. data/web/views/queue.erb +11 -2
  66. data/web/views/queues.erb +9 -1
  67. data/web/views/retries.erb +5 -2
  68. data/web/views/retry.erb +2 -2
  69. data/web/views/scheduled.erb +5 -2
  70. metadata +27 -60
  71. data/.circleci/config.yml +0 -61
  72. data/.github/contributing.md +0 -32
  73. data/.github/issue_template.md +0 -11
  74. data/.gitignore +0 -15
  75. data/.travis.yml +0 -11
  76. data/3.0-Upgrade.md +0 -70
  77. data/4.0-Upgrade.md +0 -53
  78. data/5.0-Upgrade.md +0 -56
  79. data/COMM-LICENSE +0 -97
  80. data/Ent-Changes.md +0 -238
  81. data/Gemfile +0 -23
  82. data/Pro-2.0-Upgrade.md +0 -138
  83. data/Pro-3.0-Upgrade.md +0 -44
  84. data/Pro-4.0-Upgrade.md +0 -35
  85. data/Pro-Changes.md +0 -759
  86. data/Rakefile +0 -9
  87. data/bin/sidekiqctl +0 -20
  88. data/code_of_conduct.md +0 -50
  89. data/lib/sidekiq/core_ext.rb +0 -1
  90. data/lib/sidekiq/ctl.rb +0 -221
  91. data/lib/sidekiq/logging.rb +0 -122
  92. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ # this file originally based on authenticity_token.rb from the sinatra/rack-protection project
4
+ #
5
+ # The MIT License (MIT)
6
+ #
7
+ # Copyright (c) 2011-2017 Konstantin Haase
8
+ # Copyright (c) 2015-2017 Zachary Scott
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining
11
+ # a copy of this software and associated documentation files (the
12
+ # 'Software'), to deal in the Software without restriction, including
13
+ # without limitation the rights to use, copy, modify, merge, publish,
14
+ # distribute, sublicense, and/or sell copies of the Software, and to
15
+ # permit persons to whom the Software is furnished to do so, subject to
16
+ # the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be
19
+ # included in all copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
22
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+
29
+ require "securerandom"
30
+ require "base64"
31
+ require "rack/request"
32
+
33
+ module Sidekiq
34
+ class Web
35
+ class CsrfProtection
36
+ def initialize(app, options = nil)
37
+ @app = app
38
+ end
39
+
40
+ def call(env)
41
+ accept?(env) ? admit(env) : deny(env)
42
+ end
43
+
44
+ private
45
+
46
+ def admit(env)
47
+ # On each successful request, we create a fresh masked token
48
+ # which will be used in any forms rendered for this request.
49
+ s = session(env)
50
+ s[:csrf] ||= SecureRandom.base64(TOKEN_LENGTH)
51
+ env[:csrf_token] = mask_token(s[:csrf])
52
+ @app.call(env)
53
+ end
54
+
55
+ def safe?(env)
56
+ %w[GET HEAD OPTIONS TRACE].include? env["REQUEST_METHOD"]
57
+ end
58
+
59
+ def logger(env)
60
+ @logger ||= (env["rack.logger"] || ::Logger.new(env["rack.errors"]))
61
+ end
62
+
63
+ def deny(env)
64
+ logger(env).warn "attack prevented by #{self.class}"
65
+ [403, {"Content-Type" => "text/plain"}, ["Forbidden"]]
66
+ end
67
+
68
+ def session(env)
69
+ env["rack.session"] || fail(<<~EOM)
70
+ Sidekiq::Web needs a valid Rack session for CSRF protection. If this is a Rails app,
71
+ make sure you mount Sidekiq::Web *inside* your application routes:
72
+
73
+
74
+ Rails.application.routes.draw do
75
+ mount Sidekiq::Web => "/sidekiq"
76
+ ....
77
+ end
78
+
79
+
80
+ If this is a Rails app in API mode, you need to enable sessions.
81
+
82
+ https://guides.rubyonrails.org/api_app.html#using-session-middlewares
83
+
84
+ If this is a bare Rack app, use a session middleware before Sidekiq::Web:
85
+
86
+ # first, use IRB to create a shared secret key for sessions and commit it
87
+ require 'securerandom'; File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }
88
+
89
+ # now use the secret with a session cookie middleware
90
+ use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
91
+ run Sidekiq::Web
92
+
93
+ EOM
94
+ end
95
+
96
+ def accept?(env)
97
+ return true if safe?(env)
98
+
99
+ giventoken = ::Rack::Request.new(env).params["authenticity_token"]
100
+ valid_token?(env, giventoken)
101
+ end
102
+
103
+ TOKEN_LENGTH = 32
104
+
105
+ # Checks that the token given to us as a parameter matches
106
+ # the token stored in the session.
107
+ def valid_token?(env, giventoken)
108
+ return false if giventoken.nil? || giventoken.empty?
109
+
110
+ begin
111
+ token = decode_token(giventoken)
112
+ rescue ArgumentError # client input is invalid
113
+ return false
114
+ end
115
+
116
+ sess = session(env)
117
+ localtoken = sess[:csrf]
118
+
119
+ # Checks that Rack::Session::Cookie actualy contains the csrf toekn
120
+ return false if localtoken.nil?
121
+
122
+ # Rotate the session token after every use
123
+ sess[:csrf] = SecureRandom.base64(TOKEN_LENGTH)
124
+
125
+ # See if it's actually a masked token or not. We should be able
126
+ # to handle any unmasked tokens that we've issued without error.
127
+
128
+ if unmasked_token?(token)
129
+ compare_with_real_token token, localtoken
130
+ elsif masked_token?(token)
131
+ unmasked = unmask_token(token)
132
+ compare_with_real_token unmasked, localtoken
133
+ else
134
+ false # Token is malformed
135
+ end
136
+ end
137
+
138
+ # Creates a masked version of the authenticity token that varies
139
+ # on each request. The masking is used to mitigate SSL attacks
140
+ # like BREACH.
141
+ def mask_token(token)
142
+ token = decode_token(token)
143
+ one_time_pad = SecureRandom.random_bytes(token.length)
144
+ encrypted_token = xor_byte_strings(one_time_pad, token)
145
+ masked_token = one_time_pad + encrypted_token
146
+ Base64.strict_encode64(masked_token)
147
+ end
148
+
149
+ # Essentially the inverse of +mask_token+.
150
+ def unmask_token(masked_token)
151
+ # Split the token into the one-time pad and the encrypted
152
+ # value and decrypt it
153
+ token_length = masked_token.length / 2
154
+ one_time_pad = masked_token[0...token_length]
155
+ encrypted_token = masked_token[token_length..-1]
156
+ xor_byte_strings(one_time_pad, encrypted_token)
157
+ end
158
+
159
+ def unmasked_token?(token)
160
+ token.length == TOKEN_LENGTH
161
+ end
162
+
163
+ def masked_token?(token)
164
+ token.length == TOKEN_LENGTH * 2
165
+ end
166
+
167
+ def compare_with_real_token(token, local)
168
+ ::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
169
+ end
170
+
171
+ def decode_token(token)
172
+ Base64.strict_decode64(token)
173
+ end
174
+
175
+ def xor_byte_strings(s1, s2)
176
+ s1.bytes.zip(s2.bytes).map { |(c1, c2)| c1 ^ c2 }.pack("c*")
177
+ end
178
+ end
179
+ end
180
+ end
@@ -1,15 +1,16 @@
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
12
+ @strings ||= {}
13
+ @strings[lang] ||= begin
13
14
  # Allow sidekiq-web extensions to add locale paths
14
15
  # so extensions can be localized
15
16
  settings.locales.each_with_object({}) do |path, global|
@@ -21,20 +22,28 @@ module Sidekiq
21
22
  end
22
23
  end
23
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
+
24
33
  def clear_caches
25
- @@strings = nil
26
- @@locale_files = nil
27
- @@available_locales = nil
34
+ @strings = nil
35
+ @locale_files = nil
36
+ @available_locales = nil
28
37
  end
29
38
 
30
39
  def locale_files
31
- @@locale_files ||= settings.locales.flat_map do |path|
40
+ @locale_files ||= settings.locales.flat_map { |path|
32
41
  Dir["#{path}/*.yml"]
33
- end
42
+ }
34
43
  end
35
44
 
36
45
  def available_locales
37
- @@available_locales ||= locale_files.map { |path| File.basename(path, '.yml') }.uniq
46
+ @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
38
47
  end
39
48
 
40
49
  def find_locale_files(lang)
@@ -63,32 +72,35 @@ module Sidekiq
63
72
  end
64
73
 
65
74
  def poll_path
66
- if current_path != '' && params['poll']
67
- 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
68
80
  else
69
81
  ""
70
82
  end
71
83
  end
72
84
 
73
85
  def text_direction
74
- get_locale['TextDirection'] || 'ltr'
86
+ get_locale["TextDirection"] || "ltr"
75
87
  end
76
88
 
77
89
  def rtl?
78
- text_direction == 'rtl'
90
+ text_direction == "rtl"
79
91
  end
80
92
 
81
93
  # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
82
94
  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
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
87
99
  quality = quality ? quality.to_f : 1.0
88
100
  [locale, quality]
89
- end.sort do |(_, left), (_, right)|
101
+ }.sort { |(_, left), (_, right)|
90
102
  right <=> left
91
- end.map(&:first).compact
103
+ }.map(&:first).compact
92
104
  end
93
105
 
94
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"
@@ -97,31 +109,38 @@ module Sidekiq
97
109
  # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
98
110
  def locale
99
111
  @locale ||= begin
100
- matched_locale = user_preferred_languages.map do |preferred|
101
- preferred_language = preferred.split('-', 2).first
112
+ matched_locale = user_preferred_languages.map { |preferred|
113
+ preferred_language = preferred.split("-", 2).first
102
114
 
103
- lang_group = available_locales.select do |available|
104
- preferred_language == available.split('-', 2).first
105
- end
115
+ lang_group = available_locales.select { |available|
116
+ preferred_language == available.split("-", 2).first
117
+ }
106
118
 
107
119
  lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
108
- end.compact.first
120
+ }.compact.first
109
121
 
110
- matched_locale || 'en'
122
+ matched_locale || "en"
111
123
  end
112
124
  end
113
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
+
114
133
  # mperham/sidekiq#3243
115
134
  def unfiltered?
116
- yield unless env['PATH_INFO'].start_with?("/filter/")
135
+ yield unless env["PATH_INFO"].start_with?("/filter/")
117
136
  end
118
137
 
119
138
  def get_locale
120
139
  strings(locale)
121
140
  end
122
141
 
123
- def t(msg, options={})
124
- string = get_locale[msg] || strings('en')[msg] || msg
142
+ def t(msg, options = {})
143
+ string = get_locale[msg] || strings("en")[msg] || msg
125
144
  if options.empty?
126
145
  string
127
146
  else
@@ -129,6 +148,10 @@ module Sidekiq
129
148
  end
130
149
  end
131
150
 
151
+ def sort_direction_label
152
+ params[:direction] == "asc" ? "&uarr;" : "&darr;"
153
+ end
154
+
132
155
  def workers
133
156
  @workers ||= Sidekiq::Workers.new
134
157
  end
@@ -141,16 +164,9 @@ module Sidekiq
141
164
  @stats ||= Sidekiq::Stats.new
142
165
  end
143
166
 
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) }
148
- end
149
-
150
167
  def redis_connection
151
168
  Sidekiq.redis do |conn|
152
- c = conn.connection
153
- "redis://#{c[:location]}/#{c[:db]}"
169
+ conn.connection[:id]
154
170
  end
155
171
  end
156
172
 
@@ -163,24 +179,24 @@ module Sidekiq
163
179
  end
164
180
 
165
181
  def root_path
166
- "#{env['SCRIPT_NAME']}/"
182
+ "#{env["SCRIPT_NAME"]}/"
167
183
  end
168
184
 
169
185
  def current_path
170
- @current_path ||= request.path_info.gsub(/^\//,'')
186
+ @current_path ||= request.path_info.gsub(/^\//, "")
171
187
  end
172
188
 
173
189
  def current_status
174
- workers.size == 0 ? 'idle' : 'active'
190
+ workers.size == 0 ? "idle" : "active"
175
191
  end
176
192
 
177
193
  def relative_time(time)
178
194
  stamp = time.getutc.iso8601
179
- %{<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>}
195
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
180
196
  end
181
197
 
182
198
  def job_params(job, score)
183
- "#{score}-#{job['jid']}"
199
+ "#{score}-#{job["jid"]}"
184
200
  end
185
201
 
186
202
  def parse_params(params)
@@ -188,18 +204,19 @@ module Sidekiq
188
204
  [score.to_f, jid]
189
205
  end
190
206
 
191
- SAFE_QPARAMS = %w(page poll)
207
+ SAFE_QPARAMS = %w[page poll direction]
192
208
 
193
209
  # Merge options with current params, filter safe params, and stringify to query string
194
210
  def qparams(options)
195
- # stringify
196
- options.keys.each do |key|
197
- options[key.to_s] = options.delete(key)
198
- end
211
+ stringified_options = options.transform_keys(&:to_s)
212
+
213
+ to_query_string(params.merge(stringified_options))
214
+ end
199
215
 
200
- params.merge(options).map do |key, value|
216
+ def to_query_string(params)
217
+ params.map { |key, value|
201
218
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
202
- end.compact.join("&")
219
+ }.compact.join("&")
203
220
  end
204
221
 
205
222
  def truncate(text, truncate_after_chars = 2000)
@@ -207,40 +224,38 @@ module Sidekiq
207
224
  end
208
225
 
209
226
  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)
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)
212
229
 
213
230
  begin
214
- args.map do |arg|
231
+ args.map { |arg|
215
232
  h(truncate(to_display(arg), truncate_after_chars))
216
- end.join(", ")
233
+ }.join(", ")
217
234
  rescue
218
235
  "Illegal job arguments: #{h args.inspect}"
219
236
  end
220
237
  end
221
238
 
222
239
  def csrf_tag
223
- "<input type='hidden' name='authenticity_token' value='#{session[:csrf]}'/>"
240
+ "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
224
241
  end
225
242
 
226
243
  def to_display(arg)
244
+ arg.inspect
245
+ rescue
227
246
  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
247
+ arg.to_s
248
+ rescue => ex
249
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
235
250
  end
236
251
  end
237
252
 
238
- RETRY_JOB_KEYS = Set.new(%w(
253
+ RETRY_JOB_KEYS = Set.new(%w[
239
254
  queue class args retry_count retried_at failed_at
240
255
  jid error_message error_class backtrace
241
256
  error_backtrace enqueued_at retry wrapped
242
- created_at
243
- ))
257
+ created_at tags
258
+ ])
244
259
 
245
260
  def retry_extra_items(retry_job)
246
261
  @retry_extra_items ||= {}.tap do |extra|
@@ -250,15 +265,29 @@ module Sidekiq
250
265
  end
251
266
  end
252
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
+
253
280
  def number_with_delimiter(number)
281
+ return "" if number.nil?
282
+
254
283
  begin
255
284
  Float(number)
256
285
  rescue ArgumentError, TypeError
257
286
  return number
258
287
  end
259
288
 
260
- options = {delimiter: ',', separator: '.'}
261
- parts = number.to_s.to_str.split('.')
289
+ options = {delimiter: ",", separator: "."}
290
+ parts = number.to_s.to_str.split(".")
262
291
  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
263
292
  parts.join(options[:separator])
264
293
  end
@@ -266,8 +295,8 @@ module Sidekiq
266
295
  def h(text)
267
296
  ::Rack::Utils.escape_html(text)
268
297
  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')
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")
271
300
  retry
272
301
  end
273
302
 
@@ -284,7 +313,7 @@ module Sidekiq
284
313
  end
285
314
 
286
315
  def environment_title_prefix
287
- 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"
288
317
 
289
318
  "[#{environment.upcase}] " unless environment == "production"
290
319
  end
@@ -294,30 +323,30 @@ module Sidekiq
294
323
  end
295
324
 
296
325
  def server_utc_time
297
- Time.now.utc.strftime('%H:%M:%S UTC')
326
+ Time.now.utc.strftime("%H:%M:%S UTC")
298
327
  end
299
328
 
300
329
  def redis_connection_and_namespace
301
330
  @redis_connection_and_namespace ||= begin
302
- namespace_suffix = namespace == nil ? '' : "##{namespace}"
331
+ namespace_suffix = namespace.nil? ? "" : "##{namespace}"
303
332
  "#{redis_connection}#{namespace_suffix}"
304
333
  end
305
334
  end
306
335
 
307
336
  def retry_or_delete_or_kill(job, params)
308
- if params['retry']
337
+ if params["retry"]
309
338
  job.retry
310
- elsif params['delete']
339
+ elsif params["delete"]
311
340
  job.delete
312
- elsif params['kill']
341
+ elsif params["kill"]
313
342
  job.kill
314
343
  end
315
344
  end
316
345
 
317
346
  def delete_or_add_queue(job, params)
318
- if params['delete']
347
+ if params["delete"]
319
348
  job.delete
320
- elsif params['add_to_queue']
349
+ elsif params["add_to_queue"]
321
350
  job.add_to_queue
322
351
  end
323
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'
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