diff --git a/lib/sidekiq/web.rb b/lib/sidekiq/web.rb
index c3d7c828..40452a39 100644
--- a/lib/sidekiq/web.rb
+++ b/lib/sidekiq/web.rb
@@ -34,6 +34,10 @@ module Sidekiq
}
class << self
+ def settings
+ self
+ end
+
def default_tabs
DEFAULT_TABS
end
@@ -47,12 +51,20 @@ module Sidekiq
@locales ||= LOCALES
end
+ def views
+ @views ||= VIEWS
+ end
+
def session_secret=(secret)
- @secret = secret
+ @session_secret = secret
end
attr_accessor :app_url, :session_secret
- attr_writer :locales
+ attr_writer :locales, :views
+ end
+
+ def settings
+ self.class.settings
end
def initialize
diff --git a/lib/sidekiq/web/action.rb b/lib/sidekiq/web/action.rb
index 4ccb68ae..830cce8a 100644
--- a/lib/sidekiq/web/action.rb
+++ b/lib/sidekiq/web/action.rb
@@ -6,17 +6,35 @@ module Sidekiq
LOCATION = "Location".freeze
- TEXT_HTML = { "Content-Type".freeze => "text/html".freeze }
- APPLICATION_JSON = { "Content-Type".freeze => "application/json".freeze }
+ CONTENT_TYPE = "Content-Type".freeze
+ TEXT_HTML = { CONTENT_TYPE => "text/html".freeze }
+ APPLICATION_JSON = { CONTENT_TYPE => "application/json".freeze }
- attr_accessor :env, :app
+ attr_accessor :env, :app, :type
+
+ def settings
+ Web.settings
+ end
def request
@request ||= Rack::Request.new(env)
end
+ def halt(res)
+ throw :halt, res
+ end
+
+ def redirect(location)
+ throw :halt, [302, { LOCATION => "#{request.base_url}#{location}" }, []]
+ end
+
def params
- request.params
+ indifferent_hash = Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
+
+ indifferent_hash.merge! request.params
+ route_params.each {|k,v| indifferent_hash[k.to_s] = v }
+
+ indifferent_hash
end
def route_params
@@ -27,28 +45,29 @@ module Sidekiq
env[RACK_SESSION]
end
- def erb(content, options = {})
- b = binding
+ def content_type(type)
+ @type = type
+ end
- if locals = options[:locals]
- locals.each {|k, v| b.local_variable_set(k, v) }
+ def erb(content, options = {})
+ if content.kind_of? Symbol
+ content = File.read("#{Web.settings.views}/#{content}.erb")
end
- _render { ERB.new(content).result(b) }
+ if @_erb
+ _erb(content, options[:locals])
+ else
+ @_erb = true
+ content = _erb(content, options[:locals])
+
+ _render { content }
+ end
end
- def partial(file, locals = {})
- ERB.new(File.read "#{Web::VIEWS}/_#{file}.erb").result(binding)
- end
+ def render(engine, content, options = {})
+ raise "Only erb templates are supported" if engine != :erb
- def redirect(location)
- [302, { LOCATION => "#{request.base_url}#{root_path}#{location}" }, []]
- end
-
- def render(file, locals = {})
- output = erb(File.read "#{Web::VIEWS}/#{file}.erb", locals: locals)
-
- [200, TEXT_HTML, [output]]
+ erb(content, options)
end
def json(payload)
@@ -59,5 +78,13 @@ module Sidekiq
@env = env
@app = app
end
+
+ private
+
+ def _erb(file, locals)
+ locals.each {|k, v| define_singleton_method(k){ v } } if locals
+
+ ERB.new(file).result(binding)
+ end
end
end
diff --git a/lib/sidekiq/web/application.rb b/lib/sidekiq/web/application.rb
index 158249a7..00110ac2 100644
--- a/lib/sidekiq/web/application.rb
+++ b/lib/sidekiq/web/application.rb
@@ -4,20 +4,25 @@ module Sidekiq
class WebApplication
extend WebRouter
+ CONTENT_TYPE = "Content-Type".freeze
REDIS_KEYS = %w(redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human)
NOPE = [404, {}, []]
+ def self.settings
+ Web.settings
+ 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
- render(:dashboard)
+ erb(:dashboard)
end
get "/busy" do
- render(:busy)
+ erb(:busy)
end
post "/busy" do
@@ -32,39 +37,39 @@ module Sidekiq
end
end
- redirect "busy"
+ redirect "#{root_path}busy"
end
get "/queues" do
@queues = Sidekiq::Queue.all
- render(:queues)
+ erb(:queues)
end
get "/queues/:name" do
@name = route_params[:name]
- next(NOPE) unless @name
+ halt(404) 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)
+ erb(:queue)
end
post "/queues/:name" do
Sidekiq::Queue.new(route_params[:name]).clear
- redirect "queues"
+ redirect "#{root_path}queues"
end
post "/queues/:name/delete" do
name = route_params[:name]
Sidekiq::Job.new(params['key_val'], name).delete
- redirect_with_query("queues/#{name}")
+ redirect_with_query("#{root_path}queues/#{name}")
end
get '/morgue' do
@@ -72,51 +77,51 @@ module Sidekiq
(@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)
+ erb(:morgue)
end
get "/morgue/:key" do
- next NOPE unless key = route_params[:key]
+ halt(404) unless key = route_params[:key]
@dead = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
if @dead.nil?
- redirect "morgue"
+ redirect "#{root_path}morgue"
else
- render(:dead)
+ erb(:dead)
end
end
post '/morgue' do
- next redirect(request.path) unless params['key']
+ 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")
+ redirect_with_query("#{root_path}morgue")
end
post "/morgue/all/delete" do
Sidekiq::DeadSet.new.clear
- redirect "morgue"
+ redirect "#{root_path}morgue"
end
post "/morgue/all/retry" do
Sidekiq::DeadSet.new.retry_all
- redirect "morgue"
+ redirect "#{root_path}morgue"
end
post "/morgue/:key" do
- next NOPE unless key = route_params[:key]
+ halt(404) 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")
+ redirect_with_query("#{root_path}morgue")
end
get '/retries' do
@@ -124,40 +129,40 @@ module Sidekiq
(@current_page, @total_size, @retries) = page("retry", params['page'], @count)
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
- render(:retries)
+ erb(:retries)
end
get "/retries/:key" do
@retry = Sidekiq::RetrySet.new.fetch(*parse_params(route_params[:key])).first
if @retry.nil?
- redirect "retries"
+ redirect "#{root_path}retries"
else
- render(:retry)
+ erb(:retry)
end
end
post '/retries' do
- next redirect(request.path) unless params['key']
+ 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")
+ redirect_with_query("#{root_path}retries")
end
post "/retries/all/delete" do
Sidekiq::RetrySet.new.clear
- redirect "retries"
+ redirect "#{root_path}retries"
end
post "/retries/all/retry" do
Sidekiq::RetrySet.new.retry_all
- redirect "retries"
+ redirect "#{root_path}retries"
end
post "/retries/:key" do
@@ -165,7 +170,7 @@ module Sidekiq
retry_or_delete_or_kill job, params if job
- redirect_with_query("retries")
+ redirect_with_query("#{root_path}retries")
end
get '/scheduled' do
@@ -173,41 +178,41 @@ module Sidekiq
(@current_page, @total_size, @scheduled) = page("schedule", params['page'], @count)
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
- render(:scheduled)
+ erb(:scheduled)
end
get "/scheduled/:key" do
@job = Sidekiq::ScheduledSet.new.fetch(*parse_params(route_params[:key])).first
if @job.nil?
- redirect "scheduled"
+ redirect "#{root_path}scheduled"
else
- render(:scheduled_job_info)
+ erb(:scheduled_job_info)
end
end
post '/scheduled' do
- next redirect(request.path) unless params['key']
+ 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")
+ redirect_with_query("#{root_path}scheduled")
end
post "/scheduled/:key" do
- next NOPE unless key = route_params[:key]
+ halt(404) 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")
+ redirect_with_query("#{root_path}scheduled")
end
get '/dashboard/stats' do
- redirect "stats"
+ redirect "#{root_path}stats"
end
get '/stats' do
@@ -238,42 +243,60 @@ module Sidekiq
action = self.class.match(env)
return NOPE unless action
- self.class.run_befores(env)
- resp = action.instance_exec env, &action.app
- self.class.run_afters(env)
+ resp = catch(:halt) do
+ self.class.run_befores(action)
+ resp = action.instance_exec env, &action.app
+ self.class.run_afters(action)
+
+ resp
+ end
case resp
when Array
resp
- when Integer
+ when Fixnum
[resp, {}, []]
else
- [200, WebAction::TEXT_HTML, [resp]]
+ headers = case action.type
+ when :json
+ WebAction::APPLICATION_JSON
+ when String
+ { WebAction::CONTENT_TYPE => action.type }
+ else
+ WebAction::TEXT_HTML
+ end
+
+ [200, headers, [resp]]
end
end
- def self.helpers(mod)
- WebAction.send(:include, mod)
- end
-
- def self.before(&block)
- befores << block
- end
-
- def self.after(&block)
- afters << block
- end
-
- def self.run_befores(env)
- befores.each do |b|
- b.call(env)
+ def self.helpers(mod=nil, &block)
+ if block_given?
+ WebAction.class_eval(&block)
+ else
+ WebAction.send(:include, mod)
end
end
- def self.run_afters(env)
- afters.each do |b|
- b.call(env)
- end
+ def self.before(path=nil, &block)
+ befores << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
+ end
+
+ def self.after(path=nil, &block)
+ afters << [path && Regexp.new("\\A#{path.gsub("*", ".*")}\\z"), block]
+ end
+
+ def self.run_befores(action)
+ run_hooks(befores, action)
+ end
+
+ def self.run_afters(action)
+ run_hooks(afters, action)
+ end
+
+ def self.run_hooks(hooks, action)
+ hooks.select { |p,_| !p || p =~ action.env[WebRouter::PATH_INFO] }.
+ each {|_,b| action.instance_exec(action.env, &b) }
end
def self.befores
diff --git a/lib/sidekiq/web/helpers.rb b/lib/sidekiq/web/helpers.rb
index d6d293d5..398740b8 100644
--- a/lib/sidekiq/web/helpers.rb
+++ b/lib/sidekiq/web/helpers.rb
@@ -10,7 +10,7 @@ module Sidekiq
@@strings[lang] ||= begin
# Allow sidekiq-web extensions to add locale paths
# so extensions can be localized
- Web.locales.each_with_object({}) do |path, global|
+ settings.locales.each_with_object({}) do |path, global|
find_locale_files(lang).each do |file|
strs = YAML.load(File.open(file))
global.deep_merge!(strs[lang])
@@ -25,7 +25,7 @@ module Sidekiq
end
def locale_files
- @@locale_files ||= Web.locales.flat_map do |path|
+ @@locale_files ||= settings.locales.flat_map do |path|
Dir["#{path}/*.yml"]
end
end
@@ -46,21 +46,13 @@ module Sidekiq
#
# <% end %>
#
- def add_to_head(&block)
+ def add_to_head
@head_html ||= []
- @head_html << block if block_given?
+ @head_html << yield.dup if block_given?
end
def display_custom_head
- return unless defined?(@head_html)
- @head_html.map { |block| capture(&block) }.join
- end
-
- # Simple capture method for erb templates. The origin was
- # capture method from sinatra-contrib library.
- def capture(&block)
- block.call
- eval('', block.binding)
+ @head_html.join if @head_html
end
# Given a browser request Accept-Language header like
diff --git a/lib/sidekiq/web/router.rb b/lib/sidekiq/web/router.rb
index 804f3976..60aac5f7 100644
--- a/lib/sidekiq/web/router.rb
+++ b/lib/sidekiq/web/router.rb
@@ -3,6 +3,7 @@
module Sidekiq
module WebRouter
GET = 'GET'.freeze
+ DELETE = 'DELETE'.freeze
POST = 'POST'.freeze
HEAD = 'HEAD'.freeze
@@ -18,6 +19,10 @@ module Sidekiq
route(POST, path, &block)
end
+ def delete(path, &block)
+ route(DELETE, path, &block)
+ end
+
def route(method, path, &block)
@routes ||= []
@routes << WebRoute.new(method, path, block)
diff --git a/test/test_web.rb b/test/test_web.rb
index 99b2603c..f82062b2 100644
--- a/test/test_web.rb
+++ b/test/test_web.rb
@@ -3,7 +3,6 @@
require_relative 'helper'
require 'sidekiq/web'
require 'rack/test'
-#require 'tilt/erubis'
class TestWeb < Sidekiq::Test
@@ -371,18 +370,17 @@ class TestWeb < Sidekiq::Test
describe 'custom locales' do
before do
- Sidekiq::Web.locales << File.join(File.dirname(__FILE__), "fixtures")
+ Sidekiq::Web.settings.locales << File.join(File.dirname(__FILE__), "fixtures")
Sidekiq::Web.tabs['Custom Tab'] = '/custom'
Sidekiq::WebApplication.get('/custom') do
clear_caches # ugly hack since I can't figure out how to access WebHelpers outside of this context
-
- [200, { "Content-Type" => 'text/html' }, [ t('translated_text') ]]
+ t('translated_text')
end
end
after do
Sidekiq::Web.tabs.delete 'Custom Tab'
- Sidekiq::Web.locales.pop
+ Sidekiq::Web.settings.locales.pop
end
it 'can show user defined tab with custom locales' do
diff --git a/web/views/_job_info.erb b/web/views/_job_info.erb
index 549a09ab..a943a7a2 100644
--- a/web/views/_job_info.erb
+++ b/web/views/_job_info.erb
@@ -1,5 +1,3 @@
-<% job = locals[:job] %>
-
<%= t('Job') %>