sidekiq 7.3.9 → 8.0.0.beta1

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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +21 -4
  3. data/README.md +16 -13
  4. data/bin/sidekiqload +10 -10
  5. data/bin/webload +69 -0
  6. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +5 -5
  7. data/lib/sidekiq/api.rb +108 -36
  8. data/lib/sidekiq/capsule.rb +1 -1
  9. data/lib/sidekiq/cli.rb +15 -19
  10. data/lib/sidekiq/client.rb +13 -16
  11. data/lib/sidekiq/component.rb +30 -2
  12. data/lib/sidekiq/config.rb +18 -15
  13. data/lib/sidekiq/embedded.rb +1 -0
  14. data/lib/sidekiq/job/iterable.rb +2 -4
  15. data/lib/sidekiq/job_retry.rb +2 -2
  16. data/lib/sidekiq/job_util.rb +5 -1
  17. data/lib/sidekiq/launcher.rb +1 -1
  18. data/lib/sidekiq/logger.rb +6 -10
  19. data/lib/sidekiq/manager.rb +0 -1
  20. data/lib/sidekiq/metrics/query.rb +1 -3
  21. data/lib/sidekiq/middleware/current_attributes.rb +5 -17
  22. data/lib/sidekiq/paginator.rb +8 -1
  23. data/lib/sidekiq/processor.rb +21 -14
  24. data/lib/sidekiq/profiler.rb +59 -0
  25. data/lib/sidekiq/redis_client_adapter.rb +0 -1
  26. data/lib/sidekiq/testing.rb +2 -2
  27. data/lib/sidekiq/version.rb +2 -2
  28. data/lib/sidekiq/web/action.rb +101 -85
  29. data/lib/sidekiq/web/application.rb +339 -333
  30. data/lib/sidekiq/web/config.rb +116 -0
  31. data/lib/sidekiq/web/helpers.rb +40 -15
  32. data/lib/sidekiq/web/router.rb +60 -76
  33. data/lib/sidekiq/web.rb +51 -156
  34. data/sidekiq.gemspec +5 -4
  35. data/web/assets/javascripts/application.js +6 -13
  36. data/web/assets/javascripts/base-charts.js +30 -18
  37. data/web/assets/javascripts/metrics.js +1 -1
  38. data/web/assets/stylesheets/style.css +750 -0
  39. data/web/locales/ar.yml +1 -0
  40. data/web/locales/cs.yml +1 -0
  41. data/web/locales/da.yml +1 -0
  42. data/web/locales/de.yml +1 -0
  43. data/web/locales/el.yml +1 -0
  44. data/web/locales/en.yml +6 -0
  45. data/web/locales/es.yml +24 -2
  46. data/web/locales/fa.yml +1 -0
  47. data/web/locales/fr.yml +1 -0
  48. data/web/locales/gd.yml +1 -0
  49. data/web/locales/he.yml +1 -0
  50. data/web/locales/hi.yml +1 -0
  51. data/web/locales/it.yml +1 -0
  52. data/web/locales/ja.yml +1 -0
  53. data/web/locales/ko.yml +1 -0
  54. data/web/locales/lt.yml +1 -0
  55. data/web/locales/nb.yml +1 -0
  56. data/web/locales/nl.yml +1 -0
  57. data/web/locales/pl.yml +1 -0
  58. data/web/locales/{pt-br.yml → pt-BR.yml} +2 -1
  59. data/web/locales/pt.yml +1 -0
  60. data/web/locales/ru.yml +1 -0
  61. data/web/locales/sv.yml +1 -0
  62. data/web/locales/ta.yml +1 -0
  63. data/web/locales/tr.yml +1 -0
  64. data/web/locales/uk.yml +1 -0
  65. data/web/locales/ur.yml +1 -0
  66. data/web/locales/vi.yml +1 -0
  67. data/web/locales/{zh-cn.yml → zh-CN.yml} +85 -73
  68. data/web/locales/{zh-tw.yml → zh-TW.yml} +2 -1
  69. data/web/views/_footer.erb +31 -33
  70. data/web/views/_job_info.erb +91 -89
  71. data/web/views/_metrics_period_select.erb +1 -1
  72. data/web/views/_nav.erb +14 -21
  73. data/web/views/_paging.erb +23 -21
  74. data/web/views/_poll_link.erb +2 -2
  75. data/web/views/_summary.erb +16 -16
  76. data/web/views/busy.erb +124 -122
  77. data/web/views/dashboard.erb +61 -66
  78. data/web/views/dead.erb +31 -27
  79. data/web/views/filtering.erb +3 -3
  80. data/web/views/layout.erb +5 -21
  81. data/web/views/metrics.erb +83 -80
  82. data/web/views/metrics_for_job.erb +39 -42
  83. data/web/views/morgue.erb +61 -70
  84. data/web/views/profiles.erb +43 -0
  85. data/web/views/queue.erb +54 -52
  86. data/web/views/queues.erb +43 -41
  87. data/web/views/retries.erb +66 -75
  88. data/web/views/retry.erb +32 -27
  89. data/web/views/scheduled.erb +58 -54
  90. data/web/views/scheduled_job_info.erb +1 -1
  91. metadata +31 -18
  92. data/web/assets/stylesheets/application-dark.css +0 -147
  93. data/web/assets/stylesheets/application-rtl.css +0 -163
  94. data/web/assets/stylesheets/application.css +0 -759
  95. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  96. data/web/assets/stylesheets/bootstrap.css +0 -5
  97. data/web/views/_status.erb +0 -4
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq/web/csrf_protection"
4
+
5
+ module Sidekiq
6
+ class Web
7
+ ##
8
+ # Configure the Sidekiq::Web instance in this process:
9
+ #
10
+ # require "sidekiq/web"
11
+ # Sidekiq::Web.configure do |config|
12
+ # config.register(MyExtension, name: "myext", tab: "TabName", index: "tabpage/")
13
+ # end
14
+ #
15
+ # This should go in your `config/routes.rb` or similar. It
16
+ # does not belong in your initializer since Web should not be
17
+ # loaded in some processes (like an actual Sidekiq process)
18
+ class Config
19
+ extend Forwardable
20
+
21
+ OPTIONS = {
22
+ # By default we support direct uploads to p.f.c since the UI is a JS SPA
23
+ # and very difficult for us to vendor or provide ourselves. If you are worried
24
+ # about data security and wish to self-host, you can change these URLs.
25
+ profile_view_url: "https://profiler.firefox.com/public/%s",
26
+ profile_store_url: "https://api.profiler.firefox.com/compressed-store"
27
+ }
28
+
29
+ ##
30
+ # Allows users to add custom rows to all of the Job
31
+ # tables, e.g. Retries, Dead, Scheduled, with custom
32
+ # links to other systems, see _job_info.erb and test
33
+ # in web_test.rb
34
+ #
35
+ # Sidekiq::Web.configure do |cfg|
36
+ # cfg.custom_job_info_rows << JobLogLink.new
37
+ # end
38
+ #
39
+ # class JobLogLink
40
+ # def add_pair(job)
41
+ # yield "External Logs", "<a href='https://example.com/logs/#{job.jid}'>Logs for #{job.jid}</a>"
42
+ # end
43
+ # end
44
+ attr_accessor :custom_job_info_rows
45
+
46
+ attr_reader :tabs
47
+ attr_reader :locales
48
+ attr_reader :views
49
+ attr_reader :middlewares
50
+
51
+ # Adds the "Back to App" link in the header
52
+ attr_accessor :app_url
53
+
54
+ def initialize
55
+ @options = OPTIONS.dup
56
+ @locales = LOCALES
57
+ @views = VIEWS
58
+ @tabs = DEFAULT_TABS.dup
59
+ @middlewares = [Sidekiq::Web::CsrfProtection]
60
+ @custom_job_info_rows = []
61
+ end
62
+
63
+ def_delegators :@options, :[], :[]=, :fetch, :key?, :has_key?, :merge!, :dig
64
+
65
+ def use(*args, &block)
66
+ middlewares << [args, block]
67
+ end
68
+
69
+ # Register a class as a Sidekiq Web UI extension. The class should
70
+ # provide one or more tabs which map to an index route. Options:
71
+ #
72
+ # @param extclass [Class] Class which contains the HTTP actions, required
73
+ # @param name [String] the name of the extension, used to namespace assets
74
+ # @param tab [String | Array] labels(s) of the UI tabs
75
+ # @param index [String | Array] index route(s) for each tab
76
+ # @param root_dir [String] directory location to find assets, locales and views, typically `web/` within the gemfile
77
+ # @param asset_paths [Array] one or more directories under {root}/assets/{name} to be publicly served, e.g. ["js", "css", "img"]
78
+ # @param cache_for [Integer] amount of time to cache assets, default one day
79
+ #
80
+ # Web extensions will have a root `web/` directory with `locales/`, `assets/`
81
+ # and `views/` subdirectories.
82
+ def register_extension(extclass, name:, tab:, index:, root_dir: nil, cache_for: 86400, asset_paths: nil)
83
+ tab = Array(tab)
84
+ index = Array(index)
85
+ tab.zip(index).each do |tab, index|
86
+ tabs[tab] = index
87
+ end
88
+ if root_dir
89
+ locdir = File.join(root_dir, "locales")
90
+ locales << locdir if File.directory?(locdir)
91
+
92
+ if asset_paths && name
93
+ # if you have {root}/assets/{name}/js/scripts.js
94
+ # and {root}/assets/{name}/css/styles.css
95
+ # you would pass in:
96
+ # asset_paths: ["js", "css"]
97
+ # See script_tag and style_tag in web/helpers.rb
98
+ assdir = File.join(root_dir, "assets")
99
+ assurls = Array(asset_paths).map { |x| "/#{name}/#{x}" }
100
+ assetprops = {
101
+ urls: assurls,
102
+ root: assdir,
103
+ cascade: true
104
+ }
105
+ assetprops[:header_rules] = [[:all, {"cache-control" => "private, max-age=#{cache_for.to_i}"}]] if cache_for
106
+ middlewares << [[Rack::Static, assetprops], nil]
107
+ end
108
+ end
109
+
110
+ yield self if block_given?
111
+ extclass.registered(Web::Application)
112
+ end
113
+ alias_method :register, :register_extension
114
+ end
115
+ end
116
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "uri"
4
- require "set"
5
4
  require "yaml"
6
5
  require "cgi"
7
6
 
@@ -9,6 +8,20 @@ module Sidekiq
9
8
  # These methods are available to pages within the Web UI and UI extensions.
10
9
  # They are not public APIs for applications to use.
11
10
  module WebHelpers
11
+ def store_name
12
+ hash = redis_info
13
+ return "Dragonfly" if hash.has_key?("dragonfly_version")
14
+ return "Valkey" if hash.has_key?("valkey_version")
15
+ "Redis"
16
+ end
17
+
18
+ def store_version
19
+ hash = redis_info
20
+ return hash["dragonfly_version"] if hash.has_key?("dragonfly_version")
21
+ return hash["valkey_version"] if hash.has_key?("valkey_version")
22
+ hash["redis_version"]
23
+ end
24
+
12
25
  def style_tag(location, **kwargs)
13
26
  global = location.match?(/:\/\//)
14
27
  location = root_path + location if !global && !location.start_with?(root_path)
@@ -19,7 +32,9 @@ module Sidekiq
19
32
  nonce: csp_nonce,
20
33
  href: location
21
34
  }
22
- html_tag(:link, attrs.merge(kwargs))
35
+ add_to_head do
36
+ html_tag(:link, attrs.merge(kwargs))
37
+ end
23
38
  end
24
39
 
25
40
  def script_tag(location, **kwargs)
@@ -36,7 +51,7 @@ module Sidekiq
36
51
  # NB: keys and values are not escaped; do not allow user input
37
52
  # in the attributes
38
53
  private def html_tag(tagname, attrs)
39
- s = +"<#{tagname}"
54
+ s = "<#{tagname}"
40
55
  attrs.each_pair do |k, v|
41
56
  next unless v
42
57
  s << " #{k}=\"#{v}\""
@@ -56,9 +71,9 @@ module Sidekiq
56
71
 
57
72
  # Allow sidekiq-web extensions to add locale paths
58
73
  # so extensions can be localized
59
- @@strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
74
+ @@strings[lang] ||= config.locales.each_with_object({}) do |path, global|
60
75
  find_locale_files(lang).each do |file|
61
- strs = YAML.safe_load(File.read(file))
76
+ strs = YAML.safe_load_file(file)
62
77
  global.merge!(strs[lang])
63
78
  end
64
79
  end
@@ -83,7 +98,7 @@ module Sidekiq
83
98
  end
84
99
 
85
100
  def locale_files
86
- @@locale_files ||= settings.locales.flat_map { |path|
101
+ @@locale_files ||= config.locales.flat_map { |path|
87
102
  Dir["#{path}/*.yml"]
88
103
  }
89
104
  end
@@ -96,6 +111,10 @@ module Sidekiq
96
111
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
97
112
  end
98
113
 
114
+ def language_name(locale)
115
+ strings(locale).fetch("LanguageName", locale)
116
+ end
117
+
99
118
  def search(jobset, substr)
100
119
  resultset = jobset.scan(substr).to_a
101
120
  @current_page = 1
@@ -149,7 +168,7 @@ module Sidekiq
149
168
  # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
150
169
  def user_preferred_languages
151
170
  languages = env["HTTP_ACCEPT_LANGUAGE"]
152
- languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
171
+ languages.to_s.gsub(/\s+/, "").split(",").map { |language|
153
172
  locale, quality = language.split(";q=", 2)
154
173
  locale = nil if locale == "*" # Ignore wildcards
155
174
  quality = quality ? quality.to_f : 1.0
@@ -168,7 +187,13 @@ module Sidekiq
168
187
  @locale ||= if (l = session&.fetch(:locale, nil)) && available_locales.include?(l)
169
188
  l
170
189
  else
171
- matched_locale = user_preferred_languages.map { |preferred|
190
+
191
+ # exactly match with preferred like "pt-BR, zh-CN, zh-TW..." first
192
+ matched_locale = user_preferred_languages.find { |preferred|
193
+ available_locales.include?(preferred) if preferred.length == 5
194
+ }
195
+
196
+ matched_locale ||= user_preferred_languages.map { |preferred|
172
197
  preferred_language = preferred.split("-", 2).first
173
198
 
174
199
  lang_group = available_locales.select { |available|
@@ -202,7 +227,7 @@ module Sidekiq
202
227
  end
203
228
 
204
229
  def sort_direction_label
205
- (params[:direction] == "asc") ? "&uarr;" : "&darr;"
230
+ (url_params("direction") == "asc") ? "&uarr;" : "&darr;"
206
231
  end
207
232
 
208
233
  def workset
@@ -245,7 +270,7 @@ module Sidekiq
245
270
  end
246
271
 
247
272
  def redis_info
248
- Sidekiq.default_configuration.redis_info
273
+ @info ||= Sidekiq.default_configuration.redis_info
249
274
  end
250
275
 
251
276
  def root_path
@@ -269,8 +294,8 @@ module Sidekiq
269
294
  "#{score}-#{job["jid"]}"
270
295
  end
271
296
 
272
- def parse_params(params)
273
- score, jid = params.split("-", 2)
297
+ def parse_key(key)
298
+ score, jid = key.split("-", 2)
274
299
  [score.to_f, jid]
275
300
  end
276
301
 
@@ -280,11 +305,11 @@ module Sidekiq
280
305
  def qparams(options)
281
306
  stringified_options = options.transform_keys(&:to_s)
282
307
 
283
- to_query_string(params.merge(stringified_options))
308
+ to_query_string(request.params.merge(stringified_options))
284
309
  end
285
310
 
286
- def to_query_string(params)
287
- params.map { |key, value|
311
+ def to_query_string(hash)
312
+ hash.map { |key, value|
288
313
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
289
314
  }.compact.join("&")
290
315
  end
@@ -3,104 +3,88 @@
3
3
  require "rack"
4
4
 
5
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
6
+ class Web
7
+ # Provides an API to declare endpoints, along with a match
8
+ # API to dynamically route a request to an endpoint.
9
+ module Router
10
+ def head(path, &) = route(:head, path, &)
21
11
 
22
- def get(path, &block)
23
- route(GET, path, &block)
24
- end
12
+ def get(path, &) = route(:get, path, &)
25
13
 
26
- def post(path, &block)
27
- route(POST, path, &block)
28
- end
14
+ def post(path, &) = route(:post, path, &)
29
15
 
30
- def put(path, &block)
31
- route(PUT, path, &block)
32
- end
16
+ def put(path, &) = route(:put, path, &)
33
17
 
34
- def patch(path, &block)
35
- route(PATCH, path, &block)
36
- end
18
+ def patch(path, &) = route(:patch, path, &)
37
19
 
38
- def delete(path, &block)
39
- route(DELETE, path, &block)
40
- end
20
+ def delete(path, &) = route(:delete, path, &)
41
21
 
42
- def route(*methods, path, &block)
43
- @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
44
-
45
- methods.each do |method|
46
- method = method.to_s.upcase
47
- @routes[method] << WebRoute.new(method, path, block)
22
+ def route(*methods, path, &block)
23
+ methods.each do |method|
24
+ raise ArgumentError, "Invalid method #{method}. Must be one of #{@routes.keys.join(",")}" unless route_cache.has_key?(method)
25
+ route_cache[method] << Route.new(method, path, block)
26
+ end
48
27
  end
49
- end
50
28
 
51
- def match(env)
52
- request_method = env[REQUEST_METHOD]
53
- path_info = ::Rack::Utils.unescape env[PATH_INFO]
29
+ def match(env)
30
+ request_method = env["REQUEST_METHOD"].downcase.to_sym
31
+ path_info = ::Rack::Utils.unescape_path env["PATH_INFO"]
54
32
 
55
- # There are servers which send an empty string when requesting the root.
56
- # These servers should be ashamed of themselves.
57
- path_info = "/" if path_info == ""
33
+ # There are servers which send an empty string when requesting the root.
34
+ # These servers should be ashamed of themselves.
35
+ path_info = "/" if path_info == ""
58
36
 
59
- @routes[request_method].each do |route|
60
- params = route.match(request_method, path_info)
61
- if params
62
- env[ROUTE_PARAMS] = params
63
-
64
- return WebAction.new(env, route.block)
37
+ route_cache[request_method].each do |route|
38
+ params = route.match(request_method, path_info)
39
+ if params
40
+ env["rack.route_params"] = params
41
+ return Action.new(env, route.block)
42
+ end
65
43
  end
44
+
45
+ nil
66
46
  end
67
47
 
68
- nil
48
+ def route_cache
49
+ @@routes ||= {get: [], post: [], put: [], patch: [], delete: [], head: []}
50
+ end
69
51
  end
70
- end
71
52
 
72
- class WebRoute
73
- attr_accessor :request_method, :pattern, :block, :name
53
+ class Route
54
+ attr_accessor :request_method, :pattern, :block, :name
74
55
 
75
- NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
56
+ NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
76
57
 
77
- def initialize(request_method, pattern, block)
78
- @request_method = request_method
79
- @pattern = pattern
80
- @block = block
81
- end
58
+ def initialize(request_method, pattern, block)
59
+ @request_method = request_method
60
+ @pattern = pattern
61
+ @block = block
62
+ end
82
63
 
83
- def matcher
84
- @matcher ||= compile
85
- end
64
+ def matcher
65
+ @matcher ||= compile
66
+ end
86
67
 
87
- def compile
88
- if pattern.match?(NAMED_SEGMENTS_PATTERN)
89
- p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
68
+ def compile
69
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
70
+ p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
90
71
 
91
- Regexp.new("\\A#{p}\\Z")
92
- else
93
- pattern
72
+ Regexp.new("\\A#{p}\\Z")
73
+ else
74
+ pattern
75
+ end
94
76
  end
95
- end
96
77
 
97
- def match(request_method, path)
98
- case matcher
99
- when String
100
- {} if path == matcher
101
- else
102
- path_match = path.match(matcher)
103
- path_match&.named_captures&.transform_keys(&:to_sym)
78
+ EMPTY = {}.freeze
79
+
80
+ def match(request_method, path)
81
+ case matcher
82
+ when String
83
+ EMPTY if path == matcher
84
+ else
85
+ path_match = path.match(matcher)
86
+ path_match&.named_captures&.transform_keys(&:to_sym)
87
+ end
104
88
  end
105
89
  end
106
90
  end
data/lib/sidekiq/web.rb CHANGED
@@ -2,20 +2,11 @@
2
2
 
3
3
  require "erb"
4
4
  require "securerandom"
5
-
6
- require "sidekiq"
7
- require "sidekiq/api"
8
- require "sidekiq/paginator"
9
- require "sidekiq/web/helpers"
10
-
11
- require "sidekiq/web/router"
12
- require "sidekiq/web/action"
13
- require "sidekiq/web/application"
14
- require "sidekiq/web/csrf_protection"
15
-
16
- require "rack/content_length"
17
5
  require "rack/builder"
18
6
  require "rack/static"
7
+ require "sidekiq"
8
+ require "sidekiq/api"
9
+ require "sidekiq/web/config"
19
10
 
20
11
  module Sidekiq
21
12
  class Web
@@ -32,176 +23,86 @@ module Sidekiq
32
23
  "Retries" => "retries",
33
24
  "Scheduled" => "scheduled",
34
25
  "Dead" => "morgue",
35
- "Metrics" => "metrics"
26
+ "Metrics" => "metrics",
27
+ "Profiles" => "profiles"
36
28
  }
37
29
 
38
- if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
39
- CONTENT_LANGUAGE = "Content-Language"
40
- CONTENT_SECURITY_POLICY = "Content-Security-Policy"
41
- LOCATION = "Location"
42
- X_CASCADE = "X-Cascade"
43
- X_CONTENT_TYPE_OPTIONS = "X-Content-Type-Options"
44
- else
45
- CONTENT_LANGUAGE = "content-language"
46
- CONTENT_SECURITY_POLICY = "content-security-policy"
47
- LOCATION = "location"
48
- X_CASCADE = "x-cascade"
49
- X_CONTENT_TYPE_OPTIONS = "x-content-type-options"
50
- end
30
+ @@config = Sidekiq::Web::Config.new
51
31
 
52
32
  class << self
53
- # Forward compatibility with 8.0
54
33
  def configure
55
- yield self
56
- end
57
-
58
- def settings
59
- self
34
+ if block_given?
35
+ yield @@config
36
+ else
37
+ @@config
38
+ end
60
39
  end
61
40
 
62
- def default_tabs
63
- DEFAULT_TABS
41
+ def app_url=(url)
42
+ @@config.app_url = url
64
43
  end
65
44
 
66
- def custom_tabs
67
- @custom_tabs ||= {}
68
- end
69
- alias_method :tabs, :custom_tabs
45
+ def tabs = @@config.tabs
70
46
 
71
- def custom_job_info_rows
72
- @custom_job_info_rows ||= []
73
- end
47
+ def locales = @@config.locales
74
48
 
75
- def locales
76
- @locales ||= LOCALES
77
- end
49
+ def views = @@config.views
78
50
 
79
- def views
80
- @views ||= VIEWS
81
- end
51
+ def custom_job_info_rows = @@config.custom_job_info_rows
82
52
 
83
- def enable(*opts)
84
- opts.each { |key| set(key, true) }
53
+ def redis_pool
54
+ @pool || Sidekiq.default_configuration.redis_pool
85
55
  end
86
56
 
87
- def disable(*opts)
88
- opts.each { |key| set(key, false) }
57
+ def redis_pool=(pool)
58
+ @pool = pool
89
59
  end
90
60
 
91
- def middlewares
92
- @middlewares ||= []
93
- end
61
+ def middlewares = @@config.middlewares
94
62
 
95
- def use(*args, &block)
96
- middlewares << [args, block]
97
- end
63
+ def use(*args, &block) = @@config.middlewares << [args, block]
98
64
 
99
- def set(attribute, value)
100
- send(:"#{attribute}=", value)
65
+ def register(*args, **kw, &block)
66
+ # TODO
67
+ puts "`Sidekiq::Web.register` is deprecated, use `Sidekiq::Web.configure {|cfg| cfg.register(...) }`"
68
+ @@config.register(*args, **kw, &block)
101
69
  end
102
-
103
- attr_accessor :app_url, :redis_pool
104
- attr_writer :locales, :views
105
- end
106
-
107
- def self.inherited(child)
108
- child.app_url = app_url
109
- child.redis_pool = redis_pool
110
- end
111
-
112
- def settings
113
- self.class.settings
114
70
  end
115
71
 
116
- def middlewares
117
- @middlewares ||= self.class.middlewares
72
+ # Allow user to say
73
+ # run Sidekiq::Web
74
+ # rather than:
75
+ # run Sidekiq::Web.new
76
+ def self.call(env)
77
+ @inst ||= new
78
+ @inst.call(env)
118
79
  end
119
80
 
120
- def use(*args, &block)
121
- middlewares << [args, block]
81
+ # testing, internal use only
82
+ def self.reset!
83
+ @@config.reset!
84
+ @inst = nil
122
85
  end
123
86
 
124
87
  def call(env)
125
- env[:csp_nonce] = SecureRandom.base64(16)
88
+ env[:web_config] = Sidekiq::Web.configure
89
+ env[:csp_nonce] = SecureRandom.hex(8)
90
+ env[:redis_pool] = self.class.redis_pool
126
91
  app.call(env)
127
92
  end
128
93
 
129
- def self.call(env)
130
- @app ||= new
131
- @app.call(env)
132
- end
133
-
134
94
  def app
135
- @app ||= build
136
- end
137
-
138
- def enable(*opts)
139
- opts.each { |key| set(key, true) }
140
- end
141
-
142
- def disable(*opts)
143
- opts.each { |key| set(key, false) }
144
- end
145
-
146
- def set(attribute, value)
147
- send(:"#{attribute}=", value)
148
- end
149
-
150
- # Register a class as a Sidekiq Web UI extension. The class should
151
- # provide one or more tabs which map to an index route. Options:
152
- #
153
- # @param extension [Class] Class which contains the HTTP actions, required
154
- # @param name [String] the name of the extension, used to namespace assets
155
- # @param tab [String | Array] labels(s) of the UI tabs
156
- # @param index [String | Array] index route(s) for each tab
157
- # @param root_dir [String] directory location to find assets, locales and views, typically `web/` within the gemfile
158
- # @param asset_paths [Array] one or more directories under {root}/assets/{name} to be publicly served, e.g. ["js", "css", "img"]
159
- # @param cache_for [Integer] amount of time to cache assets, default one day
160
- #
161
- # TODO name, tab and index will be mandatory in 8.0
162
- #
163
- # Web extensions will have a root `web/` directory with `locales/`, `assets/`
164
- # and `views/` subdirectories.
165
- def self.register(extension, name: nil, tab: nil, index: nil, root_dir: nil, cache_for: 86400, asset_paths: nil)
166
- tab = Array(tab)
167
- index = Array(index)
168
- tab.zip(index).each do |tab, index|
169
- tabs[tab] = index
170
- end
171
- if root_dir
172
- locdir = File.join(root_dir, "locales")
173
- locales << locdir if File.directory?(locdir)
174
-
175
- if asset_paths && name
176
- # if you have {root}/assets/{name}/js/scripts.js
177
- # and {root}/assets/{name}/css/styles.css
178
- # you would pass in:
179
- # asset_paths: ["js", "css"]
180
- # See script_tag and style_tag in web/helpers.rb
181
- assdir = File.join(root_dir, "assets")
182
- assurls = Array(asset_paths).map { |x| "/#{name}/#{x}" }
183
- assetprops = {
184
- urls: assurls,
185
- root: assdir,
186
- cascade: true
187
- }
188
- assetprops[:header_rules] = [[:all, {Rack::CACHE_CONTROL => "private, max-age=#{cache_for.to_i}"}]] if cache_for
189
- middlewares << [[Rack::Static, assetprops], nil]
190
- end
191
- end
192
-
193
- yield self if block_given?
194
- extension.registered(WebApplication)
95
+ @app ||= build(@@config)
195
96
  end
196
97
 
197
98
  private
198
99
 
199
- def build
200
- klass = self.class
201
- m = middlewares
100
+ def build(cfg)
101
+ cfg.freeze
102
+ m = cfg.middlewares
202
103
 
203
104
  rules = []
204
- rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
105
+ rules = [[:all, {"cache-control" => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
205
106
 
206
107
  ::Rack::Builder.new do
207
108
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
@@ -209,18 +110,12 @@ module Sidekiq
209
110
  cascade: true,
210
111
  header_rules: rules
211
112
  m.each { |middleware, block| use(*middleware, &block) }
212
- use Sidekiq::Web::CsrfProtection unless $TESTING
213
- run WebApplication.new(klass)
113
+ run Sidekiq::Web::Application.new(self.class)
214
114
  end
215
115
  end
216
116
  end
217
-
218
- Sidekiq::WebApplication.helpers WebHelpers
219
- Sidekiq::WebApplication.helpers Sidekiq::Paginator
220
-
221
- Sidekiq::WebAction.class_eval <<-RUBY, Web::LAYOUT, -1 # standard:disable Style/EvalWithLocation
222
- def _render
223
- #{ERB.new(File.read(Web::LAYOUT)).src}
224
- end
225
- RUBY
226
117
  end
118
+
119
+ require "sidekiq/web/router"
120
+ require "sidekiq/web/action"
121
+ require "sidekiq/web/application"