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.
- checksums.yaml +5 -5
- data/Changes.md +445 -0
- data/LICENSE +1 -1
- data/README.md +21 -34
- data/bin/sidekiq +26 -2
- data/bin/sidekiqload +28 -38
- data/bin/sidekiqmon +8 -0
- data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +2 -2
- data/lib/generators/sidekiq/worker_generator.rb +21 -13
- data/lib/sidekiq/api.rb +347 -213
- data/lib/sidekiq/cli.rb +221 -212
- data/lib/sidekiq/client.rb +75 -52
- data/lib/sidekiq/delay.rb +41 -0
- data/lib/sidekiq/exception_handler.rb +12 -16
- data/lib/sidekiq/extensions/action_mailer.rb +13 -22
- data/lib/sidekiq/extensions/active_record.rb +13 -10
- data/lib/sidekiq/extensions/class_methods.rb +14 -11
- data/lib/sidekiq/extensions/generic_proxy.rb +10 -4
- data/lib/sidekiq/fetch.rb +38 -31
- data/lib/sidekiq/job_logger.rb +63 -0
- data/lib/sidekiq/job_retry.rb +263 -0
- data/lib/sidekiq/launcher.rb +169 -70
- data/lib/sidekiq/logger.rb +166 -0
- data/lib/sidekiq/manager.rb +17 -20
- data/lib/sidekiq/middleware/chain.rb +15 -5
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +133 -0
- data/lib/sidekiq/paginator.rb +18 -14
- data/lib/sidekiq/processor.rb +161 -70
- data/lib/sidekiq/rails.rb +30 -73
- data/lib/sidekiq/redis_connection.rb +67 -20
- data/lib/sidekiq/scheduled.rb +61 -35
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +24 -0
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/testing.rb +54 -26
- data/lib/sidekiq/util.rb +48 -15
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +15 -15
- data/lib/sidekiq/web/application.rb +112 -89
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +153 -73
- data/lib/sidekiq/web/router.rb +27 -19
- data/lib/sidekiq/web.rb +64 -109
- data/lib/sidekiq/worker.rb +164 -41
- data/lib/sidekiq.rb +86 -60
- data/sidekiq.gemspec +24 -22
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +25 -27
- data/web/assets/javascripts/dashboard.js +34 -38
- data/web/assets/stylesheets/application-dark.css +160 -0
- data/web/assets/stylesheets/application-rtl.css +246 -0
- data/web/assets/stylesheets/application.css +402 -12
- data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +81 -0
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +4 -0
- data/web/locales/es.yml +4 -3
- data/web/locales/fa.yml +80 -0
- data/web/locales/fr.yml +3 -3
- data/web/locales/he.yml +79 -0
- data/web/locales/ja.yml +9 -4
- data/web/locales/lt.yml +83 -0
- data/web/locales/pl.yml +4 -4
- data/web/locales/ru.yml +4 -0
- data/web/locales/ur.yml +80 -0
- data/web/locales/vi.yml +83 -0
- data/web/views/_footer.erb +5 -2
- data/web/views/_job_info.erb +3 -2
- data/web/views/_nav.erb +4 -18
- data/web/views/_paging.erb +1 -1
- data/web/views/busy.erb +57 -19
- data/web/views/dashboard.erb +3 -3
- data/web/views/dead.erb +2 -2
- data/web/views/layout.erb +13 -2
- data/web/views/morgue.erb +19 -12
- data/web/views/queue.erb +22 -12
- data/web/views/queues.erb +13 -3
- data/web/views/retries.erb +22 -13
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +7 -4
- metadata +42 -194
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -4
- data/.gitignore +0 -12
- data/.travis.yml +0 -12
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/COMM-LICENSE +0 -95
- data/Ent-Changes.md +0 -146
- data/Gemfile +0 -29
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-Changes.md +0 -585
- data/Rakefile +0 -9
- data/bin/sidekiqctl +0 -99
- data/code_of_conduct.md +0 -50
- data/lib/sidekiq/core_ext.rb +0 -106
- data/lib/sidekiq/logging.rb +0 -106
- data/lib/sidekiq/middleware/server/active_record.rb +0 -13
- data/lib/sidekiq/middleware/server/logging.rb +0 -40
- data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
- data/test/config.yml +0 -9
- data/test/env_based_config.yml +0 -11
- data/test/fake_env.rb +0 -1
- data/test/fixtures/en.yml +0 -2
- data/test/helper.rb +0 -75
- data/test/test_actors.rb +0 -138
- data/test/test_api.rb +0 -528
- data/test/test_cli.rb +0 -418
- data/test/test_client.rb +0 -266
- data/test/test_exception_handler.rb +0 -56
- data/test/test_extensions.rb +0 -127
- data/test/test_fetch.rb +0 -50
- data/test/test_launcher.rb +0 -95
- data/test/test_logging.rb +0 -35
- data/test/test_manager.rb +0 -50
- data/test/test_middleware.rb +0 -158
- data/test/test_processor.rb +0 -235
- data/test/test_rails.rb +0 -22
- data/test/test_redis_connection.rb +0 -132
- data/test/test_retry.rb +0 -326
- data/test/test_retry_exhausted.rb +0 -149
- data/test/test_scheduled.rb +0 -115
- data/test/test_scheduling.rb +0 -58
- data/test/test_sidekiq.rb +0 -107
- data/test/test_testing.rb +0 -143
- data/test/test_testing_fake.rb +0 -357
- data/test/test_testing_inline.rb +0 -94
- data/test/test_util.rb +0 -13
- data/test/test_web.rb +0 -726
- data/test/test_web_helpers.rb +0 -54
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -1,33 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require
|
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
|
-
|
10
|
-
|
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.
|
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
|
-
|
24
|
-
|
34
|
+
@strings = nil
|
35
|
+
@locale_files = nil
|
36
|
+
@available_locales = nil
|
25
37
|
end
|
26
38
|
|
27
39
|
def locale_files
|
28
|
-
|
40
|
+
@locale_files ||= settings.locales.flat_map { |path|
|
29
41
|
Dir["#{path}/*.yml"]
|
30
|
-
|
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 !=
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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" ? "↑" : "↓"
|
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
|
168
|
+
Sidekiq.redis do |conn|
|
169
|
+
conn.connection[:id]
|
170
|
+
end
|
120
171
|
end
|
121
172
|
|
122
173
|
def namespace
|
123
|
-
|
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[
|
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 ?
|
190
|
+
workers.size == 0 ? "idle" : "active"
|
140
191
|
end
|
141
192
|
|
142
193
|
def relative_time(time)
|
143
|
-
|
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[
|
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
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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
|
171
|
-
|
172
|
-
|
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='#{
|
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.
|
182
|
-
rescue
|
183
|
-
|
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:
|
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?(
|
223
|
-
text.encode!(
|
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[
|
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
|
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[
|
337
|
+
if params["retry"]
|
258
338
|
job.retry
|
259
|
-
elsif params[
|
339
|
+
elsif params["delete"]
|
260
340
|
job.delete
|
261
|
-
elsif params[
|
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[
|
347
|
+
if params["delete"]
|
268
348
|
job.delete
|
269
|
-
elsif params[
|
349
|
+
elsif params["add_to_queue"]
|
270
350
|
job.add_to_queue
|
271
351
|
end
|
272
352
|
end
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require "rack"
|
3
4
|
|
4
5
|
module Sidekiq
|
5
6
|
module WebRouter
|
6
|
-
GET =
|
7
|
-
DELETE =
|
8
|
-
POST =
|
9
|
-
PUT =
|
10
|
-
PATCH =
|
11
|
-
HEAD =
|
12
|
-
|
13
|
-
ROUTE_PARAMS =
|
14
|
-
REQUEST_METHOD =
|
15
|
-
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,18 +40,22 @@ module Sidekiq
|
|
35
40
|
end
|
36
41
|
|
37
42
|
def route(method, path, &block)
|
38
|
-
@routes ||= {
|
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
|
-
|
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 = /\/([^\/]*):([
|
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
|
-
|
91
|
-
|
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
|