sidekiq 4.2.2 → 6.3.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 +516 -0
- data/LICENSE +2 -2
- data/README.md +23 -36
- 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 +401 -243
- data/lib/sidekiq/cli.rb +228 -212
- data/lib/sidekiq/client.rb +76 -53
- 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 +12 -4
- data/lib/sidekiq/fetch.rb +39 -31
- data/lib/sidekiq/job.rb +13 -0
- data/lib/sidekiq/job_logger.rb +63 -0
- data/lib/sidekiq/job_retry.rb +259 -0
- data/lib/sidekiq/launcher.rb +170 -71
- data/lib/sidekiq/logger.rb +166 -0
- data/lib/sidekiq/manager.rb +17 -20
- data/lib/sidekiq/middleware/chain.rb +20 -8
- data/lib/sidekiq/middleware/current_attributes.rb +52 -0
- 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 +169 -78
- data/lib/sidekiq/rails.rb +41 -36
- data/lib/sidekiq/redis_connection.rb +65 -20
- data/lib/sidekiq/scheduled.rb +85 -34
- 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 +52 -26
- data/lib/sidekiq/util.rb +48 -15
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +15 -17
- data/lib/sidekiq/web/application.rb +114 -92
- data/lib/sidekiq/web/csrf_protection.rb +180 -0
- data/lib/sidekiq/web/helpers.rb +151 -83
- data/lib/sidekiq/web/router.rb +27 -19
- data/lib/sidekiq/web.rb +85 -76
- data/lib/sidekiq/worker.rb +233 -43
- data/lib/sidekiq.rb +88 -64
- data/sidekiq.gemspec +24 -22
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +86 -59
- data/web/assets/javascripts/dashboard.js +81 -85
- data/web/assets/stylesheets/application-dark.css +147 -0
- data/web/assets/stylesheets/application-rtl.css +242 -0
- data/web/assets/stylesheets/application.css +319 -141
- data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +87 -0
- data/web/locales/de.yml +14 -2
- data/web/locales/en.yml +8 -1
- data/web/locales/es.yml +22 -5
- data/web/locales/fa.yml +80 -0
- data/web/locales/fr.yml +10 -3
- data/web/locales/he.yml +79 -0
- data/web/locales/ja.yml +12 -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 +4 -3
- data/web/views/_nav.erb +4 -18
- data/web/views/_paging.erb +1 -1
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +60 -22
- data/web/views/dashboard.erb +23 -15
- data/web/views/dead.erb +3 -3
- data/web/views/layout.erb +14 -3
- data/web/views/morgue.erb +19 -12
- data/web/views/queue.erb +24 -14
- data/web/views/queues.erb +14 -4
- data/web/views/retries.erb +22 -13
- data/web/views/retry.erb +4 -4
- data/web/views/scheduled.erb +7 -4
- metadata +44 -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 -570
- 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 -201
- 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 -50
- 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 -666
- data/test/test_web_helpers.rb +0 -54
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -1,33 +1,48 @@
|
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
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.load(File.open(file))
|
19
|
+
global.merge!(strs[lang])
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
21
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
|
+
|
22
32
|
def clear_caches
|
23
|
-
|
24
|
-
|
33
|
+
@strings = nil
|
34
|
+
@locale_files = nil
|
35
|
+
@available_locales = nil
|
25
36
|
end
|
26
37
|
|
27
38
|
def locale_files
|
28
|
-
|
39
|
+
@locale_files ||= settings.locales.flat_map { |path|
|
29
40
|
Dir["#{path}/*.yml"]
|
30
|
-
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def available_locales
|
45
|
+
@available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
|
31
46
|
end
|
32
47
|
|
33
48
|
def find_locale_files(lang)
|
@@ -55,37 +70,65 @@ module Sidekiq
|
|
55
70
|
@head_html.join if defined?(@head_html)
|
56
71
|
end
|
57
72
|
|
58
|
-
def
|
59
|
-
|
60
|
-
root_path + current_path
|
61
|
-
else
|
62
|
-
""
|
63
|
-
end
|
73
|
+
def text_direction
|
74
|
+
get_locale["TextDirection"] || "ltr"
|
64
75
|
end
|
65
76
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
92
|
+
end
|
93
|
+
|
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
|
70
98
|
def locale
|
71
99
|
@locale ||= begin
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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"
|
80
111
|
end
|
81
112
|
end
|
82
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
|
+
|
121
|
+
# mperham/sidekiq#3243
|
122
|
+
def unfiltered?
|
123
|
+
yield unless env["PATH_INFO"].start_with?("/filter/")
|
124
|
+
end
|
125
|
+
|
83
126
|
def get_locale
|
84
127
|
strings(locale)
|
85
128
|
end
|
86
129
|
|
87
|
-
def t(msg, options={})
|
88
|
-
string = get_locale[msg] || msg
|
130
|
+
def t(msg, options = {})
|
131
|
+
string = get_locale[msg] || strings("en")[msg] || msg
|
89
132
|
if options.empty?
|
90
133
|
string
|
91
134
|
else
|
@@ -93,6 +136,10 @@ module Sidekiq
|
|
93
136
|
end
|
94
137
|
end
|
95
138
|
|
139
|
+
def sort_direction_label
|
140
|
+
params[:direction] == "asc" ? "↑" : "↓"
|
141
|
+
end
|
142
|
+
|
96
143
|
def workers
|
97
144
|
@workers ||= Sidekiq::Workers.new
|
98
145
|
end
|
@@ -105,22 +152,14 @@ module Sidekiq
|
|
105
152
|
@stats ||= Sidekiq::Stats.new
|
106
153
|
end
|
107
154
|
|
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
155
|
def redis_connection
|
119
|
-
Sidekiq.redis
|
156
|
+
Sidekiq.redis do |conn|
|
157
|
+
conn.connection[:id]
|
158
|
+
end
|
120
159
|
end
|
121
160
|
|
122
161
|
def namespace
|
123
|
-
|
162
|
+
@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
|
124
163
|
end
|
125
164
|
|
126
165
|
def redis_info
|
@@ -128,38 +167,44 @@ module Sidekiq
|
|
128
167
|
end
|
129
168
|
|
130
169
|
def root_path
|
131
|
-
"#{env[
|
170
|
+
"#{env["SCRIPT_NAME"]}/"
|
132
171
|
end
|
133
172
|
|
134
173
|
def current_path
|
135
|
-
@current_path ||= request.path_info.gsub(/^\//,
|
174
|
+
@current_path ||= request.path_info.gsub(/^\//, "")
|
136
175
|
end
|
137
176
|
|
138
177
|
def current_status
|
139
|
-
workers.size == 0 ?
|
178
|
+
workers.size == 0 ? "idle" : "active"
|
140
179
|
end
|
141
180
|
|
142
181
|
def relative_time(time)
|
143
|
-
|
182
|
+
stamp = time.getutc.iso8601
|
183
|
+
%(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
|
144
184
|
end
|
145
185
|
|
146
186
|
def job_params(job, score)
|
147
|
-
"#{score}-#{job[
|
187
|
+
"#{score}-#{job["jid"]}"
|
148
188
|
end
|
149
189
|
|
150
190
|
def parse_params(params)
|
151
|
-
score, jid = params.split("-")
|
191
|
+
score, jid = params.split("-", 2)
|
152
192
|
[score.to_f, jid]
|
153
193
|
end
|
154
194
|
|
155
|
-
SAFE_QPARAMS = %w
|
195
|
+
SAFE_QPARAMS = %w[page direction]
|
156
196
|
|
157
197
|
# Merge options with current params, filter safe params, and stringify to query string
|
158
198
|
def qparams(options)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
199
|
+
stringified_options = options.transform_keys(&:to_s)
|
200
|
+
|
201
|
+
to_query_string(params.merge(stringified_options))
|
202
|
+
end
|
203
|
+
|
204
|
+
def to_query_string(params)
|
205
|
+
params.map { |key, value|
|
206
|
+
SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
|
207
|
+
}.compact.join("&")
|
163
208
|
end
|
164
209
|
|
165
210
|
def truncate(text, truncate_after_chars = 2000)
|
@@ -167,33 +212,38 @@ module Sidekiq
|
|
167
212
|
end
|
168
213
|
|
169
214
|
def display_args(args, truncate_after_chars = 2000)
|
170
|
-
args
|
171
|
-
|
172
|
-
|
215
|
+
return "Invalid job payload, args is nil" if args.nil?
|
216
|
+
return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
|
217
|
+
|
218
|
+
begin
|
219
|
+
args.map { |arg|
|
220
|
+
h(truncate(to_display(arg), truncate_after_chars))
|
221
|
+
}.join(", ")
|
222
|
+
rescue
|
223
|
+
"Illegal job arguments: #{h args.inspect}"
|
224
|
+
end
|
173
225
|
end
|
174
226
|
|
175
227
|
def csrf_tag
|
176
|
-
"<input type='hidden' name='authenticity_token' value='#{
|
228
|
+
"<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
|
177
229
|
end
|
178
230
|
|
179
231
|
def to_display(arg)
|
232
|
+
arg.inspect
|
233
|
+
rescue
|
180
234
|
begin
|
181
|
-
arg.
|
182
|
-
rescue
|
183
|
-
|
184
|
-
arg.to_s
|
185
|
-
rescue => ex
|
186
|
-
"Cannot display argument: [#{ex.class.name}] #{ex.message}"
|
187
|
-
end
|
235
|
+
arg.to_s
|
236
|
+
rescue => ex
|
237
|
+
"Cannot display argument: [#{ex.class.name}] #{ex.message}"
|
188
238
|
end
|
189
239
|
end
|
190
240
|
|
191
|
-
RETRY_JOB_KEYS = Set.new(%w
|
241
|
+
RETRY_JOB_KEYS = Set.new(%w[
|
192
242
|
queue class args retry_count retried_at failed_at
|
193
243
|
jid error_message error_class backtrace
|
194
244
|
error_backtrace enqueued_at retry wrapped
|
195
|
-
created_at
|
196
|
-
)
|
245
|
+
created_at tags
|
246
|
+
])
|
197
247
|
|
198
248
|
def retry_extra_items(retry_job)
|
199
249
|
@retry_extra_items ||= {}.tap do |extra|
|
@@ -203,15 +253,29 @@ module Sidekiq
|
|
203
253
|
end
|
204
254
|
end
|
205
255
|
|
256
|
+
def format_memory(rss_kb)
|
257
|
+
return "0" if rss_kb.nil? || rss_kb == 0
|
258
|
+
|
259
|
+
if rss_kb < 100_000
|
260
|
+
"#{number_with_delimiter(rss_kb)} KB"
|
261
|
+
elsif rss_kb < 10_000_000
|
262
|
+
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
263
|
+
else
|
264
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
206
268
|
def number_with_delimiter(number)
|
269
|
+
return "" if number.nil?
|
270
|
+
|
207
271
|
begin
|
208
272
|
Float(number)
|
209
273
|
rescue ArgumentError, TypeError
|
210
274
|
return number
|
211
275
|
end
|
212
276
|
|
213
|
-
options = {delimiter:
|
214
|
-
parts = number.to_s.to_str.split(
|
277
|
+
options = {delimiter: ",", separator: "."}
|
278
|
+
parts = number.to_s.to_str.split(".")
|
215
279
|
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
216
280
|
parts.join(options[:separator])
|
217
281
|
end
|
@@ -219,8 +283,8 @@ module Sidekiq
|
|
219
283
|
def h(text)
|
220
284
|
::Rack::Utils.escape_html(text)
|
221
285
|
rescue ArgumentError => e
|
222
|
-
raise unless e.message.eql?(
|
223
|
-
text.encode!(
|
286
|
+
raise unless e.message.eql?("invalid byte sequence in UTF-8")
|
287
|
+
text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
|
224
288
|
retry
|
225
289
|
end
|
226
290
|
|
@@ -237,7 +301,7 @@ module Sidekiq
|
|
237
301
|
end
|
238
302
|
|
239
303
|
def environment_title_prefix
|
240
|
-
environment = Sidekiq.options[:environment] || ENV[
|
304
|
+
environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
241
305
|
|
242
306
|
"[#{environment.upcase}] " unless environment == "production"
|
243
307
|
end
|
@@ -246,27 +310,31 @@ module Sidekiq
|
|
246
310
|
"Sidekiq v#{Sidekiq::VERSION}"
|
247
311
|
end
|
248
312
|
|
313
|
+
def server_utc_time
|
314
|
+
Time.now.utc.strftime("%H:%M:%S UTC")
|
315
|
+
end
|
316
|
+
|
249
317
|
def redis_connection_and_namespace
|
250
318
|
@redis_connection_and_namespace ||= begin
|
251
|
-
namespace_suffix = namespace
|
319
|
+
namespace_suffix = namespace.nil? ? "" : "##{namespace}"
|
252
320
|
"#{redis_connection}#{namespace_suffix}"
|
253
321
|
end
|
254
322
|
end
|
255
323
|
|
256
324
|
def retry_or_delete_or_kill(job, params)
|
257
|
-
if params[
|
325
|
+
if params["retry"]
|
258
326
|
job.retry
|
259
|
-
elsif params[
|
327
|
+
elsif params["delete"]
|
260
328
|
job.delete
|
261
|
-
elsif params[
|
329
|
+
elsif params["kill"]
|
262
330
|
job.kill
|
263
331
|
end
|
264
332
|
end
|
265
333
|
|
266
334
|
def delete_or_add_queue(job, params)
|
267
|
-
if params[
|
335
|
+
if params["delete"]
|
268
336
|
job.delete
|
269
|
-
elsif params[
|
337
|
+
elsif params["add_to_queue"]
|
270
338
|
job.add_to_queue
|
271
339
|
end
|
272
340
|
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
|