sidekiq 3.5.4 → 7.2.0

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 (228) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +992 -6
  3. data/LICENSE.txt +9 -0
  4. data/README.md +52 -43
  5. data/bin/sidekiq +22 -4
  6. data/bin/sidekiqload +209 -115
  7. data/bin/sidekiqmon +11 -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/job_test.rb.erb +8 -0
  12. data/lib/sidekiq/api.rb +633 -295
  13. data/lib/sidekiq/capsule.rb +127 -0
  14. data/lib/sidekiq/cli.rb +270 -248
  15. data/lib/sidekiq/client.rb +139 -108
  16. data/lib/sidekiq/component.rb +68 -0
  17. data/lib/sidekiq/config.rb +287 -0
  18. data/lib/sidekiq/deploy.rb +62 -0
  19. data/lib/sidekiq/embedded.rb +61 -0
  20. data/lib/sidekiq/fetch.rb +53 -121
  21. data/lib/sidekiq/job.rb +374 -0
  22. data/lib/sidekiq/job_logger.rb +51 -0
  23. data/lib/sidekiq/job_retry.rb +301 -0
  24. data/lib/sidekiq/job_util.rb +107 -0
  25. data/lib/sidekiq/launcher.rb +241 -69
  26. data/lib/sidekiq/logger.rb +131 -0
  27. data/lib/sidekiq/manager.rb +88 -190
  28. data/lib/sidekiq/metrics/query.rb +155 -0
  29. data/lib/sidekiq/metrics/shared.rb +95 -0
  30. data/lib/sidekiq/metrics/tracking.rb +136 -0
  31. data/lib/sidekiq/middleware/chain.rb +114 -56
  32. data/lib/sidekiq/middleware/current_attributes.rb +95 -0
  33. data/lib/sidekiq/middleware/i18n.rb +8 -7
  34. data/lib/sidekiq/middleware/modules.rb +21 -0
  35. data/lib/sidekiq/monitor.rb +146 -0
  36. data/lib/sidekiq/paginator.rb +29 -16
  37. data/lib/sidekiq/processor.rb +238 -118
  38. data/lib/sidekiq/rails.rb +57 -27
  39. data/lib/sidekiq/redis_client_adapter.rb +111 -0
  40. data/lib/sidekiq/redis_connection.rb +49 -50
  41. data/lib/sidekiq/ring_buffer.rb +29 -0
  42. data/lib/sidekiq/scheduled.rb +173 -52
  43. data/lib/sidekiq/sd_notify.rb +149 -0
  44. data/lib/sidekiq/systemd.rb +24 -0
  45. data/lib/sidekiq/testing/inline.rb +7 -5
  46. data/lib/sidekiq/testing.rb +197 -65
  47. data/lib/sidekiq/transaction_aware_client.rb +44 -0
  48. data/lib/sidekiq/version.rb +4 -1
  49. data/lib/sidekiq/web/action.rb +93 -0
  50. data/lib/sidekiq/web/application.rb +463 -0
  51. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  52. data/lib/sidekiq/web/helpers.rb +364 -0
  53. data/lib/sidekiq/web/router.rb +104 -0
  54. data/lib/sidekiq/web.rb +113 -216
  55. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  56. data/lib/sidekiq.rb +99 -142
  57. data/sidekiq.gemspec +26 -23
  58. data/web/assets/images/apple-touch-icon.png +0 -0
  59. data/web/assets/javascripts/application.js +163 -74
  60. data/web/assets/javascripts/base-charts.js +106 -0
  61. data/web/assets/javascripts/chart.min.js +13 -0
  62. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  63. data/web/assets/javascripts/dashboard-charts.js +182 -0
  64. data/web/assets/javascripts/dashboard.js +37 -280
  65. data/web/assets/javascripts/metrics.js +298 -0
  66. data/web/assets/stylesheets/application-dark.css +147 -0
  67. data/web/assets/stylesheets/application-rtl.css +153 -0
  68. data/web/assets/stylesheets/application.css +181 -198
  69. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  70. data/web/assets/stylesheets/bootstrap.css +4 -8
  71. data/web/locales/ar.yml +87 -0
  72. data/web/locales/cs.yml +62 -52
  73. data/web/locales/da.yml +60 -53
  74. data/web/locales/de.yml +65 -53
  75. data/web/locales/el.yml +43 -24
  76. data/web/locales/en.yml +86 -62
  77. data/web/locales/es.yml +70 -53
  78. data/web/locales/fa.yml +80 -0
  79. data/web/locales/fr.yml +86 -56
  80. data/web/locales/gd.yml +99 -0
  81. data/web/locales/he.yml +80 -0
  82. data/web/locales/hi.yml +59 -59
  83. data/web/locales/it.yml +53 -53
  84. data/web/locales/ja.yml +78 -56
  85. data/web/locales/ko.yml +52 -52
  86. data/web/locales/lt.yml +83 -0
  87. data/web/locales/nb.yml +61 -61
  88. data/web/locales/nl.yml +52 -52
  89. data/web/locales/pl.yml +45 -45
  90. data/web/locales/pt-br.yml +83 -55
  91. data/web/locales/pt.yml +51 -51
  92. data/web/locales/ru.yml +68 -60
  93. data/web/locales/sv.yml +53 -53
  94. data/web/locales/ta.yml +60 -60
  95. data/web/locales/uk.yml +62 -61
  96. data/web/locales/ur.yml +80 -0
  97. data/web/locales/vi.yml +83 -0
  98. data/web/locales/zh-cn.yml +43 -16
  99. data/web/locales/zh-tw.yml +42 -8
  100. data/web/views/_footer.erb +10 -9
  101. data/web/views/_job_info.erb +26 -5
  102. data/web/views/_metrics_period_select.erb +12 -0
  103. data/web/views/_nav.erb +6 -20
  104. data/web/views/_paging.erb +3 -1
  105. data/web/views/_poll_link.erb +3 -6
  106. data/web/views/_summary.erb +7 -7
  107. data/web/views/busy.erb +87 -28
  108. data/web/views/dashboard.erb +51 -21
  109. data/web/views/dead.erb +4 -4
  110. data/web/views/filtering.erb +7 -0
  111. data/web/views/layout.erb +15 -5
  112. data/web/views/metrics.erb +91 -0
  113. data/web/views/metrics_for_job.erb +59 -0
  114. data/web/views/morgue.erb +25 -22
  115. data/web/views/queue.erb +35 -25
  116. data/web/views/queues.erb +23 -7
  117. data/web/views/retries.erb +28 -23
  118. data/web/views/retry.erb +5 -5
  119. data/web/views/scheduled.erb +19 -17
  120. data/web/views/scheduled_job_info.erb +1 -1
  121. metadata +86 -268
  122. data/.gitignore +0 -12
  123. data/.travis.yml +0 -16
  124. data/3.0-Upgrade.md +0 -70
  125. data/COMM-LICENSE +0 -95
  126. data/Contributing.md +0 -32
  127. data/Ent-Changes.md +0 -39
  128. data/Gemfile +0 -27
  129. data/LICENSE +0 -9
  130. data/Pro-2.0-Upgrade.md +0 -138
  131. data/Pro-Changes.md +0 -454
  132. data/Rakefile +0 -9
  133. data/bin/sidekiqctl +0 -93
  134. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  135. data/lib/generators/sidekiq/templates/worker_test.rb.erb +0 -8
  136. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  137. data/lib/sidekiq/actor.rb +0 -39
  138. data/lib/sidekiq/core_ext.rb +0 -105
  139. data/lib/sidekiq/exception_handler.rb +0 -30
  140. data/lib/sidekiq/extensions/action_mailer.rb +0 -56
  141. data/lib/sidekiq/extensions/active_record.rb +0 -39
  142. data/lib/sidekiq/extensions/class_methods.rb +0 -39
  143. data/lib/sidekiq/extensions/generic_proxy.rb +0 -24
  144. data/lib/sidekiq/logging.rb +0 -104
  145. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  146. data/lib/sidekiq/middleware/server/logging.rb +0 -40
  147. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -206
  148. data/lib/sidekiq/util.rb +0 -68
  149. data/lib/sidekiq/web_helpers.rb +0 -249
  150. data/lib/sidekiq/worker.rb +0 -103
  151. data/test/config.yml +0 -9
  152. data/test/env_based_config.yml +0 -11
  153. data/test/fake_env.rb +0 -0
  154. data/test/fixtures/en.yml +0 -2
  155. data/test/helper.rb +0 -49
  156. data/test/test_api.rb +0 -493
  157. data/test/test_cli.rb +0 -335
  158. data/test/test_client.rb +0 -194
  159. data/test/test_exception_handler.rb +0 -55
  160. data/test/test_extensions.rb +0 -126
  161. data/test/test_fetch.rb +0 -104
  162. data/test/test_logging.rb +0 -34
  163. data/test/test_manager.rb +0 -168
  164. data/test/test_middleware.rb +0 -159
  165. data/test/test_processor.rb +0 -237
  166. data/test/test_rails.rb +0 -21
  167. data/test/test_redis_connection.rb +0 -126
  168. data/test/test_retry.rb +0 -325
  169. data/test/test_scheduled.rb +0 -114
  170. data/test/test_scheduling.rb +0 -49
  171. data/test/test_sidekiq.rb +0 -99
  172. data/test/test_testing.rb +0 -142
  173. data/test/test_testing_fake.rb +0 -268
  174. data/test/test_testing_inline.rb +0 -93
  175. data/test/test_util.rb +0 -16
  176. data/test/test_web.rb +0 -608
  177. data/test/test_web_helpers.rb +0 -53
  178. data/web/assets/images/bootstrap/glyphicons-halflings-white.png +0 -0
  179. data/web/assets/images/bootstrap/glyphicons-halflings.png +0 -0
  180. data/web/assets/images/status/active.png +0 -0
  181. data/web/assets/images/status/idle.png +0 -0
  182. data/web/assets/javascripts/locales/README.md +0 -27
  183. data/web/assets/javascripts/locales/jquery.timeago.ar.js +0 -96
  184. data/web/assets/javascripts/locales/jquery.timeago.bg.js +0 -18
  185. data/web/assets/javascripts/locales/jquery.timeago.bs.js +0 -49
  186. data/web/assets/javascripts/locales/jquery.timeago.ca.js +0 -18
  187. data/web/assets/javascripts/locales/jquery.timeago.cs.js +0 -18
  188. data/web/assets/javascripts/locales/jquery.timeago.cy.js +0 -20
  189. data/web/assets/javascripts/locales/jquery.timeago.da.js +0 -18
  190. data/web/assets/javascripts/locales/jquery.timeago.de.js +0 -18
  191. data/web/assets/javascripts/locales/jquery.timeago.el.js +0 -18
  192. data/web/assets/javascripts/locales/jquery.timeago.en-short.js +0 -20
  193. data/web/assets/javascripts/locales/jquery.timeago.en.js +0 -20
  194. data/web/assets/javascripts/locales/jquery.timeago.es.js +0 -18
  195. data/web/assets/javascripts/locales/jquery.timeago.et.js +0 -18
  196. data/web/assets/javascripts/locales/jquery.timeago.fa.js +0 -22
  197. data/web/assets/javascripts/locales/jquery.timeago.fi.js +0 -28
  198. data/web/assets/javascripts/locales/jquery.timeago.fr-short.js +0 -16
  199. data/web/assets/javascripts/locales/jquery.timeago.fr.js +0 -17
  200. data/web/assets/javascripts/locales/jquery.timeago.he.js +0 -18
  201. data/web/assets/javascripts/locales/jquery.timeago.hr.js +0 -49
  202. data/web/assets/javascripts/locales/jquery.timeago.hu.js +0 -18
  203. data/web/assets/javascripts/locales/jquery.timeago.hy.js +0 -18
  204. data/web/assets/javascripts/locales/jquery.timeago.id.js +0 -18
  205. data/web/assets/javascripts/locales/jquery.timeago.it.js +0 -16
  206. data/web/assets/javascripts/locales/jquery.timeago.ja.js +0 -19
  207. data/web/assets/javascripts/locales/jquery.timeago.ko.js +0 -17
  208. data/web/assets/javascripts/locales/jquery.timeago.lt.js +0 -20
  209. data/web/assets/javascripts/locales/jquery.timeago.mk.js +0 -20
  210. data/web/assets/javascripts/locales/jquery.timeago.nl.js +0 -20
  211. data/web/assets/javascripts/locales/jquery.timeago.no.js +0 -18
  212. data/web/assets/javascripts/locales/jquery.timeago.pl.js +0 -31
  213. data/web/assets/javascripts/locales/jquery.timeago.pt-br.js +0 -16
  214. data/web/assets/javascripts/locales/jquery.timeago.pt.js +0 -16
  215. data/web/assets/javascripts/locales/jquery.timeago.ro.js +0 -18
  216. data/web/assets/javascripts/locales/jquery.timeago.rs.js +0 -49
  217. data/web/assets/javascripts/locales/jquery.timeago.ru.js +0 -34
  218. data/web/assets/javascripts/locales/jquery.timeago.sk.js +0 -18
  219. data/web/assets/javascripts/locales/jquery.timeago.sl.js +0 -44
  220. data/web/assets/javascripts/locales/jquery.timeago.sv.js +0 -18
  221. data/web/assets/javascripts/locales/jquery.timeago.th.js +0 -20
  222. data/web/assets/javascripts/locales/jquery.timeago.tr.js +0 -16
  223. data/web/assets/javascripts/locales/jquery.timeago.uk.js +0 -34
  224. data/web/assets/javascripts/locales/jquery.timeago.uz.js +0 -19
  225. data/web/assets/javascripts/locales/jquery.timeago.zh-cn.js +0 -20
  226. data/web/assets/javascripts/locales/jquery.timeago.zh-tw.js +0 -20
  227. data/web/views/_poll_js.erb +0 -5
  228. /data/web/assets/images/{status-sd8051fd480.png → status.png} +0 -0
@@ -0,0 +1,364 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "set"
5
+ require "yaml"
6
+ require "cgi"
7
+
8
+ module Sidekiq
9
+ # This is not a public API
10
+ module WebHelpers
11
+ def strings(lang)
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.read(file))
19
+ global.merge!(strs[lang])
20
+ end
21
+ end
22
+ end
23
+
24
+ def to_json(x)
25
+ Sidekiq.dump_json(x)
26
+ end
27
+
28
+ def singularize(str, count)
29
+ if count == 1 && str.respond_to?(:singularize) # rails
30
+ str.singularize
31
+ else
32
+ str
33
+ end
34
+ end
35
+
36
+ def clear_caches
37
+ @strings = nil
38
+ @locale_files = nil
39
+ @available_locales = nil
40
+ end
41
+
42
+ def locale_files
43
+ @locale_files ||= settings.locales.flat_map { |path|
44
+ Dir["#{path}/*.yml"]
45
+ }
46
+ end
47
+
48
+ def available_locales
49
+ @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
50
+ end
51
+
52
+ def find_locale_files(lang)
53
+ locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
54
+ end
55
+
56
+ def search(jobset, substr)
57
+ resultset = jobset.scan(substr).to_a
58
+ @current_page = 1
59
+ @count = @total_size = resultset.size
60
+ resultset
61
+ end
62
+
63
+ def filtering(which)
64
+ erb(:filtering, locals: {which: which})
65
+ end
66
+
67
+ def filter_link(jid, within = "retries")
68
+ if within.nil?
69
+ ::Rack::Utils.escape_html(jid)
70
+ else
71
+ "<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
72
+ end
73
+ end
74
+
75
+ def display_tags(job, within = "retries")
76
+ job.tags.map { |tag|
77
+ "<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
78
+ }.join(" ")
79
+ end
80
+
81
+ # This view helper provide ability display you html code in
82
+ # to head of page. Example:
83
+ #
84
+ # <% add_to_head do %>
85
+ # <link rel="stylesheet" .../>
86
+ # <meta .../>
87
+ # <% end %>
88
+ #
89
+ def add_to_head
90
+ @head_html ||= []
91
+ @head_html << yield.dup if block_given?
92
+ end
93
+
94
+ def display_custom_head
95
+ @head_html.join if defined?(@head_html)
96
+ end
97
+
98
+ def text_direction
99
+ get_locale["TextDirection"] || "ltr"
100
+ end
101
+
102
+ def rtl?
103
+ text_direction == "rtl"
104
+ end
105
+
106
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
107
+ def user_preferred_languages
108
+ languages = env["HTTP_ACCEPT_LANGUAGE"]
109
+ languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
110
+ locale, quality = language.split(";q=", 2)
111
+ locale = nil if locale == "*" # Ignore wildcards
112
+ quality = quality ? quality.to_f : 1.0
113
+ [locale, quality]
114
+ }.sort { |(_, left), (_, right)|
115
+ right <=> left
116
+ }.map(&:first).compact
117
+ end
118
+
119
+ # 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"
120
+ # this method will try to best match the available locales to the user's preferred languages.
121
+ #
122
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
123
+ def locale
124
+ @locale ||= begin
125
+ matched_locale = user_preferred_languages.map { |preferred|
126
+ preferred_language = preferred.split("-", 2).first
127
+
128
+ lang_group = available_locales.select { |available|
129
+ preferred_language == available.split("-", 2).first
130
+ }
131
+
132
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
133
+ }.compact.first
134
+
135
+ matched_locale || "en"
136
+ end
137
+ end
138
+
139
+ # sidekiq/sidekiq#3243
140
+ def unfiltered?
141
+ yield unless env["PATH_INFO"].start_with?("/filter/")
142
+ end
143
+
144
+ def get_locale
145
+ strings(locale)
146
+ end
147
+
148
+ def t(msg, options = {})
149
+ string = get_locale[msg] || strings("en")[msg] || msg
150
+ if options.empty?
151
+ string
152
+ else
153
+ string % options
154
+ end
155
+ end
156
+
157
+ def sort_direction_label
158
+ (params[:direction] == "asc") ? "&uarr;" : "&darr;"
159
+ end
160
+
161
+ def workset
162
+ @work ||= Sidekiq::WorkSet.new
163
+ end
164
+
165
+ def processes
166
+ @processes ||= Sidekiq::ProcessSet.new
167
+ end
168
+
169
+ # Sorts processes by hostname following the natural sort order
170
+ def sorted_processes
171
+ @sorted_processes ||= begin
172
+ return processes unless processes.all? { |p| p["hostname"] }
173
+
174
+ processes.to_a.sort_by do |process|
175
+ # Kudos to `shurikk` on StackOverflow
176
+ # https://stackoverflow.com/a/15170063/575547
177
+ process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
178
+ end
179
+ end
180
+ end
181
+
182
+ def busy_weights(capsule_weights)
183
+ # backwards compat with 7.0.0, remove in 7.1
184
+ cw = [capsule_weights].flatten
185
+ cw.map { |hash|
186
+ hash.map { |name, weight| (weight > 0) ? +name << ": " << weight.to_s : name }.join(", ")
187
+ }.join("; ")
188
+ end
189
+
190
+ def stats
191
+ @stats ||= Sidekiq::Stats.new
192
+ end
193
+
194
+ def redis_url
195
+ Sidekiq.redis do |conn|
196
+ conn.config.server_url
197
+ end
198
+ end
199
+
200
+ def redis_info
201
+ Sidekiq.default_configuration.redis_info
202
+ end
203
+
204
+ def root_path
205
+ "#{env["SCRIPT_NAME"]}/"
206
+ end
207
+
208
+ def current_path
209
+ @current_path ||= request.path_info.gsub(/^\//, "")
210
+ end
211
+
212
+ def current_status
213
+ (workset.size == 0) ? "idle" : "active"
214
+ end
215
+
216
+ def relative_time(time)
217
+ stamp = time.getutc.iso8601
218
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
219
+ end
220
+
221
+ def job_params(job, score)
222
+ "#{score}-#{job["jid"]}"
223
+ end
224
+
225
+ def parse_params(params)
226
+ score, jid = params.split("-", 2)
227
+ [score.to_f, jid]
228
+ end
229
+
230
+ SAFE_QPARAMS = %w[page direction]
231
+
232
+ # Merge options with current params, filter safe params, and stringify to query string
233
+ def qparams(options)
234
+ stringified_options = options.transform_keys(&:to_s)
235
+
236
+ to_query_string(params.merge(stringified_options))
237
+ end
238
+
239
+ def to_query_string(params)
240
+ params.map { |key, value|
241
+ SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
242
+ }.compact.join("&")
243
+ end
244
+
245
+ def truncate(text, truncate_after_chars = 2000)
246
+ (truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
247
+ end
248
+
249
+ def display_args(args, truncate_after_chars = 2000)
250
+ return "Invalid job payload, args is nil" if args.nil?
251
+ return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
252
+
253
+ begin
254
+ args.map { |arg|
255
+ h(truncate(to_display(arg), truncate_after_chars))
256
+ }.join(", ")
257
+ rescue
258
+ "Illegal job arguments: #{h args.inspect}"
259
+ end
260
+ end
261
+
262
+ def csrf_tag
263
+ "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
264
+ end
265
+
266
+ def to_display(arg)
267
+ arg.inspect
268
+ rescue
269
+ begin
270
+ arg.to_s
271
+ rescue => ex
272
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
273
+ end
274
+ end
275
+
276
+ RETRY_JOB_KEYS = Set.new(%w[
277
+ queue class args retry_count retried_at failed_at
278
+ jid error_message error_class backtrace
279
+ error_backtrace enqueued_at retry wrapped
280
+ created_at tags display_class
281
+ ])
282
+
283
+ def retry_extra_items(retry_job)
284
+ @retry_extra_items ||= {}.tap do |extra|
285
+ retry_job.item.each do |key, value|
286
+ extra[key] = value unless RETRY_JOB_KEYS.include?(key)
287
+ end
288
+ end
289
+ end
290
+
291
+ def format_memory(rss_kb)
292
+ return "0" if rss_kb.nil? || rss_kb == 0
293
+
294
+ if rss_kb < 100_000
295
+ "#{number_with_delimiter(rss_kb)} KB"
296
+ elsif rss_kb < 10_000_000
297
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
298
+ else
299
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
300
+ end
301
+ end
302
+
303
+ def number_with_delimiter(number, options = {})
304
+ precision = options[:precision] || 0
305
+ %(<span data-nwp="#{precision}">#{number.round(precision)}</span>)
306
+ end
307
+
308
+ def h(text)
309
+ ::Rack::Utils.escape_html(text)
310
+ rescue ArgumentError => e
311
+ raise unless e.message.eql?("invalid byte sequence in UTF-8")
312
+ text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
313
+ retry
314
+ end
315
+
316
+ # Any paginated list that performs an action needs to redirect
317
+ # back to the proper page after performing that action.
318
+ def redirect_with_query(url)
319
+ r = request.referer
320
+ if r && r =~ /\?/
321
+ ref = URI(r)
322
+ redirect("#{url}?#{ref.query}")
323
+ else
324
+ redirect url
325
+ end
326
+ end
327
+
328
+ def environment_title_prefix
329
+ environment = Sidekiq.default_configuration[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
330
+
331
+ "[#{environment.upcase}] " unless environment == "production"
332
+ end
333
+
334
+ def product_version
335
+ "Sidekiq v#{Sidekiq::VERSION}"
336
+ end
337
+
338
+ def server_utc_time
339
+ Time.now.utc.strftime("%H:%M:%S UTC")
340
+ end
341
+
342
+ def pollable?
343
+ !(current_path == "" || current_path.start_with?("metrics"))
344
+ end
345
+
346
+ def retry_or_delete_or_kill(job, params)
347
+ if params["retry"]
348
+ job.retry
349
+ elsif params["delete"]
350
+ job.delete
351
+ elsif params["kill"]
352
+ job.kill
353
+ end
354
+ end
355
+
356
+ def delete_or_add_queue(job, params)
357
+ if params["delete"]
358
+ job.delete
359
+ elsif params["add_to_queue"]
360
+ job.add_to_queue
361
+ end
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+
5
+ module Sidekiq
6
+ module WebRouter
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
21
+
22
+ def get(path, &block)
23
+ route(GET, path, &block)
24
+ end
25
+
26
+ def post(path, &block)
27
+ route(POST, path, &block)
28
+ end
29
+
30
+ def put(path, &block)
31
+ route(PUT, path, &block)
32
+ end
33
+
34
+ def patch(path, &block)
35
+ route(PATCH, path, &block)
36
+ end
37
+
38
+ def delete(path, &block)
39
+ route(DELETE, path, &block)
40
+ end
41
+
42
+ def route(method, path, &block)
43
+ @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
44
+
45
+ @routes[method] << WebRoute.new(method, path, block)
46
+ end
47
+
48
+ def match(env)
49
+ request_method = env[REQUEST_METHOD]
50
+ path_info = ::Rack::Utils.unescape env[PATH_INFO]
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
+
56
+ @routes[request_method].each do |route|
57
+ params = route.match(request_method, path_info)
58
+ if params
59
+ env[ROUTE_PARAMS] = params
60
+
61
+ return WebAction.new(env, route.block)
62
+ end
63
+ end
64
+
65
+ nil
66
+ end
67
+ end
68
+
69
+ class WebRoute
70
+ attr_accessor :request_method, :pattern, :block, :name
71
+
72
+ NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
73
+
74
+ def initialize(request_method, pattern, block)
75
+ @request_method = request_method
76
+ @pattern = pattern
77
+ @block = block
78
+ end
79
+
80
+ def matcher
81
+ @matcher ||= compile
82
+ end
83
+
84
+ def compile
85
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
86
+ p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
87
+
88
+ Regexp.new("\\A#{p}\\Z")
89
+ else
90
+ pattern
91
+ end
92
+ end
93
+
94
+ def match(request_method, path)
95
+ case matcher
96
+ when String
97
+ {} if path == matcher
98
+ else
99
+ path_match = path.match(matcher)
100
+ path_match&.named_captures&.transform_keys(&:to_sym)
101
+ end
102
+ end
103
+ end
104
+ end