sidekiq 5.2.5 → 6.0.3
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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +82 -0
- data/.gitignore +0 -2
- data/.standard.yml +20 -0
- data/6.0-Upgrade.md +72 -0
- data/COMM-LICENSE +11 -9
- data/Changes.md +136 -0
- data/Ent-2.0-Upgrade.md +37 -0
- data/Ent-Changes.md +32 -1
- data/Gemfile +12 -17
- data/Gemfile.lock +196 -0
- data/Pro-5.0-Upgrade.md +25 -0
- data/Pro-Changes.md +26 -2
- data/README.md +19 -31
- data/Rakefile +5 -4
- data/bin/sidekiqload +33 -25
- data/bin/sidekiqmon +8 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +20 -12
- data/lib/sidekiq/api.rb +230 -214
- data/lib/sidekiq/cli.rb +111 -174
- data/lib/sidekiq/client.rb +55 -46
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +10 -20
- data/lib/sidekiq/extensions/active_record.rb +9 -7
- data/lib/sidekiq/extensions/class_methods.rb +9 -7
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
- data/lib/sidekiq/fetch.rb +11 -12
- data/lib/sidekiq/job_logger.rb +45 -7
- data/lib/sidekiq/job_retry.rb +71 -60
- data/lib/sidekiq/launcher.rb +57 -51
- data/lib/sidekiq/logger.rb +165 -0
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/chain.rb +14 -4
- 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 +83 -75
- data/lib/sidekiq/rails.rb +23 -29
- data/lib/sidekiq/redis_connection.rb +31 -37
- data/lib/sidekiq/scheduled.rb +28 -29
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/testing.rb +34 -23
- data/lib/sidekiq/util.rb +17 -16
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web/action.rb +14 -10
- data/lib/sidekiq/web/application.rb +64 -66
- data/lib/sidekiq/web/helpers.rb +89 -71
- data/lib/sidekiq/web/router.rb +17 -14
- data/lib/sidekiq/web.rb +41 -49
- data/lib/sidekiq/worker.rb +129 -97
- data/lib/sidekiq.rb +61 -42
- data/sidekiq.gemspec +16 -16
- data/web/assets/javascripts/dashboard.js +4 -23
- data/web/assets/stylesheets/application-dark.css +125 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +1 -1
- data/web/locales/de.yml +14 -2
- data/web/locales/ja.yml +2 -1
- data/web/views/_job_info.erb +2 -1
- data/web/views/busy.erb +4 -1
- data/web/views/dead.erb +2 -2
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +4 -1
- data/web/views/queue.erb +10 -1
- data/web/views/queues.erb +1 -1
- data/web/views/retries.erb +4 -1
- data/web/views/retry.erb +2 -2
- data/web/views/scheduled.erb +4 -1
- metadata +21 -32
- data/.travis.yml +0 -17
- data/Appraisals +0 -9
- data/bin/sidekiqctl +0 -237
- data/gemfiles/rails_4.gemfile +0 -31
- data/gemfiles/rails_5.gemfile +0 -31
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
| @@ -5,8 +5,7 @@ module Sidekiq | |
| 5 5 | 
             
                extend WebRouter
         | 
| 6 6 |  | 
| 7 7 | 
             
                CONTENT_LENGTH = "Content-Length"
         | 
| 8 | 
            -
                 | 
| 9 | 
            -
                REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
         | 
| 8 | 
            +
                REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
         | 
| 10 9 | 
             
                CSP_HEADER = [
         | 
| 11 10 | 
             
                  "default-src 'self' https: http:",
         | 
| 12 11 | 
             
                  "child-src 'self'",
         | 
| @@ -20,8 +19,8 @@ module Sidekiq | |
| 20 19 | 
             
                  "script-src 'self' https: http: 'unsafe-inline'",
         | 
| 21 20 | 
             
                  "style-src 'self' https: http: 'unsafe-inline'",
         | 
| 22 21 | 
             
                  "worker-src 'self'",
         | 
| 23 | 
            -
                  "base-uri 'self'"
         | 
| 24 | 
            -
                ].join( | 
| 22 | 
            +
                  "base-uri 'self'",
         | 
| 23 | 
            +
                ].join("; ").freeze
         | 
| 25 24 |  | 
| 26 25 | 
             
                def initialize(klass)
         | 
| 27 26 | 
             
                  @klass = klass
         | 
| @@ -44,8 +43,8 @@ module Sidekiq | |
| 44 43 | 
             
                end
         | 
| 45 44 |  | 
| 46 45 | 
             
                get "/" do
         | 
| 47 | 
            -
                  @redis_info = redis_info.select{ |k, v| REDIS_KEYS.include? k }
         | 
| 48 | 
            -
                  stats_history = Sidekiq::Stats::History.new((params[ | 
| 46 | 
            +
                  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
         | 
| 47 | 
            +
                  stats_history = Sidekiq::Stats::History.new((params["days"] || 30).to_i)
         | 
| 49 48 | 
             
                  @processed_history = stats_history.processed
         | 
| 50 49 | 
             
                  @failed_history = stats_history.failed
         | 
| 51 50 |  | 
| @@ -57,14 +56,14 @@ module Sidekiq | |
| 57 56 | 
             
                end
         | 
| 58 57 |  | 
| 59 58 | 
             
                post "/busy" do
         | 
| 60 | 
            -
                  if params[ | 
| 61 | 
            -
                    p = Sidekiq::Process.new( | 
| 62 | 
            -
                    p.quiet! if params[ | 
| 63 | 
            -
                    p.stop! if params[ | 
| 59 | 
            +
                  if params["identity"]
         | 
| 60 | 
            +
                    p = Sidekiq::Process.new("identity" => params["identity"])
         | 
| 61 | 
            +
                    p.quiet! if params["quiet"]
         | 
| 62 | 
            +
                    p.stop! if params["stop"]
         | 
| 64 63 | 
             
                  else
         | 
| 65 64 | 
             
                    processes.each do |pro|
         | 
| 66 | 
            -
                      pro.quiet! if params[ | 
| 67 | 
            -
                      pro.stop! if params[ | 
| 65 | 
            +
                      pro.quiet! if params["quiet"]
         | 
| 66 | 
            +
                      pro.stop! if params["stop"]
         | 
| 68 67 | 
             
                    end
         | 
| 69 68 | 
             
                  end
         | 
| 70 69 |  | 
| @@ -82,9 +81,9 @@ module Sidekiq | |
| 82 81 |  | 
| 83 82 | 
             
                  halt(404) unless @name
         | 
| 84 83 |  | 
| 85 | 
            -
                  @count = (params[ | 
| 84 | 
            +
                  @count = (params["count"] || 25).to_i
         | 
| 86 85 | 
             
                  @queue = Sidekiq::Queue.new(@name)
         | 
| 87 | 
            -
                  (@current_page, @total_size, @messages) = page("queue:#{@name}", params[ | 
| 86 | 
            +
                  (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
         | 
| 88 87 | 
             
                  @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
         | 
| 89 88 |  | 
| 90 89 | 
             
                  erb(:queue)
         | 
| @@ -98,21 +97,22 @@ module Sidekiq | |
| 98 97 |  | 
| 99 98 | 
             
                post "/queues/:name/delete" do
         | 
| 100 99 | 
             
                  name = route_params[:name]
         | 
| 101 | 
            -
                  Sidekiq::Job.new(params[ | 
| 100 | 
            +
                  Sidekiq::Job.new(params["key_val"], name).delete
         | 
| 102 101 |  | 
| 103 102 | 
             
                  redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
         | 
| 104 103 | 
             
                end
         | 
| 105 104 |  | 
| 106 | 
            -
                get  | 
| 107 | 
            -
                  @count = (params[ | 
| 108 | 
            -
                  (@current_page, @total_size, @dead) = page("dead", params[ | 
| 105 | 
            +
                get "/morgue" do
         | 
| 106 | 
            +
                  @count = (params["count"] || 25).to_i
         | 
| 107 | 
            +
                  (@current_page, @total_size, @dead) = page("dead", params["page"], @count, reverse: true)
         | 
| 109 108 | 
             
                  @dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
         | 
| 110 109 |  | 
| 111 110 | 
             
                  erb(:morgue)
         | 
| 112 111 | 
             
                end
         | 
| 113 112 |  | 
| 114 113 | 
             
                get "/morgue/:key" do
         | 
| 115 | 
            -
                   | 
| 114 | 
            +
                  key = route_params[:key]
         | 
| 115 | 
            +
                  halt(404) unless key
         | 
| 116 116 |  | 
| 117 117 | 
             
                  @dead = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
         | 
| 118 118 |  | 
| @@ -123,10 +123,10 @@ module Sidekiq | |
| 123 123 | 
             
                  end
         | 
| 124 124 | 
             
                end
         | 
| 125 125 |  | 
| 126 | 
            -
                post  | 
| 127 | 
            -
                  redirect(request.path) unless params[ | 
| 126 | 
            +
                post "/morgue" do
         | 
| 127 | 
            +
                  redirect(request.path) unless params["key"]
         | 
| 128 128 |  | 
| 129 | 
            -
                  params[ | 
| 129 | 
            +
                  params["key"].each do |key|
         | 
| 130 130 | 
             
                    job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
         | 
| 131 131 | 
             
                    retry_or_delete_or_kill job, params if job
         | 
| 132 132 | 
             
                  end
         | 
| @@ -147,7 +147,8 @@ module Sidekiq | |
| 147 147 | 
             
                end
         | 
| 148 148 |  | 
| 149 149 | 
             
                post "/morgue/:key" do
         | 
| 150 | 
            -
                   | 
| 150 | 
            +
                  key = route_params[:key]
         | 
| 151 | 
            +
                  halt(404) unless key
         | 
| 151 152 |  | 
| 152 153 | 
             
                  job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
         | 
| 153 154 | 
             
                  retry_or_delete_or_kill job, params if job
         | 
| @@ -155,9 +156,9 @@ module Sidekiq | |
| 155 156 | 
             
                  redirect_with_query("#{root_path}morgue")
         | 
| 156 157 | 
             
                end
         | 
| 157 158 |  | 
| 158 | 
            -
                get  | 
| 159 | 
            -
                  @count = (params[ | 
| 160 | 
            -
                  (@current_page, @total_size, @retries) = page("retry", params[ | 
| 159 | 
            +
                get "/retries" do
         | 
| 160 | 
            +
                  @count = (params["count"] || 25).to_i
         | 
| 161 | 
            +
                  (@current_page, @total_size, @retries) = page("retry", params["page"], @count)
         | 
| 161 162 | 
             
                  @retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
         | 
| 162 163 |  | 
| 163 164 | 
             
                  erb(:retries)
         | 
| @@ -173,10 +174,10 @@ module Sidekiq | |
| 173 174 | 
             
                  end
         | 
| 174 175 | 
             
                end
         | 
| 175 176 |  | 
| 176 | 
            -
                post  | 
| 177 | 
            -
                  redirect(request.path) unless params[ | 
| 177 | 
            +
                post "/retries" do
         | 
| 178 | 
            +
                  redirect(request.path) unless params["key"]
         | 
| 178 179 |  | 
| 179 | 
            -
                  params[ | 
| 180 | 
            +
                  params["key"].each do |key|
         | 
| 180 181 | 
             
                    job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
         | 
| 181 182 | 
             
                    retry_or_delete_or_kill job, params if job
         | 
| 182 183 | 
             
                  end
         | 
| @@ -210,9 +211,9 @@ module Sidekiq | |
| 210 211 | 
             
                  redirect_with_query("#{root_path}retries")
         | 
| 211 212 | 
             
                end
         | 
| 212 213 |  | 
| 213 | 
            -
                get  | 
| 214 | 
            -
                  @count = (params[ | 
| 215 | 
            -
                  (@current_page, @total_size, @scheduled) = page("schedule", params[ | 
| 214 | 
            +
                get "/scheduled" do
         | 
| 215 | 
            +
                  @count = (params["count"] || 25).to_i
         | 
| 216 | 
            +
                  (@current_page, @total_size, @scheduled) = page("schedule", params["page"], @count)
         | 
| 216 217 | 
             
                  @scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
         | 
| 217 218 |  | 
| 218 219 | 
             
                  erb(:scheduled)
         | 
| @@ -228,10 +229,10 @@ module Sidekiq | |
| 228 229 | 
             
                  end
         | 
| 229 230 | 
             
                end
         | 
| 230 231 |  | 
| 231 | 
            -
                post  | 
| 232 | 
            -
                  redirect(request.path) unless params[ | 
| 232 | 
            +
                post "/scheduled" do
         | 
| 233 | 
            +
                  redirect(request.path) unless params["key"]
         | 
| 233 234 |  | 
| 234 | 
            -
                  params[ | 
| 235 | 
            +
                  params["key"].each do |key|
         | 
| 235 236 | 
             
                    job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
         | 
| 236 237 | 
             
                    delete_or_add_queue job, params if job
         | 
| 237 238 | 
             
                  end
         | 
| @@ -240,7 +241,8 @@ module Sidekiq | |
| 240 241 | 
             
                end
         | 
| 241 242 |  | 
| 242 243 | 
             
                post "/scheduled/:key" do
         | 
| 243 | 
            -
                   | 
| 244 | 
            +
                  key = route_params[:key]
         | 
| 245 | 
            +
                  halt(404) unless key
         | 
| 244 246 |  | 
| 245 247 | 
             
                  job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
         | 
| 246 248 | 
             
                  delete_or_add_queue job, params if job
         | 
| @@ -248,48 +250,44 @@ module Sidekiq | |
| 248 250 | 
             
                  redirect_with_query("#{root_path}scheduled")
         | 
| 249 251 | 
             
                end
         | 
| 250 252 |  | 
| 251 | 
            -
                get  | 
| 253 | 
            +
                get "/dashboard/stats" do
         | 
| 252 254 | 
             
                  redirect "#{root_path}stats"
         | 
| 253 255 | 
             
                end
         | 
| 254 256 |  | 
| 255 | 
            -
                get  | 
| 257 | 
            +
                get "/stats" do
         | 
| 256 258 | 
             
                  sidekiq_stats = Sidekiq::Stats.new
         | 
| 257 | 
            -
                  redis_stats | 
| 259 | 
            +
                  redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
         | 
| 258 260 | 
             
                  json(
         | 
| 259 261 | 
             
                    sidekiq: {
         | 
| 260 | 
            -
                      processed: | 
| 261 | 
            -
                      failed: | 
| 262 | 
            -
                      busy: | 
| 263 | 
            -
                      processes: | 
| 264 | 
            -
                      enqueued: | 
| 265 | 
            -
                      scheduled: | 
| 266 | 
            -
                      retries: | 
| 267 | 
            -
                      dead: | 
| 268 | 
            -
                      default_latency: sidekiq_stats.default_queue_latency
         | 
| 262 | 
            +
                      processed: sidekiq_stats.processed,
         | 
| 263 | 
            +
                      failed: sidekiq_stats.failed,
         | 
| 264 | 
            +
                      busy: sidekiq_stats.workers_size,
         | 
| 265 | 
            +
                      processes: sidekiq_stats.processes_size,
         | 
| 266 | 
            +
                      enqueued: sidekiq_stats.enqueued,
         | 
| 267 | 
            +
                      scheduled: sidekiq_stats.scheduled_size,
         | 
| 268 | 
            +
                      retries: sidekiq_stats.retry_size,
         | 
| 269 | 
            +
                      dead: sidekiq_stats.dead_size,
         | 
| 270 | 
            +
                      default_latency: sidekiq_stats.default_queue_latency,
         | 
| 269 271 | 
             
                    },
         | 
| 270 272 | 
             
                    redis: redis_stats,
         | 
| 271 273 | 
             
                    server_utc_time: server_utc_time
         | 
| 272 274 | 
             
                  )
         | 
| 273 275 | 
             
                end
         | 
| 274 276 |  | 
| 275 | 
            -
                get  | 
| 277 | 
            +
                get "/stats/queues" do
         | 
| 276 278 | 
             
                  json Sidekiq::Stats::Queues.new.lengths
         | 
| 277 279 | 
             
                end
         | 
| 278 280 |  | 
| 279 281 | 
             
                def call(env)
         | 
| 280 282 | 
             
                  action = self.class.match(env)
         | 
| 281 | 
            -
                  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass" | 
| 283 | 
            +
                  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
         | 
| 282 284 |  | 
| 283 | 
            -
                   | 
| 284 | 
            -
             | 
| 285 | 
            +
                  app = @klass
         | 
| 286 | 
            +
                  resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
         | 
| 285 287 | 
             
                    self.class.run_befores(app, action)
         | 
| 286 | 
            -
                     | 
| 287 | 
            -
             | 
| 288 | 
            -
                     | 
| 289 | 
            -
                      self.class.run_afters(app, action)
         | 
| 290 | 
            -
                    end
         | 
| 291 | 
            -
             | 
| 292 | 
            -
                    resp
         | 
| 288 | 
            +
                    action.instance_exec env, &action.block
         | 
| 289 | 
            +
                  ensure
         | 
| 290 | 
            +
                    self.class.run_afters(app, action)
         | 
| 293 291 | 
             
                  end
         | 
| 294 292 |  | 
| 295 293 | 
             
                  resp = case resp
         | 
| @@ -300,7 +298,7 @@ module Sidekiq | |
| 300 298 | 
             
                      "Content-Type" => "text/html",
         | 
| 301 299 | 
             
                      "Cache-Control" => "no-cache",
         | 
| 302 300 | 
             
                      "Content-Language" => action.locale,
         | 
| 303 | 
            -
                      "Content-Security-Policy" => CSP_HEADER
         | 
| 301 | 
            +
                      "Content-Security-Policy" => CSP_HEADER,
         | 
| 304 302 | 
             
                    }
         | 
| 305 303 |  | 
| 306 304 | 
             
                    [200, headers, [resp]]
         | 
| @@ -308,12 +306,12 @@ module Sidekiq | |
| 308 306 |  | 
| 309 307 | 
             
                  resp[1] = resp[1].dup
         | 
| 310 308 |  | 
| 311 | 
            -
                  resp[1][CONTENT_LENGTH] = resp[2]. | 
| 309 | 
            +
                  resp[1][CONTENT_LENGTH] = resp[2].sum(&:bytesize).to_s
         | 
| 312 310 |  | 
| 313 311 | 
             
                  resp
         | 
| 314 312 | 
             
                end
         | 
| 315 313 |  | 
| 316 | 
            -
                def self.helpers(mod=nil, &block)
         | 
| 314 | 
            +
                def self.helpers(mod = nil, &block)
         | 
| 317 315 | 
             
                  if block_given?
         | 
| 318 316 | 
             
                    WebAction.class_eval(&block)
         | 
| 319 317 | 
             
                  else
         | 
| @@ -321,11 +319,11 @@ module Sidekiq | |
| 321 319 | 
             
                  end
         | 
| 322 320 | 
             
                end
         | 
| 323 321 |  | 
| 324 | 
            -
                def self.before(path=nil, &block)
         | 
| 322 | 
            +
                def self.before(path = nil, &block)
         | 
| 325 323 | 
             
                  befores << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
         | 
| 326 324 | 
             
                end
         | 
| 327 325 |  | 
| 328 | 
            -
                def self.after(path=nil, &block)
         | 
| 326 | 
            +
                def self.after(path = nil, &block)
         | 
| 329 327 | 
             
                  afters << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
         | 
| 330 328 | 
             
                end
         | 
| 331 329 |  | 
| @@ -338,8 +336,8 @@ module Sidekiq | |
| 338 336 | 
             
                end
         | 
| 339 337 |  | 
| 340 338 | 
             
                def self.run_hooks(hooks, app, action)
         | 
| 341 | 
            -
                  hooks.select { |p,_| !p || p =~ action.env[WebRouter::PATH_INFO] } | 
| 342 | 
            -
             | 
| 339 | 
            +
                  hooks.select { |p, _| !p || p =~ action.env[WebRouter::PATH_INFO] }
         | 
| 340 | 
            +
                    .each { |_, b| action.instance_exec(action.env, app, &b) }
         | 
| 343 341 | 
             
                end
         | 
| 344 342 |  | 
| 345 343 | 
             
                def self.befores
         | 
    
        data/lib/sidekiq/web/helpers.rb
    CHANGED
    
    | @@ -1,15 +1,16 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 5 | 
            -
            require  | 
| 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 | 
            -
                   | 
| 12 | 
            -
                   | 
| 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|
         | 
| @@ -22,19 +23,19 @@ module Sidekiq | |
| 22 23 | 
             
                end
         | 
| 23 24 |  | 
| 24 25 | 
             
                def clear_caches
         | 
| 25 | 
            -
                   | 
| 26 | 
            -
                   | 
| 27 | 
            -
                   | 
| 26 | 
            +
                  @strings = nil
         | 
| 27 | 
            +
                  @locale_files = nil
         | 
| 28 | 
            +
                  @available_locales = nil
         | 
| 28 29 | 
             
                end
         | 
| 29 30 |  | 
| 30 31 | 
             
                def locale_files
         | 
| 31 | 
            -
                   | 
| 32 | 
            +
                  @locale_files ||= settings.locales.flat_map { |path|
         | 
| 32 33 | 
             
                    Dir["#{path}/*.yml"]
         | 
| 33 | 
            -
                   | 
| 34 | 
            +
                  }
         | 
| 34 35 | 
             
                end
         | 
| 35 36 |  | 
| 36 37 | 
             
                def available_locales
         | 
| 37 | 
            -
                   | 
| 38 | 
            +
                  @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
         | 
| 38 39 | 
             
                end
         | 
| 39 40 |  | 
| 40 41 | 
             
                def find_locale_files(lang)
         | 
| @@ -63,32 +64,35 @@ module Sidekiq | |
| 63 64 | 
             
                end
         | 
| 64 65 |  | 
| 65 66 | 
             
                def poll_path
         | 
| 66 | 
            -
                  if current_path !=  | 
| 67 | 
            -
                    root_path + current_path
         | 
| 67 | 
            +
                  if current_path != "" && params["poll"]
         | 
| 68 | 
            +
                    path = root_path + current_path
         | 
| 69 | 
            +
                    query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
         | 
| 70 | 
            +
                    path += "?#{query_string}" unless query_string.empty?
         | 
| 71 | 
            +
                    path
         | 
| 68 72 | 
             
                  else
         | 
| 69 73 | 
             
                    ""
         | 
| 70 74 | 
             
                  end
         | 
| 71 75 | 
             
                end
         | 
| 72 76 |  | 
| 73 77 | 
             
                def text_direction
         | 
| 74 | 
            -
                  get_locale[ | 
| 78 | 
            +
                  get_locale["TextDirection"] || "ltr"
         | 
| 75 79 | 
             
                end
         | 
| 76 80 |  | 
| 77 81 | 
             
                def rtl?
         | 
| 78 | 
            -
                  text_direction ==  | 
| 82 | 
            +
                  text_direction == "rtl"
         | 
| 79 83 | 
             
                end
         | 
| 80 84 |  | 
| 81 85 | 
             
                # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
         | 
| 82 86 | 
             
                def user_preferred_languages
         | 
| 83 | 
            -
                  languages = env[ | 
| 84 | 
            -
                  languages.to_s.downcase.gsub(/\s+/,  | 
| 85 | 
            -
                    locale, quality = language.split( | 
| 86 | 
            -
                    locale | 
| 87 | 
            +
                  languages = env["HTTP_ACCEPT_LANGUAGE"]
         | 
| 88 | 
            +
                  languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
         | 
| 89 | 
            +
                    locale, quality = language.split(";q=", 2)
         | 
| 90 | 
            +
                    locale = nil if locale == "*" # Ignore wildcards
         | 
| 87 91 | 
             
                    quality = quality ? quality.to_f : 1.0
         | 
| 88 92 | 
             
                    [locale, quality]
         | 
| 89 | 
            -
                   | 
| 93 | 
            +
                  }.sort { |(_, left), (_, right)|
         | 
| 90 94 | 
             
                    right <=> left
         | 
| 91 | 
            -
                   | 
| 95 | 
            +
                  }.map(&:first).compact
         | 
| 92 96 | 
             
                end
         | 
| 93 97 |  | 
| 94 98 | 
             
                # 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 +101,38 @@ module Sidekiq | |
| 97 101 | 
             
                # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
         | 
| 98 102 | 
             
                def locale
         | 
| 99 103 | 
             
                  @locale ||= begin
         | 
| 100 | 
            -
                    matched_locale = user_preferred_languages.map  | 
| 101 | 
            -
                      preferred_language = preferred.split( | 
| 104 | 
            +
                    matched_locale = user_preferred_languages.map { |preferred|
         | 
| 105 | 
            +
                      preferred_language = preferred.split("-", 2).first
         | 
| 102 106 |  | 
| 103 | 
            -
                      lang_group = available_locales.select  | 
| 104 | 
            -
                        preferred_language == available.split( | 
| 105 | 
            -
                       | 
| 107 | 
            +
                      lang_group = available_locales.select { |available|
         | 
| 108 | 
            +
                        preferred_language == available.split("-", 2).first
         | 
| 109 | 
            +
                      }
         | 
| 106 110 |  | 
| 107 111 | 
             
                      lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
         | 
| 108 | 
            -
                     | 
| 112 | 
            +
                    }.compact.first
         | 
| 109 113 |  | 
| 110 | 
            -
                    matched_locale ||  | 
| 114 | 
            +
                    matched_locale || "en"
         | 
| 111 115 | 
             
                  end
         | 
| 112 116 | 
             
                end
         | 
| 113 117 |  | 
| 118 | 
            +
                # within is used by Sidekiq Pro
         | 
| 119 | 
            +
                def display_tags(job, within = nil)
         | 
| 120 | 
            +
                  job.tags.map { |tag|
         | 
| 121 | 
            +
                    "<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
         | 
| 122 | 
            +
                  }.join(" ")
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 114 125 | 
             
                # mperham/sidekiq#3243
         | 
| 115 126 | 
             
                def unfiltered?
         | 
| 116 | 
            -
                  yield unless env[ | 
| 127 | 
            +
                  yield unless env["PATH_INFO"].start_with?("/filter/")
         | 
| 117 128 | 
             
                end
         | 
| 118 129 |  | 
| 119 130 | 
             
                def get_locale
         | 
| 120 131 | 
             
                  strings(locale)
         | 
| 121 132 | 
             
                end
         | 
| 122 133 |  | 
| 123 | 
            -
                def t(msg, options={})
         | 
| 124 | 
            -
                  string = get_locale[msg] || strings( | 
| 134 | 
            +
                def t(msg, options = {})
         | 
| 135 | 
            +
                  string = get_locale[msg] || strings("en")[msg] || msg
         | 
| 125 136 | 
             
                  if options.empty?
         | 
| 126 137 | 
             
                    string
         | 
| 127 138 | 
             
                  else
         | 
| @@ -129,6 +140,10 @@ module Sidekiq | |
| 129 140 | 
             
                  end
         | 
| 130 141 | 
             
                end
         | 
| 131 142 |  | 
| 143 | 
            +
                def sort_direction_label
         | 
| 144 | 
            +
                  params[:direction] == "asc" ? "↑" : "↓"
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 132 147 | 
             
                def workers
         | 
| 133 148 | 
             
                  @workers ||= Sidekiq::Workers.new
         | 
| 134 149 | 
             
                end
         | 
| @@ -141,12 +156,6 @@ module Sidekiq | |
| 141 156 | 
             
                  @stats ||= Sidekiq::Stats.new
         | 
| 142 157 | 
             
                end
         | 
| 143 158 |  | 
| 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 159 | 
             
                def redis_connection
         | 
| 151 160 | 
             
                  Sidekiq.redis do |conn|
         | 
| 152 161 | 
             
                    c = conn.connection
         | 
| @@ -163,24 +172,24 @@ module Sidekiq | |
| 163 172 | 
             
                end
         | 
| 164 173 |  | 
| 165 174 | 
             
                def root_path
         | 
| 166 | 
            -
                  "#{env[ | 
| 175 | 
            +
                  "#{env["SCRIPT_NAME"]}/"
         | 
| 167 176 | 
             
                end
         | 
| 168 177 |  | 
| 169 178 | 
             
                def current_path
         | 
| 170 | 
            -
                  @current_path ||= request.path_info.gsub(/^\//, | 
| 179 | 
            +
                  @current_path ||= request.path_info.gsub(/^\//, "")
         | 
| 171 180 | 
             
                end
         | 
| 172 181 |  | 
| 173 182 | 
             
                def current_status
         | 
| 174 | 
            -
                  workers.size == 0 ?  | 
| 183 | 
            +
                  workers.size == 0 ? "idle" : "active"
         | 
| 175 184 | 
             
                end
         | 
| 176 185 |  | 
| 177 186 | 
             
                def relative_time(time)
         | 
| 178 187 | 
             
                  stamp = time.getutc.iso8601
         | 
| 179 | 
            -
                  % | 
| 188 | 
            +
                  %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
         | 
| 180 189 | 
             
                end
         | 
| 181 190 |  | 
| 182 191 | 
             
                def job_params(job, score)
         | 
| 183 | 
            -
                  "#{score}-#{job[ | 
| 192 | 
            +
                  "#{score}-#{job["jid"]}"
         | 
| 184 193 | 
             
                end
         | 
| 185 194 |  | 
| 186 195 | 
             
                def parse_params(params)
         | 
| @@ -188,7 +197,7 @@ module Sidekiq | |
| 188 197 | 
             
                  [score.to_f, jid]
         | 
| 189 198 | 
             
                end
         | 
| 190 199 |  | 
| 191 | 
            -
                SAFE_QPARAMS = %w | 
| 200 | 
            +
                SAFE_QPARAMS = %w[page poll direction]
         | 
| 192 201 |  | 
| 193 202 | 
             
                # Merge options with current params, filter safe params, and stringify to query string
         | 
| 194 203 | 
             
                def qparams(options)
         | 
| @@ -197,9 +206,13 @@ module Sidekiq | |
| 197 206 | 
             
                    options[key.to_s] = options.delete(key)
         | 
| 198 207 | 
             
                  end
         | 
| 199 208 |  | 
| 200 | 
            -
                  params.merge(options) | 
| 209 | 
            +
                  to_query_string(params.merge(options))
         | 
| 210 | 
            +
                end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                def to_query_string(params)
         | 
| 213 | 
            +
                  params.map { |key, value|
         | 
| 201 214 | 
             
                    SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
         | 
| 202 | 
            -
                   | 
| 215 | 
            +
                  }.compact.join("&")
         | 
| 203 216 | 
             
                end
         | 
| 204 217 |  | 
| 205 218 | 
             
                def truncate(text, truncate_after_chars = 2000)
         | 
| @@ -207,9 +220,16 @@ module Sidekiq | |
| 207 220 | 
             
                end
         | 
| 208 221 |  | 
| 209 222 | 
             
                def display_args(args, truncate_after_chars = 2000)
         | 
| 210 | 
            -
                  args | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 223 | 
            +
                  return "Invalid job payload, args is nil" if args.nil?
         | 
| 224 | 
            +
                  return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                  begin
         | 
| 227 | 
            +
                    args.map { |arg|
         | 
| 228 | 
            +
                      h(truncate(to_display(arg), truncate_after_chars))
         | 
| 229 | 
            +
                    }.join(", ")
         | 
| 230 | 
            +
                  rescue
         | 
| 231 | 
            +
                    "Illegal job arguments: #{h args.inspect}"
         | 
| 232 | 
            +
                  end
         | 
| 213 233 | 
             
                end
         | 
| 214 234 |  | 
| 215 235 | 
             
                def csrf_tag
         | 
| @@ -217,23 +237,21 @@ module Sidekiq | |
| 217 237 | 
             
                end
         | 
| 218 238 |  | 
| 219 239 | 
             
                def to_display(arg)
         | 
| 240 | 
            +
                  arg.inspect
         | 
| 241 | 
            +
                rescue
         | 
| 220 242 | 
             
                  begin
         | 
| 221 | 
            -
                    arg. | 
| 222 | 
            -
                  rescue
         | 
| 223 | 
            -
                     | 
| 224 | 
            -
                      arg.to_s
         | 
| 225 | 
            -
                    rescue => ex
         | 
| 226 | 
            -
                      "Cannot display argument: [#{ex.class.name}] #{ex.message}"
         | 
| 227 | 
            -
                    end
         | 
| 243 | 
            +
                    arg.to_s
         | 
| 244 | 
            +
                  rescue => ex
         | 
| 245 | 
            +
                    "Cannot display argument: [#{ex.class.name}] #{ex.message}"
         | 
| 228 246 | 
             
                  end
         | 
| 229 247 | 
             
                end
         | 
| 230 248 |  | 
| 231 | 
            -
                RETRY_JOB_KEYS = Set.new(%w | 
| 249 | 
            +
                RETRY_JOB_KEYS = Set.new(%w[
         | 
| 232 250 | 
             
                  queue class args retry_count retried_at failed_at
         | 
| 233 251 | 
             
                  jid error_message error_class backtrace
         | 
| 234 252 | 
             
                  error_backtrace enqueued_at retry wrapped
         | 
| 235 | 
            -
                  created_at
         | 
| 236 | 
            -
                ) | 
| 253 | 
            +
                  created_at tags
         | 
| 254 | 
            +
                ])
         | 
| 237 255 |  | 
| 238 256 | 
             
                def retry_extra_items(retry_job)
         | 
| 239 257 | 
             
                  @retry_extra_items ||= {}.tap do |extra|
         | 
| @@ -250,8 +268,8 @@ module Sidekiq | |
| 250 268 | 
             
                    return number
         | 
| 251 269 | 
             
                  end
         | 
| 252 270 |  | 
| 253 | 
            -
                  options = {delimiter:  | 
| 254 | 
            -
                  parts = number.to_s.to_str.split( | 
| 271 | 
            +
                  options = {delimiter: ",", separator: "."}
         | 
| 272 | 
            +
                  parts = number.to_s.to_str.split(".")
         | 
| 255 273 | 
             
                  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
         | 
| 256 274 | 
             
                  parts.join(options[:separator])
         | 
| 257 275 | 
             
                end
         | 
| @@ -259,8 +277,8 @@ module Sidekiq | |
| 259 277 | 
             
                def h(text)
         | 
| 260 278 | 
             
                  ::Rack::Utils.escape_html(text)
         | 
| 261 279 | 
             
                rescue ArgumentError => e
         | 
| 262 | 
            -
                  raise unless e.message.eql?( | 
| 263 | 
            -
                  text.encode!( | 
| 280 | 
            +
                  raise unless e.message.eql?("invalid byte sequence in UTF-8")
         | 
| 281 | 
            +
                  text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
         | 
| 264 282 | 
             
                  retry
         | 
| 265 283 | 
             
                end
         | 
| 266 284 |  | 
| @@ -277,7 +295,7 @@ module Sidekiq | |
| 277 295 | 
             
                end
         | 
| 278 296 |  | 
| 279 297 | 
             
                def environment_title_prefix
         | 
| 280 | 
            -
                  environment = Sidekiq.options[:environment] || ENV[ | 
| 298 | 
            +
                  environment = Sidekiq.options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
         | 
| 281 299 |  | 
| 282 300 | 
             
                  "[#{environment.upcase}] " unless environment == "production"
         | 
| 283 301 | 
             
                end
         | 
| @@ -287,30 +305,30 @@ module Sidekiq | |
| 287 305 | 
             
                end
         | 
| 288 306 |  | 
| 289 307 | 
             
                def server_utc_time
         | 
| 290 | 
            -
                  Time.now.utc.strftime( | 
| 308 | 
            +
                  Time.now.utc.strftime("%H:%M:%S UTC")
         | 
| 291 309 | 
             
                end
         | 
| 292 310 |  | 
| 293 311 | 
             
                def redis_connection_and_namespace
         | 
| 294 312 | 
             
                  @redis_connection_and_namespace ||= begin
         | 
| 295 | 
            -
                    namespace_suffix = namespace | 
| 313 | 
            +
                    namespace_suffix = namespace.nil? ? "" : "##{namespace}"
         | 
| 296 314 | 
             
                    "#{redis_connection}#{namespace_suffix}"
         | 
| 297 315 | 
             
                  end
         | 
| 298 316 | 
             
                end
         | 
| 299 317 |  | 
| 300 318 | 
             
                def retry_or_delete_or_kill(job, params)
         | 
| 301 | 
            -
                  if params[ | 
| 319 | 
            +
                  if params["retry"]
         | 
| 302 320 | 
             
                    job.retry
         | 
| 303 | 
            -
                  elsif params[ | 
| 321 | 
            +
                  elsif params["delete"]
         | 
| 304 322 | 
             
                    job.delete
         | 
| 305 | 
            -
                  elsif params[ | 
| 323 | 
            +
                  elsif params["kill"]
         | 
| 306 324 | 
             
                    job.kill
         | 
| 307 325 | 
             
                  end
         | 
| 308 326 | 
             
                end
         | 
| 309 327 |  | 
| 310 328 | 
             
                def delete_or_add_queue(job, params)
         | 
| 311 | 
            -
                  if params[ | 
| 329 | 
            +
                  if params["delete"]
         | 
| 312 330 | 
             
                    job.delete
         | 
| 313 | 
            -
                  elsif params[ | 
| 331 | 
            +
                  elsif params["add_to_queue"]
         | 
| 314 332 | 
             
                    job.add_to_queue
         | 
| 315 333 | 
             
                  end
         | 
| 316 334 | 
             
                end
         |