1
0
Fork 0
mirror of https://github.com/mperham/sidekiq.git synced 2022-11-09 13:52:34 -05:00

Migrate Sidekiq::Web to a pure Rack application

Migrate Sidekiq::Web a pure Rack application to avoid sinatra as
dependency. rack-protection is still needed.

The application is mounted on top of Rack::Builder, mantaining all of
the previous http interface.

Rack apps being used:

- Rack::File to serve assets
- Rack::Session::Cookie, the secret can be configured via
  Sidekiq::Web.session_secret
- Rack::Protection, same as before when using sinatra
- Sidekiq::WebApplication, described below.

Sidekiq::WebApplication is a very simple rack application composed of a
Sidekiq::WebRouter and a Sidekiq::WebAction dispatcher. This terminology
was adopted to be able to mantain Sidekiq::Web as a Rack app.

The Router is heavily inspired on Rack::Router[0] (and in many parts
identical), however not being retrocompatible.

The Action is a wrapper to provide convenience, DRY code and maintain
the old interface.

I tried to mantain most of the old application structures so that
customizations and monkey-patches are easily adjustable or even
further work be done to enforce retrocompatibility.

Testing welcome!

0: https://github.com/pjb3/rack-router
This commit is contained in:
Amadeus Folego 2016-07-26 11:03:17 -03:00
parent c89ff2e034
commit 9ea167db16
21 changed files with 519 additions and 263 deletions

View file

@ -1,26 +1,30 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'erb' require 'erb'
require 'yaml' require 'yaml'
require 'sinatra/base'
require 'sidekiq' require 'sidekiq'
require 'sidekiq/api' require 'sidekiq/api'
require 'sidekiq/paginator' require 'sidekiq/paginator'
require 'sidekiq/web_helpers'
require 'sidekiq/web/router'
require 'sidekiq/web/application'
require 'rack/protection'
require 'rack/builder'
require 'rack/static'
require 'rack/session/cookie'
module Sidekiq module Sidekiq
class Web < Sinatra::Base class Web
include Sidekiq::Paginator REQUEST_METHOD = 'REQUEST_METHOD'.freeze
PATH_INFO = 'PATH_INFO'.freeze
enable :sessions ROOT = File.expand_path(File.dirname(__FILE__) + "/../../web")
use ::Rack::Protection, :use => :authenticity_token unless ENV['RACK_ENV'] == 'test' VIEWS = "#{ROOT}/views"
LOCALES = ["#{ROOT}/locales"]
set :root, File.expand_path(File.dirname(__FILE__) + "/../../web") LAYOUT = "#{VIEWS}/layout.erb"
set :public_folder, proc { "#{root}/assets" } ASSETS = "#{ROOT}/assets"
set :views, proc { "#{root}/views" }
set :locales, ["#{root}/locales"]
helpers WebHelpers
DEFAULT_TABS = { DEFAULT_TABS = {
"Dashboard" => '', "Dashboard" => '',
@ -41,226 +45,56 @@ module Sidekiq
end end
alias_method :tabs, :custom_tabs alias_method :tabs, :custom_tabs
attr_accessor :app_url def locales
@locales ||= LOCALES
end
def session_secret=(secret)
@secret = secret
end
attr_accessor :app_url, :session_secret
attr_writer :locales
end end
get "/busy" do def initialize
erb :busy secret = Web.session_secret
end
post "/busy" do if secret.nil?
if params['identity'] # explicitly generating a session secret eagerly to play nice with preforking
p = Sidekiq::Process.new('identity' => params['identity']) begin
p.quiet! if params[:quiet] require 'securerandom'
p.stop! if params[:stop] secret = SecureRandom.hex(64)
else rescue LoadError, NotImplementedError
processes.each do |pro| # SecureRandom raises a NotImplementedError if no random device is available
pro.quiet! if params[:quiet] secret = "%064x" % Kernel.rand(2**256-1)
pro.stop! if params[:stop]
end end
end end
redirect "#{root_path}busy"
end
get "/queues" do @app = Rack::Builder.new do
@queues = Sidekiq::Queue.all %w(stylesheets javascripts images).each do |asset_dir|
erb :queues map "/#{asset_dir}" do
end run Rack::File.new("#{ASSETS}/#{asset_dir}")
end
end
get "/queues/:name" do use Rack::Session::Cookie, secret: secret
halt 404 unless params[:name] use ::Rack::Protection, use: :authenticity_token unless ENV['RACK_ENV'] == 'test'
@count = (params[:count] || 25).to_i
@name = params[:name]
@queue = Sidekiq::Queue.new(@name)
(@current_page, @total_size, @messages) = page("queue:#{@name}", params[:page], @count)
@messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
erb :queue
end
post "/queues/:name" do run WebApplication.new
Sidekiq::Queue.new(params[:name]).clear
redirect "#{root_path}queues"
end
post "/queues/:name/delete" do
Sidekiq::Job.new(params[:key_val], params[:name]).delete
redirect_with_query("#{root_path}queues/#{params[:name]}")
end
get '/morgue' do
@count = (params[:count] || 25).to_i
(@current_page, @total_size, @dead) = page("dead", params[:page], @count, reverse: true)
@dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
erb :morgue
end
get "/morgue/:key" do
halt 404 unless params['key']
@dead = Sidekiq::DeadSet.new.fetch(*parse_params(params['key'])).first
redirect "#{root_path}morgue" if @dead.nil?
erb :dead
end
post '/morgue' do
redirect request.path unless params['key']
params['key'].each do |key|
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
retry_or_delete_or_kill job, params if job
end
redirect_with_query("#{root_path}morgue")
end
post "/morgue/all/delete" do
Sidekiq::DeadSet.new.clear
redirect "#{root_path}morgue"
end
post "/morgue/all/retry" do
Sidekiq::DeadSet.new.retry_all
redirect "#{root_path}morgue"
end
post "/morgue/:key" do
halt 404 unless params['key']
job = Sidekiq::DeadSet.new.fetch(*parse_params(params['key'])).first
retry_or_delete_or_kill job, params if job
redirect_with_query("#{root_path}morgue")
end
get '/retries' do
@count = (params[:count] || 25).to_i
(@current_page, @total_size, @retries) = page("retry", params[:page], @count)
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
erb :retries
end
get "/retries/:key" do
@retry = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
redirect "#{root_path}retries" if @retry.nil?
erb :retry
end
post '/retries' do
redirect request.path unless params['key']
params['key'].each do |key|
job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
retry_or_delete_or_kill job, params if job
end
redirect_with_query("#{root_path}retries")
end
post "/retries/all/delete" do
Sidekiq::RetrySet.new.clear
redirect "#{root_path}retries"
end
post "/retries/all/retry" do
Sidekiq::RetrySet.new.retry_all
redirect "#{root_path}retries"
end
post "/retries/:key" do
job = Sidekiq::RetrySet.new.fetch(*parse_params(params['key'])).first
retry_or_delete_or_kill job, params if job
redirect_with_query("#{root_path}retries")
end
get '/scheduled' do
@count = (params[:count] || 25).to_i
(@current_page, @total_size, @scheduled) = page("schedule", params[:page], @count)
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
erb :scheduled
end
get "/scheduled/:key" do
@job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
redirect "#{root_path}scheduled" if @job.nil?
erb :scheduled_job_info
end
post '/scheduled' do
redirect request.path unless params['key']
params['key'].each do |key|
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
delete_or_add_queue job, params if job
end
redirect_with_query("#{root_path}scheduled")
end
post "/scheduled/:key" do
halt 404 unless params['key']
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(params['key'])).first
delete_or_add_queue job, params if job
redirect_with_query("#{root_path}scheduled")
end
get '/' do
@redis_info = redis_info.select{ |k, v| REDIS_KEYS.include? k }
stats_history = Sidekiq::Stats::History.new((params[:days] || 30).to_i)
@processed_history = stats_history.processed
@failed_history = stats_history.failed
erb :dashboard
end
REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
get '/dashboard/stats' do
redirect "#{root_path}stats"
end
get '/stats' do
sidekiq_stats = Sidekiq::Stats.new
redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
content_type :json
Sidekiq.dump_json(
sidekiq: {
processed: sidekiq_stats.processed,
failed: sidekiq_stats.failed,
busy: sidekiq_stats.workers_size,
processes: sidekiq_stats.processes_size,
enqueued: sidekiq_stats.enqueued,
scheduled: sidekiq_stats.scheduled_size,
retries: sidekiq_stats.retry_size,
dead: sidekiq_stats.dead_size,
default_latency: sidekiq_stats.default_queue_latency
},
redis: redis_stats
)
end
get '/stats/queues' do
queue_stats = Sidekiq::Stats::Queues.new
content_type :json
Sidekiq.dump_json(
queue_stats.lengths
)
end
private
def retry_or_delete_or_kill job, params
if params['retry']
job.retry
elsif params['delete']
job.delete
elsif params['kill']
job.kill
end end
end end
def delete_or_add_queue job, params def call(env)
if params['delete'] @app.call(env)
job.delete
elsif params['add_to_queue']
job.add_to_queue
end
end end
def self.call(env)
@app ||= new
@app.call(env)
end
ERB.new(File.read LAYOUT).def_method(WebAction, '_render')
end end
end end

View file

@ -0,0 +1,245 @@
# frozen_string_literal: true
module Sidekiq
class WebApplication
extend WebRouter
REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
get "/" do
@redis_info = redis_info.select{ |k, v| REDIS_KEYS.include? k }
stats_history = Sidekiq::Stats::History.new((params['days'] || 30).to_i)
@processed_history = stats_history.processed
@failed_history = stats_history.failed
render(:dashboard)
end
get "/busy" do
render(:busy)
end
post "/busy" do
if params['identity']
p = Sidekiq::Process.new('identity' => params['identity'])
p.quiet! if params['quiet']
p.stop! if params['stop']
else
processes.each do |pro|
pro.quiet! if params['quiet']
pro.stop! if params['stop']
end
end
redirect "busy"
end
get "/queues" do
@queues = Sidekiq::Queue.all
render(:queues)
end
get "/queues/:name" do
@name = route_params[:name]
next(not_found(env)) unless @name
@count = (params['count'] || 25).to_i
@queue = Sidekiq::Queue.new(@name)
(@current_page, @total_size, @messages) = page("queue:#{@name}", params['page'], @count)
@messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
render(:queue)
end
post "/queues/:name" do
Sidekiq::Queue.new(route_params[:name]).clear
redirect "queues"
end
post "/queues/:name/delete" do
name = route_params[:name]
Sidekiq::Job.new(params['key_val'], name).delete
redirect_with_query("queues/#{name}")
end
get '/morgue' do
@count = (params['count'] || 25).to_i
(@current_page, @total_size, @dead) = page("dead", params['page'], @count, reverse: true)
@dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
render(:morgue)
end
get "/morgue/:key" do
next not_found(env) unless key = route_params[:key]
@dead = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
if @dead.nil?
redirect "morgue"
else
render(:dead)
end
end
post '/morgue' do
next redirect(request.path) unless params['key']
params['key'].each do |key|
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
retry_or_delete_or_kill job, params if job
end
redirect_with_query("morgue")
end
post "/morgue/all/delete" do
Sidekiq::DeadSet.new.clear
redirect "morgue"
end
post "/morgue/all/retry" do
Sidekiq::DeadSet.new.retry_all
redirect "morgue"
end
post "/morgue/:key" do
next not_found(env) unless key = route_params[:key]
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
retry_or_delete_or_kill job, params if job
redirect_with_query("morgue")
end
get '/retries' do
@count = (params['count'] || 25).to_i
(@current_page, @total_size, @retries) = page("retry", params['page'], @count)
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
render(:retries)
end
get "/retries/:key" do
@retry = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
if @retry.nil?
redirect "retries"
else
render(:retry)
end
end
post '/retries' do
next redirect(request.path) unless params['key']
params['key'].each do |key|
job = Sidekiq::RetrySet.new.fetch(*parse_params(key)).first
retry_or_delete_or_kill job, params if job
end
redirect_with_query("retries")
end
post "/retries/all/delete" do
Sidekiq::RetrySet.new.clear
redirect "retries"
end
post "/retries/all/retry" do
Sidekiq::RetrySet.new.retry_all
redirect "retries"
end
post "/retries/:key" do
job = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
retry_or_delete_or_kill job, params if job
redirect_with_query("retries")
end
get '/scheduled' do
@count = (params['count'] || 25).to_i
(@current_page, @total_size, @scheduled) = page("schedule", params['page'], @count)
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
render(:scheduled)
end
get "/scheduled/:key" do
@job = Sidekiq::ScheduledSet.new.fetch(*parse_params(route_params[:key])).first
if @job.nil?
redirect "scheduled"
else
render(:scheduled_job_info)
end
end
post '/scheduled' do
next redirect(request.path) unless params['key']
params['key'].each do |key|
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
delete_or_add_queue job, params if job
end
redirect_with_query("scheduled")
end
post "/scheduled/:key" do
next not_found(env) unless key = route_params[:key]
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
delete_or_add_queue job, params if job
redirect_with_query("scheduled")
end
get '/dashboard/stats' do
redirect "stats"
end
get '/stats' do
sidekiq_stats = Sidekiq::Stats.new
redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
json(
sidekiq: {
processed: sidekiq_stats.processed,
failed: sidekiq_stats.failed,
busy: sidekiq_stats.workers_size,
processes: sidekiq_stats.processes_size,
enqueued: sidekiq_stats.enqueued,
scheduled: sidekiq_stats.scheduled_size,
retries: sidekiq_stats.retry_size,
dead: sidekiq_stats.dead_size,
default_latency: sidekiq_stats.default_queue_latency
},
redis: redis_stats
)
end
get '/stats/queues' do
json Sidekiq::Stats::Queues.new.lengths
end
def not_found(env)
[404, {}, []]
end
def call(env)
action = self.class.match(env) || WebAction.new(env, method(:not_found).to_proc)
action.instance_exec env, &action.app
end
end
end

View file

@ -9,7 +9,7 @@ module Sidekiq
@@strings[lang] ||= begin @@strings[lang] ||= begin
# Allow sidekiq-web extensions to add locale paths # Allow sidekiq-web extensions to add locale paths
# so extensions can be localized # so extensions can be localized
settings.locales.each_with_object({}) do |path, global| Web.locales.each_with_object({}) do |path, global|
find_locale_files(lang).each do |file| find_locale_files(lang).each do |file|
strs = YAML.load(File.open(file)) strs = YAML.load(File.open(file))
global.deep_merge!(strs[lang]) global.deep_merge!(strs[lang])
@ -24,7 +24,7 @@ module Sidekiq
end end
def locale_files def locale_files
@@locale_files ||= settings.locales.flat_map do |path| @@locale_files ||= Web.locales.flat_map do |path|
Dir["#{path}/*.yml"] Dir["#{path}/*.yml"]
end end
end end
@ -69,7 +69,7 @@ module Sidekiq
def locale def locale
@locale ||= begin @locale ||= begin
locale = 'en'.freeze locale = 'en'.freeze
languages = request.env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
languages.downcase.split(','.freeze).each do |lang| languages.downcase.split(','.freeze).each do |lang|
next if lang == '*'.freeze next if lang == '*'.freeze
lang = lang.split(';'.freeze)[0] lang = lang.split(';'.freeze)[0]

173
lib/sidekiq/web/router.rb Normal file
View file

@ -0,0 +1,173 @@
# frozen_string_literal: true
require 'sidekiq/web/helpers'
module Sidekiq
module WebRouter
GET = 'GET'.freeze
POST = 'POST'.freeze
HEAD = 'HEAD'.freeze
ROUTE_PARAMS = 'rack.route_params'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
PATH_INFO = 'PATH_INFO'.freeze
def get(path, &block)
route(GET, path, &block)
end
def post(path, &block)
route(POST, path, &block)
end
def route(method, path, &block)
@routes ||= []
@routes << WebRoute.new(method, path, block)
end
def match(env)
request_method = env[REQUEST_METHOD]
request_method = GET if request_method == HEAD
@routes.each do |route|
if params = route.match(request_method, env[PATH_INFO])
env[ROUTE_PARAMS] = params
return WebAction.new(env, route.app)
end
end
nil
end
end
class WebAction
include WebHelpers
include Sidekiq::Paginator
RACK_SESSION = 'rack.session'.freeze
CONTENT_TYPE = "Content-Type".freeze
LOCATION = "Location".freeze
TEXT_HTML = "text/html".freeze
APPLICATION_JSON = "application/json".freeze
attr_accessor :env, :app
def request
@request ||= Rack::Request.new(env)
end
def params
request.params
end
def route_params
env[WebRouter::ROUTE_PARAMS]
end
def session
env[RACK_SESSION]
end
def render(file)
output = _render { ERB.new(File.read "#{Web::VIEWS}/#{file}.erb").result(binding) }
[200, { CONTENT_TYPE => TEXT_HTML }, [output]]
end
def redirect(location)
[302, { LOCATION => "#{request.base_url}#{root_path}#{location}" }, []]
end
def json(payload)
[200, { CONTENT_TYPE => APPLICATION_JSON }, [Sidekiq.dump_json(payload)]]
end
def partial(file, locals = {})
ERB.new(File.read "#{Web::VIEWS}/_#{file}.erb").result(binding)
end
def initialize(env, app)
@env = env
@app = app
end
def retry_or_delete_or_kill(job, params)
if params['retry']
job.retry
elsif params['delete']
job.delete
elsif params['kill']
job.kill
end
end
def delete_or_add_queue(job, params)
if params['delete']
job.delete
elsif params['add_to_queue']
job.add_to_queue
end
end
end
class WebRoute
attr_accessor :request_method, :pattern, :app, :constraints, :name
NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^:$\/]+)/.freeze
def initialize(request_method, pattern, app)
@request_method = request_method
@pattern = pattern
@app = app
end
def regexp
@regexp ||= compile
end
def compile
p = if pattern.match(NAMED_SEGMENTS_PATTERN)
pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
else
pattern
end
Regexp.new("\\A#{p}\\Z")
end
def match(request_method, path)
return nil unless request_method == self.request_method
if path_match = path.match(regexp)
params = Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
params if meets_constraints(params)
end
end
def meets_constraints(params)
if constraints
constraints.each do |param, constraint|
unless params[param].to_s.match(constraint)
return false
end
end
end
true
end
def eql?(o)
o.is_a?(self.class) &&
o.request_method == request_method &&
o.pattern == pattern &&
o.app == app &&
o.constraints == constraints
end
alias == eql?
def hash
request_method.hash ^ pattern.hash ^ app.hash ^ constraints.hash
end
end
end

View file

@ -18,4 +18,4 @@ gem 'sidekiq', :path => '..'
#de gem 'pry-byebug' #de gem 'pry-byebug'
# sidekiq-web dependencies # sidekiq-web dependencies
gem 'sinatra' gem 'rack-protection'

View file

@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
gem.add_dependency 'redis', '~> 3.2', '>= 3.2.1' gem.add_dependency 'redis', '~> 3.2', '>= 3.2.1'
gem.add_dependency 'connection_pool', '~> 2.2', '>= 2.2.0' gem.add_dependency 'connection_pool', '~> 2.2', '>= 2.2.0'
gem.add_dependency 'concurrent-ruby', '~> 1.0' gem.add_dependency 'concurrent-ruby', '~> 1.0'
gem.add_dependency 'sinatra', '>= 1.4.7' gem.add_dependency 'rack-protection', '~> 1.5'
gem.add_development_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2' gem.add_development_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
gem.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0' gem.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0'
gem.add_development_dependency 'rake', '~> 10.0' gem.add_development_dependency 'rake', '~> 10.0'

View file

@ -3,7 +3,7 @@
require_relative 'helper' require_relative 'helper'
require 'sidekiq/web' require 'sidekiq/web'
require 'rack/test' require 'rack/test'
require 'tilt/erubis' #require 'tilt/erubis'
class TestWeb < Sidekiq::Test class TestWeb < Sidekiq::Test
@ -341,7 +341,6 @@ class TestWeb < Sidekiq::Test
assert last_response.body.include?( "&lt;a&gt;hello&lt;&#x2F;a&gt;" ) assert last_response.body.include?( "&lt;a&gt;hello&lt;&#x2F;a&gt;" )
assert !last_response.body.include?( "<a>hello</a>" ) assert !last_response.body.include?( "<a>hello</a>" )
# on /queues page # on /queues page
params = add_xss_retry # sorry, don't know how to easily make this show up on queues page otherwise. params = add_xss_retry # sorry, don't know how to easily make this show up on queues page otherwise.
post "/retries/#{job_params(*params)}", 'retry' => 'Retry' post "/retries/#{job_params(*params)}", 'retry' => 'Retry'
@ -372,21 +371,23 @@ class TestWeb < Sidekiq::Test
describe 'custom locales' do describe 'custom locales' do
before do before do
Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "fixtures") Sidekiq::Web.locales << File.join(File.dirname(__FILE__), "fixtures")
Sidekiq::Web.tabs['Custom Tab'] = '/custom' Sidekiq::Web.tabs['Custom Tab'] = '/custom'
Sidekiq::Web.get('/custom') do Sidekiq::WebApplication.get('/custom') do
clear_caches # ugly hack since I can't figure out how to access WebHelpers outside of this context clear_caches # ugly hack since I can't figure out how to access WebHelpers outside of this context
t('translated_text')
[200, { "Content-Type" => 'text/html' }, [ t('translated_text') ]]
end end
end end
after do after do
Sidekiq::Web.tabs.delete 'Custom Tab' Sidekiq::Web.tabs.delete 'Custom Tab'
Sidekiq::Web.settings.locales.pop Sidekiq::Web.locales.pop
end end
it 'can show user defined tab with custom locales' do it 'can show user defined tab with custom locales' do
get '/custom' get '/custom'
assert_match(/Changed text/, last_response.body) assert_match(/Changed text/, last_response.body)
end end
end end
@ -564,6 +565,7 @@ class TestWeb < Sidekiq::Test
Sidekiq.redis do |conn| Sidekiq.redis do |conn|
conn.zadd('retry', score, Sidekiq.dump_json(msg)) conn.zadd('retry', score, Sidekiq.dump_json(msg))
end end
[msg, score] [msg, score]
end end

View file

@ -1,3 +1,5 @@
<% job = locals[:job] %>
<header> <header>
<h3><%= t('Job') %></h3> <h3><%= t('Job') %></h3>
</header> </header>
@ -50,7 +52,7 @@
</td> </td>
</tr> </tr>
<% end %> <% end %>
<% if type == :retry %> <% if locals[:type] == :retry %>
<% if job['retry_count'] && job['retry_count'] > 0 %> <% if job['retry_count'] && job['retry_count'] > 0 %>
<tr> <tr>
<th><%= t('RetryCount') %></th> <th><%= t('RetryCount') %></th>
@ -71,13 +73,13 @@
<td><%= relative_time(job.at) %></td> <td><%= relative_time(job.at) %></td>
</tr> </tr>
<% end %> <% end %>
<% if type == :scheduled %> <% if locals[:type] == :scheduled %>
<tr> <tr>
<th><%= t('Scheduled') %></th> <th><%= t('Scheduled') %></th>
<td><%= relative_time(job.at) %></td> <td><%= relative_time(job.at) %></td>
</tr> </tr>
<% end %> <% end %>
<% if type == :dead %> <% if locals[:type] == :dead %>
<tr> <tr>
<th><%= t('LastRetry') %></th> <th><%= t('LastRetry') %></th>
<td><%= relative_time(job.at) %></td> <td><%= relative_time(job.at) %></td>

View file

@ -7,14 +7,14 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<div class="navbar-toggle collapsed navbar-livereload"> <div class="navbar-toggle collapsed navbar-livereload">
<%= erb :_poll_link %> <%= partial :poll_link %>
<% if Sidekiq::Web.app_url %> <% if Sidekiq::Web.app_url %>
<a class="btn btn-inverse" href="<%= Sidekiq::Web.app_url %>">Back to App</a> <a class="btn btn-inverse" href="<%= Sidekiq::Web.app_url %>">Back to App</a>
<% end %> <% end %>
</div> </div>
<a class="navbar-brand" href="<%= root_path %>"> <a class="navbar-brand" href="<%= root_path %>">
<%= Sidekiq::NAME %> <%= Sidekiq::NAME %>
<%= erb :_status %> <%= partial :status %>
</a> </a>
</div> </div>
@ -54,7 +54,7 @@
<ul class="nav navbar-nav navbar-right navbar-livereload" data-navbar="static"> <ul class="nav navbar-nav navbar-right navbar-livereload" data-navbar="static">
<li> <li>
<div class="poll-wrapper pull-right"> <div class="poll-wrapper pull-right">
<%= erb :_poll_link %> <%= partial :poll_link %>
<% if Sidekiq::Web.app_url %> <% if Sidekiq::Web.app_url %>
<a class="btn btn-inverse" href="<%= Sidekiq::Web.app_url %>"><%= t('BackToApp') %></a> <a class="btn btn-inverse" href="<%= Sidekiq::Web.app_url %>"><%= t('BackToApp') %></a>
<% end %> <% end %>

View file

@ -1,23 +1,23 @@
<% if @total_size > @count %> <% if @total_size > @count %>
<ul class="pagination pull-right"> <ul class="pagination pull-right">
<li class="<%= 'disabled' if @current_page == 1 %>"> <li class="<%= 'disabled' if @current_page == 1 %>">
<a href="<%= url %>?page=1">&laquo;</a> <a href="<%= locals[:url] %>?page=1">&laquo;</a>
</li> </li>
<% if @current_page > 1 %> <% if @current_page > 1 %>
<li> <li>
<a href="<%= url %>?<%= qparams(page: @current_page - 1) %>"><%= @current_page - 1 %></a> <a href="<%= locals[:url] %>?<%= qparams(page: @current_page - 1) %>"><%= @current_page - 1 %></a>
</li> </li>
<% end %> <% end %>
<li class="disabled"> <li class="disabled">
<a href="<%= url %>?<%= qparams(page: @current_page) %>"><%= @current_page %></a> <a href="<%= locals[:url] %>?<%= qparams(page: @current_page) %>"><%= @current_page %></a>
</li> </li>
<% if @total_size > @current_page * @count %> <% if @total_size > @current_page * @count %>
<li> <li>
<a href="<%= url %>?<%= qparams(page: @current_page + 1) %>"><%= @current_page + 1 %></a> <a href="<%= locals[:url] %>?<%= qparams(page: @current_page + 1) %>"><%= @current_page + 1 %></a>
</li> </li>
<% end %> <% end %>
<li class="<%= 'disabled' if @total_size <= @current_page * @count %>"> <li class="<%= 'disabled' if @total_size <= @current_page * @count %>">
<a href="<%= url %>?<%= qparams(page: (@total_size.to_f / @count).ceil) %>">&raquo;</a> <a href="<%= locals[:url] %>?<%= qparams(page: (@total_size.to_f / @count).ceil) %>">&raquo;</a>
</li> </li>
</ul> </ul>
<% end %> <% end %>

View file

@ -1,4 +1,4 @@
<% if current_path != '' && params[:poll] %> <% if current_path != '' && params['poll'] %>
<script> <script>
updatePage('<%= root_path + current_path %>') updatePage('<%= root_path + current_path %>')
</script> </script>

View file

@ -1,5 +1,5 @@
<% if current_path != '' %> <% if current_path != '' %>
<% if params[:poll] %> <% if params['poll'] %>
<a id="live-poll" class="btn btn-primary active" href="<%= root_path + current_path %>"><%= t('StopPolling') %></a> <a id="live-poll" class="btn btn-primary active" href="<%= root_path + current_path %>"><%= t('StopPolling') %></a>
<% else %> <% else %>
<a id="live-poll" class="btn btn-primary" href="<%= root_path + current_path %>?<%= qparams(poll: true) %>"><%= t('LivePoll') %></a> <a id="live-poll" class="btn btn-primary" href="<%= root_path + current_path %>?<%= qparams(poll: true) %>"><%= t('LivePoll') %></a>

View file

@ -23,10 +23,10 @@
<div class="row chart"> <div class="row chart">
<h5> <h5>
<span class="history-heading"><%= t('History') %></span> <span class="history-heading"><%= t('History') %></span>
<a href="<%= root_path %>?days=7" class="history-graph <%= "active" if params[:days] == "7" %>"><%= t('OneWeek') %></a> <a href="<%= root_path %>?days=7" class="history-graph <%= "active" if params['days'] == "7" %>"><%= t('OneWeek') %></a>
<a href="<%= root_path %>" class="history-graph <%= "active" if params[:days].nil? || params[:days] == "30" %>"><%= t('OneMonth') %></a> <a href="<%= root_path %>" class="history-graph <%= "active" if params['days'].nil? || params['days'] == "30" %>"><%= t('OneMonth') %></a>
<a href="<%= root_path %>?days=90" class="history-graph <%= "active" if params[:days] == "90" %>"><%= t('ThreeMonths') %></a> <a href="<%= root_path %>?days=90" class="history-graph <%= "active" if params['days'] == "90" %>"><%= t('ThreeMonths') %></a>
<a href="<%= root_path %>?days=180" class="history-graph <%= "active" if params[:days] == "180" %>"><%= t('SixMonths') %></a> <a href="<%= root_path %>?days=180" class="history-graph <%= "active" if params['days'] == "180" %>"><%= t('SixMonths') %></a>
</h5> </h5>
<div id="history" data-processed-label="<%= t('Processed') %>" data-failed-label="<%= t('Failed') %>" data-processed="<%= h Sidekiq.dump_json(@processed_history) %>" data-failed="<%= h Sidekiq.dump_json(@failed_history) %>" data-update-url="<%= root_path %>stats"></div> <div id="history" data-processed-label="<%= t('Processed') %>" data-failed-label="<%= t('Failed') %>" data-processed="<%= h Sidekiq.dump_json(@processed_history) %>" data-failed="<%= h Sidekiq.dump_json(@failed_history) %>" data-update-url="<%= root_path %>stats"></div>
@ -58,14 +58,14 @@
</div> </div>
<% end %> <% end %>
<% if @redis_info.fetch("used_memory_human", nil) %> <% if @redis_info.fetch("used_memory_human", nil) %>
<div class="stat"> <div class="stat">
<h3 class="used_memory_human"><%= @redis_info.fetch("used_memory_human") %></h3> <h3 class="used_memory_human"><%= @redis_info.fetch("used_memory_human") %></h3>
<p><%= t('MemoryUsage') %></p> <p><%= t('MemoryUsage') %></p>
</div> </div>
<% end %> <% end %>
<% if @redis_info.fetch("used_memory_peak_human", nil) %> <% if @redis_info.fetch("used_memory_peak_human", nil) %>
<div class="stat"> <div class="stat">
<h3 class="used_memory_peak_human"><%= @redis_info.fetch("used_memory_peak_human") %></h3> <h3 class="used_memory_peak_human"><%= @redis_info.fetch("used_memory_peak_human") %></h3>
<p><%= t('PeakMemoryUsage') %></p> <p><%= t('PeakMemoryUsage') %></p>

View file

@ -1,4 +1,4 @@
<%= erb :_job_info, :locals => {:job => @dead, :type => :dead} %> <%= partial :job_info, job: @dead, type: :dead %>
<h3><%= t('Error') %></h3> <h3><%= t('Error') %></h3>
<div class="table_container"> <div class="table_container">

View file

@ -12,12 +12,12 @@
<%= display_custom_head %> <%= display_custom_head %>
</head> </head>
<body class="admin"> <body class="admin">
<%= erb :_nav %> <%= partial :nav %>
<div id="page"> <div id="page">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-sm-12 summary_bar"> <div class="col-sm-12 summary_bar">
<%= erb :_summary %> <%= partial :summary %>
</div> </div>
<div class="col-sm-12"> <div class="col-sm-12">
@ -26,7 +26,7 @@
</div> </div>
</div> </div>
</div> </div>
<%= erb :_footer %> <%= partial :footer %>
<%= erb :_poll_js %> <%= partial :poll_js %>
</body> </body>
</html> </html>

View file

@ -4,7 +4,7 @@
</div> </div>
<% if @dead.size > 0 && @total_size > @count %> <% if @dead.size > 0 && @total_size > @count %>
<div class="col-sm-4"> <div class="col-sm-4">
<%= erb :_paging, :locals => { :url => "#{root_path}morgue" } %> <%= partial :paging, url: "#{root_path}morgue" %>
</div> </div>
<% end %> <% end %>
<%= filtering('dead') %> <%= filtering('dead') %>

View file

@ -8,7 +8,7 @@
</h3> </h3>
</div> </div>
<div class="col-sm-4 pull-right"> <div class="col-sm-4 pull-right">
<%= erb :_paging, :locals => { :url => "#{root_path}queues/#{@name}" } %> <%= partial :paging, url: "#{root_path}queues/#{@name}" %>
</div> </div>
</header> </header>
<div class="table_container"> <div class="table_container">
@ -42,4 +42,4 @@
<% end %> <% end %>
</table> </table>
</div> </div>
<%= erb :_paging, :locals => { :url => "#{root_path}queues/#{@name}" } %> <%= partial :paging, url: "#{root_path}queues/#{@name}" %>

View file

@ -4,7 +4,7 @@
</div> </div>
<% if @retries.size > 0 && @total_size > @count %> <% if @retries.size > 0 && @total_size > @count %>
<div class="col-sm-4"> <div class="col-sm-4">
<%= erb :_paging, :locals => { :url => "#{root_path}retries" } %> <%= partial :paging, url: "#{root_path}retries" %>
</div> </div>
<% end %> <% end %>
<%= filtering('retries') %> <%= filtering('retries') %>

View file

@ -1,4 +1,4 @@
<%= erb :_job_info, :locals => {:job => @retry, :type => :retry} %> <%= partial :job_info, job: @retry, type: :retry %>
<h3><%= t('Error') %></h3> <h3><%= t('Error') %></h3>
<div class="table_container"> <div class="table_container">

View file

@ -4,7 +4,7 @@
</div> </div>
<% if @scheduled.size > 0 && @total_size > @count %> <% if @scheduled.size > 0 && @total_size > @count %>
<div class="col-sm-4"> <div class="col-sm-4">
<%= erb :_paging, :locals => { :url => "#{root_path}scheduled" } %> <%= partial :paging, url: "#{root_path}scheduled" %>
</div> </div>
<% end %> <% end %>
<%= filtering('scheduled') %> <%= filtering('scheduled') %>

View file

@ -1,4 +1,4 @@
<%= erb :_job_info, :locals => {:job => @job, :type => :scheduled} %> <%= partial :job_info, job: @job, type: :scheduled %>
<form class="form-horizontal" action="<%= root_path %>scheduled/<%= job_params(@job, @job.score) %>" method="post"> <form class="form-horizontal" action="<%= root_path %>scheduled/<%= job_params(@job, @job.score) %>" method="post">
<%= csrf_tag %> <%= csrf_tag %>