resque-web-clone 0.0.6
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.
- checksums.yaml +7 -0
- data/README.md +89 -0
- data/Rakefile +34 -0
- data/app/assets/images/resque_web/idle.png +0 -0
- data/app/assets/images/resque_web/lifebuoy.png +0 -0
- data/app/assets/images/resque_web/poll.png +0 -0
- data/app/assets/images/resque_web/rails.png +0 -0
- data/app/assets/images/resque_web/working.png +0 -0
- data/app/assets/javascripts/resque_web/application.js +16 -0
- data/app/assets/javascripts/resque_web/bootstrap.js.coffee +4 -0
- data/app/assets/javascripts/resque_web/failure.js.coffee +7 -0
- data/app/assets/javascripts/resque_web/jquery.relative-date.js +47 -0
- data/app/assets/javascripts/resque_web/polling.js.coffee +25 -0
- data/app/assets/javascripts/resque_web/relative_date.js.coffee +27 -0
- data/app/assets/stylesheets/resque_web/application.css +13 -0
- data/app/assets/stylesheets/resque_web/bootstrap_and_overrides.css.scss.erb +146 -0
- data/app/controllers/resque_web/application_controller.rb +25 -0
- data/app/controllers/resque_web/failures_controller.rb +62 -0
- data/app/controllers/resque_web/overview_controller.rb +7 -0
- data/app/controllers/resque_web/queues_controller.rb +22 -0
- data/app/controllers/resque_web/stats_controller.rb +36 -0
- data/app/controllers/resque_web/workers_controller.rb +34 -0
- data/app/controllers/resque_web/working_controller.rb +8 -0
- data/app/helpers/resque_web/application_helper.rb +69 -0
- data/app/helpers/resque_web/failures_helper.rb +58 -0
- data/app/helpers/resque_web/overview_helper.rb +6 -0
- data/app/helpers/resque_web/queues_helper.rb +72 -0
- data/app/helpers/resque_web/stats_helper.rb +54 -0
- data/app/helpers/resque_web/workers_helper.rb +16 -0
- data/app/helpers/resque_web/working_helper.rb +19 -0
- data/app/views/layouts/resque_web/application.html.erb +58 -0
- data/app/views/resque_web/failures/_failed_job.html.erb +53 -0
- data/app/views/resque_web/failures/_overview.html.erb +24 -0
- data/app/views/resque_web/failures/index.html.erb +32 -0
- data/app/views/resque_web/failures/show.html.erb +20 -0
- data/app/views/resque_web/overview/show.html.erb +4 -0
- data/app/views/resque_web/queues/_queues.html.erb +4 -0
- data/app/views/resque_web/queues/_queues_advanced.html.erb +14 -0
- data/app/views/resque_web/queues/_queues_basic.html.erb +17 -0
- data/app/views/resque_web/queues/index.html.erb +1 -0
- data/app/views/resque_web/queues/show.html.erb +30 -0
- data/app/views/resque_web/stats/key.html.erb +26 -0
- data/app/views/resque_web/stats/keys.html.erb +17 -0
- data/app/views/resque_web/stats/redis.html.erb +14 -0
- data/app/views/resque_web/stats/resque.html.erb +14 -0
- data/app/views/resque_web/workers/index.html.erb +19 -0
- data/app/views/resque_web/workers/show.html.erb +38 -0
- data/app/views/resque_web/working/_working.html.erb +34 -0
- data/app/views/resque_web/working/index.html.erb +1 -0
- data/config/initializers/resque_config.rb +2 -0
- data/config/routes.rb +34 -0
- data/lib/resque_web/engine.rb +13 -0
- data/lib/resque_web/version.rb +3 -0
- data/lib/resque_web.rb +4 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/schema.rb +16 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/functional/failures_controller_test.rb +74 -0
- data/test/functional/overview_controller_test.rb +37 -0
- data/test/functional/queues_controller_test.rb +58 -0
- data/test/functional/stats_controller_test.rb +70 -0
- data/test/functional/workers_controller_test.rb +26 -0
- data/test/functional/working_controller_test.rb +19 -0
- data/test/integration/plugin_integration_test.rb +72 -0
- data/test/support/controller_test_helpers.rb +22 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/helpers/failures_helper_test.rb +15 -0
- data/test/unit/helpers/jobs_helper_test.rb +4 -0
- data/test/unit/helpers/overview_helper_test.rb +4 -0
- data/test/unit/helpers/queues_helper_test.rb +4 -0
- data/test/unit/helpers/retry_controller_helper_test.rb +4 -0
- data/test/unit/helpers/stats_helper_test.rb +4 -0
- data/test/unit/helpers/workers_helper_test.rb +4 -0
- data/test/unit/helpers/working_helper_test.rb +4 -0
- metadata +263 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 104e4126b4c0a02f04e36e862407b720af1207f8
|
|
4
|
+
data.tar.gz: 8168053bfed19ff1ad0446eef499b009d58fde55
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 382604fafcc67a152ba8df691c35c26e47989127a27b2613374d0abfef61ae29a8fe4d53062cefdc83a2b83acf72b19658513effa964272e4ef22dafabd75bf0
|
|
7
|
+
data.tar.gz: 1d7bfa320a70d15a14fbd3c2dc0bd7dc8a12f372ab561850a07beb8639e8e599787e612f7e5d55ef4bf49df3f837a0cd5a0229f1c8bd989101ada280406ed3f8
|
data/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
resque-web
|
|
2
|
+
==========
|
|
3
|
+
[](https://rubygems.org/gems/resque-web)
|
|
4
|
+
[](https://travis-ci.org/resque/resque-web)
|
|
5
|
+
[](https://gemnasium.com/resque/resque-web)
|
|
6
|
+
[](https://codeclimate.com/github/resque/resque-web)
|
|
7
|
+
[](https://coveralls.io/r/resque/resque-web)
|
|
8
|
+
|
|
9
|
+
A Rails-based frontend to the [Resque](https://github.com/resque/resque) job
|
|
10
|
+
queue system. This provides a similar interface to the existing Sinatra
|
|
11
|
+
application that comes bundled with Resque, but deploys like a Rails application
|
|
12
|
+
and leverages Rails conventions for factoring things like controllers, helpers,
|
|
13
|
+
and views.
|
|
14
|
+
|
|
15
|
+
# NOTICE
|
|
16
|
+
Note this is NOT the old sinatra interface that comes with Resque 1-x. This is
|
|
17
|
+
a new project based on rails. If you have any issues with old web server,
|
|
18
|
+
please file an issue on the [resque](https://github.com/resque/resque) project.
|
|
19
|
+
Note that the sinatra web interface will be gone in Resque 2.0 and this is
|
|
20
|
+
meant to be the replacement.
|
|
21
|
+
|
|
22
|
+
More documentation coming soon!
|
|
23
|
+
|
|
24
|
+
## Starting
|
|
25
|
+
Resque web is built as a rails engine.
|
|
26
|
+
|
|
27
|
+
Add it to your gemfile.
|
|
28
|
+
|
|
29
|
+
```Ruby
|
|
30
|
+
gem 'resque-web', require: 'resque_web'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Mount it in your config/routes.rb.
|
|
34
|
+
|
|
35
|
+
```Ruby
|
|
36
|
+
require "resque_web"
|
|
37
|
+
|
|
38
|
+
MyApp::Application.routes.draw do
|
|
39
|
+
mount ResqueWeb::Engine => "/resque_web"
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If you need a non-default resque server, use this environment variable.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
RAILS_RESQUE_REDIS=123.x.0.456:6712
|
|
47
|
+
```
|
|
48
|
+
## Security
|
|
49
|
+
|
|
50
|
+
You almost certainly want to limit access when using resque-web in production. Using [routes constraints](http://guides.rubyonrails.org/routing.html#request-based-constraints) is one way to achieve this:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# config/routes.rb
|
|
54
|
+
|
|
55
|
+
resque_web_constraint = lambda { |request| request.remote_ip == '127.0.0.1' }
|
|
56
|
+
constraints resque_web_constraint do
|
|
57
|
+
mount ResqueWeb::Engine => "/resque_web"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Another example of a route constraint using the current user when using Devise or another warden based authentication system:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# config/routes.rb
|
|
66
|
+
resque_web_constraint = lambda do |request|
|
|
67
|
+
current_user = request.env['warden'].user
|
|
68
|
+
current_user.present? && current_user.respond_to?(:is_admin?) && current_user.is_admin?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
constraints resque_web_constraint do
|
|
72
|
+
mount ResqueWeb::Engine => "/resque_web"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### HTTP Basic Authentication
|
|
78
|
+
|
|
79
|
+
HTTP Basic Authentication is supported out of the box. Simply set the environment variables `RESQUE_WEB_HTTP_BASIC_AUTH_USER` and `RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD` to turn it on. If you're using Resque with Heroku run `heroku config:set RESQUE_WEB_HTTP_BASIC_AUTH_USER=user RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD=secret` to get ResqueWeb secured.
|
|
80
|
+
|
|
81
|
+
## Plugins
|
|
82
|
+
|
|
83
|
+
In the past with the sinatra app it was fairly simple to just monkey-patch the
|
|
84
|
+
server to add more functionality/tabs. With this rails version you have to write
|
|
85
|
+
an engine under a specific namespace. Read more in PLUGINS.md.
|
|
86
|
+
|
|
87
|
+
## Screenshot
|
|
88
|
+
|
|
89
|
+

|
data/Rakefile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
rescue LoadError
|
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
require 'rdoc/task'
|
|
8
|
+
|
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
+
rdoc.title = 'ResqueWeb'
|
|
12
|
+
rdoc.options << '--line-numbers'
|
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
18
|
+
load 'rails/tasks/engine.rake'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Bundler::GemHelper.install_tasks
|
|
23
|
+
|
|
24
|
+
require 'rake/testtask'
|
|
25
|
+
|
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
|
27
|
+
t.libs << 'lib'
|
|
28
|
+
t.libs << 'test'
|
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
|
30
|
+
t.verbose = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
task :default => :test
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
|
2
|
+
// listed below.
|
|
3
|
+
//
|
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
|
6
|
+
//
|
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
|
8
|
+
// the compiled file.
|
|
9
|
+
//
|
|
10
|
+
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
|
11
|
+
// GO AFTER THE REQUIRES BELOW.
|
|
12
|
+
//
|
|
13
|
+
//= require jquery
|
|
14
|
+
//= require jquery_ujs
|
|
15
|
+
//= require twitter/bootstrap
|
|
16
|
+
//= require_tree .
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jQ plugin adapted from 37s' relative date tool
|
|
3
|
+
* Takes the format of "Jan 15, 2007 15:45:00 GMT" and converts it to a relative time
|
|
4
|
+
* @see http://37signals.com/svn/posts/1557-javascript-makes-relative-times-compatible-with-caching
|
|
5
|
+
*/
|
|
6
|
+
(function($, window, undefined){
|
|
7
|
+
|
|
8
|
+
$.fn.relativeDate = function(opts){
|
|
9
|
+
var defaults = {
|
|
10
|
+
dateGetter: function(el){
|
|
11
|
+
return $(el).text();
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
options = $.extend({}, defaults, opts),
|
|
15
|
+
time_ago_in_words_with_parsing = function(from) {
|
|
16
|
+
var date = new Date;
|
|
17
|
+
date.setTime(Date.parse(from));
|
|
18
|
+
return time_ago_in_words(date);
|
|
19
|
+
},
|
|
20
|
+
time_ago_in_words = function(from) {
|
|
21
|
+
return distance_of_time_in_words(new Date, from);
|
|
22
|
+
},
|
|
23
|
+
distance_of_time_in_words = function(to, from) {
|
|
24
|
+
var distance_in_seconds = ((to - from) / 1000);
|
|
25
|
+
var distance_in_minutes = Math.floor(distance_in_seconds / 60);
|
|
26
|
+
|
|
27
|
+
if (distance_in_minutes == 0) { return 'less than a minute ago'; }
|
|
28
|
+
if (distance_in_minutes == 1) { return 'a minute ago'; }
|
|
29
|
+
if (distance_in_minutes < 45) { return distance_in_minutes + ' minutes ago'; }
|
|
30
|
+
if (distance_in_minutes < 90) { return 'about 1 hour ago'; }
|
|
31
|
+
if (distance_in_minutes < 1440) { return 'about ' + Math.round(distance_in_minutes / 60) + ' hours ago'; }
|
|
32
|
+
if (distance_in_minutes < 2880) { return '1 day ago'; }
|
|
33
|
+
if (distance_in_minutes < 43200) { return Math.floor(distance_in_minutes / 1440) + ' days ago'; }
|
|
34
|
+
if (distance_in_minutes < 86400) { return 'about 1 month ago'; }
|
|
35
|
+
if (distance_in_minutes < 525960) { return Math.floor(distance_in_minutes / 43200) + ' months ago'; }
|
|
36
|
+
if (distance_in_minutes < 1051199) { return 'about 1 year ago'; }
|
|
37
|
+
|
|
38
|
+
return 'over ' + Math.floor(distance_in_minutes / 525960) + ' years ago';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return $(this).each(function(){
|
|
42
|
+
date_str = options.dateGetter(this);
|
|
43
|
+
$(this).html(time_ago_in_words_with_parsing(date_str));
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
})(jQuery, window);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
jQuery ->
|
|
2
|
+
poll_interval = 2
|
|
3
|
+
|
|
4
|
+
poll_start = (el) ->
|
|
5
|
+
href = el.attr("href")
|
|
6
|
+
el.parent().text "Starting..."
|
|
7
|
+
$("#main").addClass "polling"
|
|
8
|
+
setInterval (->
|
|
9
|
+
$.ajax
|
|
10
|
+
dataType: "text"
|
|
11
|
+
type: "get"
|
|
12
|
+
url: href
|
|
13
|
+
success: (data) ->
|
|
14
|
+
$("#main").html data
|
|
15
|
+
$("#main .time").relativeDate()
|
|
16
|
+
), poll_interval * 1000
|
|
17
|
+
location.hash = "#poll"
|
|
18
|
+
|
|
19
|
+
# auto start if hash is poll
|
|
20
|
+
poll_start $("a[rel=poll]") if location.hash == "#poll"
|
|
21
|
+
|
|
22
|
+
# start when click on link
|
|
23
|
+
$("a[rel=poll]").click (e) ->
|
|
24
|
+
e.preventDefault()
|
|
25
|
+
poll_start $(this)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
jQuery ->
|
|
2
|
+
relatizer = ->
|
|
3
|
+
dt = $(this).text()
|
|
4
|
+
$(this).relativeDate()
|
|
5
|
+
relatized = $(this).text()
|
|
6
|
+
if $(this).parents("a").size() > 0 || $(this).is("a")
|
|
7
|
+
$(this).relativeDate()
|
|
8
|
+
$(this).attr("title", dt) unless $(this).attr("title")
|
|
9
|
+
else
|
|
10
|
+
$(this).html """
|
|
11
|
+
<a href='#'' class='toggle_format' title='#{dt}'>
|
|
12
|
+
<span class='date_time'>#{dt}</span>
|
|
13
|
+
<span class='relatized_time'>#{relatized}</span>
|
|
14
|
+
</a>
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
format_toggler = (e) ->
|
|
18
|
+
e.preventDefault()
|
|
19
|
+
$(".time a.toggle_format span").toggle()
|
|
20
|
+
$(this).attr "title", $("span:hidden", this).text()
|
|
21
|
+
|
|
22
|
+
# changed html when doom is ready
|
|
23
|
+
$(".time").each relatizer
|
|
24
|
+
$(".time a.toggle_format .date_time").hide()
|
|
25
|
+
|
|
26
|
+
# add event on click in relative time to show date_time
|
|
27
|
+
$(".time").on "click", "a.toggle_format", format_toggler
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
|
10
|
+
*
|
|
11
|
+
*= require_self
|
|
12
|
+
*= require_tree .
|
|
13
|
+
*/
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
=require twitter-bootstrap-static/bootstrap
|
|
3
|
+
|
|
4
|
+
Use Font Awesome icons (default)
|
|
5
|
+
To use Glyphicons sprites instead of Font Awesome, replace with "require twitter-bootstrap-static/sprites"
|
|
6
|
+
=require twitter-bootstrap-static/fontawesome
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
html {
|
|
10
|
+
background: #efefef;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body {
|
|
14
|
+
padding-top: 40px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.navbar {
|
|
18
|
+
border-bottom: 5px solid #ce1212;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.navbar .brand, .navbar .navbar-brand {
|
|
22
|
+
color: white;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.navbar-fixed-top .navbar-inner, .navbar-static-top .navbar-inner {
|
|
26
|
+
border-width: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.navbar-inverse .nav>.active {
|
|
30
|
+
background: #fa2424; /* Old browsers */
|
|
31
|
+
background: -moz-linear-gradient(top, #fa2424 0%, #ce1212 100%); /* FF3.6+ */
|
|
32
|
+
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fa2424), color-stop(100%,#ce1212)); /* Chrome,Safari4+ */
|
|
33
|
+
background: -webkit-linear-gradient(top, #fa2424 0%,#ce1212 100%); /* Chrome10+,Safari5.1+ */
|
|
34
|
+
background: -o-linear-gradient(top, #fa2424 0%,#ce1212 100%); /* Opera 11.10+ */
|
|
35
|
+
background: -ms-linear-gradient(top, #fa2424 0%,#ce1212 100%); /* IE10+ */
|
|
36
|
+
background: linear-gradient(to bottom, #fa2424 0%,#ce1212 100%); /* W3C */
|
|
37
|
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fa2424', endColorstr='#ce1212',GradientType=0 ); /* IE6-8 */
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus {
|
|
41
|
+
background: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.container h1 { color:#ce1212; font-size: 24px; }
|
|
45
|
+
|
|
46
|
+
.label-info[href], .badge-info[href] {
|
|
47
|
+
background-color: #3A87AD;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.logo {
|
|
51
|
+
float: left;
|
|
52
|
+
margin: 2px 5px;
|
|
53
|
+
width: 36px;
|
|
54
|
+
height: 36px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* The remaining CSS rules come from the legacy Resque application */
|
|
58
|
+
.subnav { padding: 8px 10px 7px 10px; background:#ce1212; font-size:90%;}
|
|
59
|
+
.subnav li { display:inline; line-height: 16px; }
|
|
60
|
+
.subnav li a { color:#fff; text-decoration:none; margin:3px; display:inline-block; background:#dd5b5b; padding:5px; -webkit-border-radius:3px; -moz-border-radius:3px;}
|
|
61
|
+
.subnav li.current a { background:#fff; font-weight:bold; color:#ce1212;}
|
|
62
|
+
.subnav li a:active { background:#b00909;}
|
|
63
|
+
|
|
64
|
+
.container span.hl { background:#efefef; padding:2px;}
|
|
65
|
+
.container h2 { margin:10px 0; font-size:130%;}
|
|
66
|
+
.container table tr th { background:#efefef; color:#888; font-size:15px; font-weight:bold; min-width: 16px; }
|
|
67
|
+
.container table tr td.no-data { text-align:center; padding:40px 0; color:#999; font-style:italic; font-size:130%;}
|
|
68
|
+
.container p { margin:5px 0;}
|
|
69
|
+
.container p.intro { margin-bottom:15px; font-size:85%; color:#999; margin-top:0; line-height:1.3;}
|
|
70
|
+
.container h1.wi { margin-bottom:5px;}
|
|
71
|
+
.container p.sub { font-size:95%; color:#999;}
|
|
72
|
+
|
|
73
|
+
.container table.queues { width:40%;}
|
|
74
|
+
.container table.queues td.queue { font-weight:bold; width:50%;}
|
|
75
|
+
.container table.queues tr.failure td { background:#ffecec; color:#d37474;}
|
|
76
|
+
.container table.queues tr.failure td a{ color:#d37474;}
|
|
77
|
+
.container table.queues td.failure .badge a { color: white; }
|
|
78
|
+
.container table.queues tr.first_failure td { border-top:2px solid #d37474; }
|
|
79
|
+
|
|
80
|
+
.container table.jobs td.class { font-family:Monaco, "Courier New", monospace; font-size:90%; width:50%;}
|
|
81
|
+
.container table.jobs td.args{ width:50%;}
|
|
82
|
+
|
|
83
|
+
.container table.workers td.icon {width:1%; background:#efefef;text-align:center;}
|
|
84
|
+
.container table.workers td.icon img { height: 16px; width: 16px; }
|
|
85
|
+
.container table.workers td.where { width:25%;}
|
|
86
|
+
.container table.workers td.queues { width:35%;}
|
|
87
|
+
.container table.workers td.queues.queue { width:10%;}
|
|
88
|
+
.container table.workers td.process { width:35%;}
|
|
89
|
+
.container table.workers td.process span.waiting { color:#999; font-size:90%;}
|
|
90
|
+
.container table.workers td.process small { font-size:80%; margin-left:5px;}
|
|
91
|
+
.container table.workers td.process code { font-family:Monaco, "Courier New", monospace; font-size:90%;}
|
|
92
|
+
.container table.workers td.process small a { color:#999;}
|
|
93
|
+
.container.polling table.workers tr.working td { background:#f4ffe4; color:#7ac312;}
|
|
94
|
+
.container.polling table.workers tr.working td.where a { color:#7ac312;}
|
|
95
|
+
.container.polling table.workers tr.working td.process code { font-weight:bold;}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
.container table.stats th { font-size:100%; width:40%; color:#000;}
|
|
99
|
+
.container hr { border:0; border-top:5px solid #efefef; margin:15px 0;}
|
|
100
|
+
|
|
101
|
+
#footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
|
|
102
|
+
#footer p a { color:#999;}
|
|
103
|
+
|
|
104
|
+
.container p.poll { background:url(image-url('<%= asset_path('resque_web/poll.png') %>')) no-repeat 0 2px; padding:3px 0; padding-left:23px; float:right; font-size:85%; }
|
|
105
|
+
|
|
106
|
+
.container ul.failed {margin-top: 20px;}
|
|
107
|
+
.container ul.failed li {
|
|
108
|
+
background: #efefef; /* Old browsers */
|
|
109
|
+
background: -moz-linear-gradient(top, #efefef 0%, #fff 100%); /* FF3.6+ */
|
|
110
|
+
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#efefef), color-stop(100%,#fff)); /* Chrome,Safari4+ */
|
|
111
|
+
background: -webkit-linear-gradient(top, #efefef 0%,#fff 100%); /* Chrome10+,Safari5.1+ */
|
|
112
|
+
background: -o-linear-gradient(top, #efefef 0%,#fff 100%); /* Opera 11.10+ */
|
|
113
|
+
background: -ms-linear-gradient(top, #efefef 0%,#fff 100%); /* IE10+ */
|
|
114
|
+
background: linear-gradient(to bottom, #efefef 0%,#fff 100%); /* W3C */
|
|
115
|
+
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#efefef', endColorstr='#fff',GradientType=0 ); /* IE6-8 */
|
|
116
|
+
|
|
117
|
+
margin-top:10px;
|
|
118
|
+
padding:10px;
|
|
119
|
+
overflow:hidden;
|
|
120
|
+
border:1px solid #ccc;
|
|
121
|
+
-webkit-border-radius:5px;
|
|
122
|
+
-moz-border-radius:5px;
|
|
123
|
+
border-radius:5px;
|
|
124
|
+
}
|
|
125
|
+
.container ul.failed li dl dt {font-size:80%; color:#999; width:60px; float:left; padding-top:1px; text-align:right;}
|
|
126
|
+
.container ul.failed li dl dd {margin-bottom:10px; margin-left:70px;}
|
|
127
|
+
.container ul.failed li dl dd .retried { float:right; text-align: right; }
|
|
128
|
+
.container ul.failed li dl dd .retried .remove { display:none; margin-top: 8px; }
|
|
129
|
+
.container ul.failed li.hover dl dd .retried .remove { display:block; }
|
|
130
|
+
.container ul.failed li dl dd .controls { display:none; float:right; }
|
|
131
|
+
.container ul.failed li.hover dl dd .controls { display:block; }
|
|
132
|
+
.container ul.failed li dl dd code, .container ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%; white-space: pre-wrap;}
|
|
133
|
+
.container ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
|
|
134
|
+
.container ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
|
|
135
|
+
|
|
136
|
+
.container p.pagination { background:#efefef; padding:10px; overflow:hidden;}
|
|
137
|
+
.container p.pagination a.less { float:left;}
|
|
138
|
+
.container p.pagination a.more { float:right;}
|
|
139
|
+
|
|
140
|
+
.container form {float:right; margin-top:-10px;margin-left:10px;}
|
|
141
|
+
|
|
142
|
+
.container .time a.toggle_format {text-decoration:none;}
|
|
143
|
+
|
|
144
|
+
#failed tr.total td {background-color: #FFECEC; color: #D37474; font-size: 15px; font-weight: bold;}
|
|
145
|
+
#failed .center {text-align: center;}
|
|
146
|
+
#failed .failed_class { padding-left: 20px; font-size:12px; }
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module ResqueWeb
|
|
2
|
+
class ApplicationController < ActionController::Base
|
|
3
|
+
protect_from_forgery
|
|
4
|
+
before_filter :set_subtabs, :authorize
|
|
5
|
+
|
|
6
|
+
helper :all
|
|
7
|
+
|
|
8
|
+
def self.subtabs(*tab_names)
|
|
9
|
+
return defined?(@subtabs) ? @subtabs : [] if tab_names.empty?
|
|
10
|
+
@subtabs = tab_names
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def set_subtabs(subtabs = self.class.subtabs)
|
|
14
|
+
@subtabs = subtabs
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def authorize
|
|
20
|
+
if ENV["RESQUE_WEB_HTTP_BASIC_AUTH_USER"] && ENV["RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD"]
|
|
21
|
+
authenticate_or_request_with_http_basic {|u, p| u == ENV["RESQUE_WEB_HTTP_BASIC_AUTH_USER"] && p == ENV["RESQUE_WEB_HTTP_BASIC_AUTH_PASSWORD"] }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module ResqueWeb
|
|
2
|
+
class FailuresController < ApplicationController
|
|
3
|
+
|
|
4
|
+
# Display all jobs in the failure queue
|
|
5
|
+
#
|
|
6
|
+
# @param [Hash] params
|
|
7
|
+
# @option params [String] :class filters failures shown by class
|
|
8
|
+
# @option params [String] :queue filters failures shown by failure queue name
|
|
9
|
+
def index
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# remove an individual job from the failure queue
|
|
13
|
+
def destroy
|
|
14
|
+
Resque::Failure.remove(params[:id])
|
|
15
|
+
redirect_to failures_path(redirect_params)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# destroy all jobs from the failure queue
|
|
19
|
+
def destroy_all
|
|
20
|
+
queue = params[:queue] || 'failed'
|
|
21
|
+
Resque::Failure.clear(queue)
|
|
22
|
+
redirect_to failures_path(redirect_params)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# retry an individual job from the failure queue
|
|
26
|
+
def retry
|
|
27
|
+
reque_single_job(params[:id])
|
|
28
|
+
redirect_to failures_path(redirect_params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# retry all jobs from the failure queue
|
|
32
|
+
def retry_all
|
|
33
|
+
if params[:queue].present? && params[:queue]!="failed"
|
|
34
|
+
Resque::Failure.requeue_queue(params[:queue])
|
|
35
|
+
else
|
|
36
|
+
(Resque::Failure.count-1).downto(0).each { |id| reque_single_job(id) }
|
|
37
|
+
end
|
|
38
|
+
redirect_to failures_path(redirect_params)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
#API agnostic for Resque 2 with duck typing on requeue_and_remove
|
|
44
|
+
def reque_single_job(id)
|
|
45
|
+
if Resque::Failure.respond_to?(:requeue_and_remove)
|
|
46
|
+
Resque::Failure.requeue_and_remove(id)
|
|
47
|
+
else
|
|
48
|
+
Resque::Failure.requeue(id)
|
|
49
|
+
Resque::Failure.remove(id)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def redirect_params
|
|
54
|
+
{}.tap do |p|
|
|
55
|
+
if params[:queue].present?
|
|
56
|
+
p[:queue] = params[:queue]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ResqueWeb
|
|
2
|
+
class QueuesController < ApplicationController
|
|
3
|
+
|
|
4
|
+
def index
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def show
|
|
8
|
+
set_subtabs view_context.queue_names
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def destroy
|
|
12
|
+
Resque.remove_queue(params[:id])
|
|
13
|
+
redirect_to queues_path
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def clear
|
|
17
|
+
Resque.redis.del("queue:#{params[:id]}")
|
|
18
|
+
redirect_to queue_path(params[:id])
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module ResqueWeb
|
|
2
|
+
class StatsController < ApplicationController
|
|
3
|
+
subtabs :resque, :redis, :keys
|
|
4
|
+
|
|
5
|
+
def index
|
|
6
|
+
redirect_to action: "resque"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def resque
|
|
10
|
+
respond_to do |format|
|
|
11
|
+
format.html
|
|
12
|
+
format.json { render json: Hash[Resque.info.sort] }
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def redis
|
|
17
|
+
respond_to do |format|
|
|
18
|
+
format.html
|
|
19
|
+
format.json { render json: Hash[Resque.redis.info.sort] }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def keys
|
|
24
|
+
respond_to do |format|
|
|
25
|
+
format.html do
|
|
26
|
+
if params[:id]
|
|
27
|
+
render 'key'
|
|
28
|
+
else
|
|
29
|
+
render 'keys'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
format.json { render json: Resque.keys.sort }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module ResqueWeb
|
|
2
|
+
class WorkersController < ApplicationController
|
|
3
|
+
before_filter :display_subtabs
|
|
4
|
+
|
|
5
|
+
def index
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def show
|
|
9
|
+
if params[:id] && params[:id] != 'all'
|
|
10
|
+
@workers = view_context.worker_hosts[params[:id]].map { |id| Resque::Worker.find(id) }
|
|
11
|
+
else
|
|
12
|
+
@workers = Resque.workers
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# def destroy
|
|
17
|
+
# Resque::Failure.remove(params[:id])
|
|
18
|
+
# redirect_to failures_path(redirect_params)
|
|
19
|
+
# end
|
|
20
|
+
|
|
21
|
+
def destroy
|
|
22
|
+
Resque::Worker.find(params[:id]).unregister_worker
|
|
23
|
+
format.html { redirect_to workers_path, notice: 'Worker successully killed.' }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def display_subtabs
|
|
30
|
+
set_subtabs view_context.worker_hosts.map(&:first)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|