resque_signal_from_child 1.25.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +15 -0
  2. data/HISTORY.md +455 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +866 -0
  5. data/Rakefile +70 -0
  6. data/bin/resque +81 -0
  7. data/bin/resque-web +27 -0
  8. data/lib/resque.rb +487 -0
  9. data/lib/resque/errors.rb +13 -0
  10. data/lib/resque/failure.rb +113 -0
  11. data/lib/resque/failure/airbrake.rb +33 -0
  12. data/lib/resque/failure/base.rb +73 -0
  13. data/lib/resque/failure/multiple.rb +59 -0
  14. data/lib/resque/failure/redis.rb +108 -0
  15. data/lib/resque/failure/redis_multi_queue.rb +89 -0
  16. data/lib/resque/helpers.rb +101 -0
  17. data/lib/resque/job.rb +346 -0
  18. data/lib/resque/log_formatters/quiet_formatter.rb +7 -0
  19. data/lib/resque/log_formatters/verbose_formatter.rb +7 -0
  20. data/lib/resque/log_formatters/very_verbose_formatter.rb +8 -0
  21. data/lib/resque/logging.rb +18 -0
  22. data/lib/resque/plugin.rb +66 -0
  23. data/lib/resque/server.rb +271 -0
  24. data/lib/resque/server/helpers.rb +52 -0
  25. data/lib/resque/server/public/favicon.ico +0 -0
  26. data/lib/resque/server/public/idle.png +0 -0
  27. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  28. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  29. data/lib/resque/server/public/poll.png +0 -0
  30. data/lib/resque/server/public/ranger.js +78 -0
  31. data/lib/resque/server/public/reset.css +44 -0
  32. data/lib/resque/server/public/style.css +91 -0
  33. data/lib/resque/server/public/working.png +0 -0
  34. data/lib/resque/server/test_helper.rb +19 -0
  35. data/lib/resque/server/views/error.erb +1 -0
  36. data/lib/resque/server/views/failed.erb +29 -0
  37. data/lib/resque/server/views/failed_job.erb +50 -0
  38. data/lib/resque/server/views/failed_queues_overview.erb +24 -0
  39. data/lib/resque/server/views/key_sets.erb +19 -0
  40. data/lib/resque/server/views/key_string.erb +11 -0
  41. data/lib/resque/server/views/layout.erb +44 -0
  42. data/lib/resque/server/views/next_more.erb +22 -0
  43. data/lib/resque/server/views/overview.erb +4 -0
  44. data/lib/resque/server/views/queues.erb +58 -0
  45. data/lib/resque/server/views/stats.erb +62 -0
  46. data/lib/resque/server/views/workers.erb +109 -0
  47. data/lib/resque/server/views/working.erb +72 -0
  48. data/lib/resque/stat.rb +57 -0
  49. data/lib/resque/tasks.rb +83 -0
  50. data/lib/resque/vendor/utf8_util.rb +26 -0
  51. data/lib/resque/vendor/utf8_util/utf8_util_18.rb +91 -0
  52. data/lib/resque/vendor/utf8_util/utf8_util_19.rb +5 -0
  53. data/lib/resque/version.rb +3 -0
  54. data/lib/resque/worker.rb +772 -0
  55. data/lib/tasks/redis.rake +161 -0
  56. data/lib/tasks/resque.rake +2 -0
  57. data/test/airbrake_test.rb +27 -0
  58. data/test/dump.rdb +0 -0
  59. data/test/failure_base_test.rb +15 -0
  60. data/test/job_hooks_test.rb +464 -0
  61. data/test/job_plugins_test.rb +230 -0
  62. data/test/logging_test.rb +24 -0
  63. data/test/plugin_test.rb +116 -0
  64. data/test/redis-test-cluster.conf +115 -0
  65. data/test/redis-test.conf +115 -0
  66. data/test/resque-web_test.rb +59 -0
  67. data/test/resque_failure_redis_test.rb +19 -0
  68. data/test/resque_hook_test.rb +190 -0
  69. data/test/resque_test.rb +278 -0
  70. data/test/test_helper.rb +198 -0
  71. data/test/worker_test.rb +1015 -0
  72. metadata +196 -0
@@ -0,0 +1,95 @@
1
+ // All credit goes to Rick Olson.
2
+ (function($) {
3
+ $.fn.relatizeDate = function() {
4
+ return $(this).each(function() {
5
+ if ($(this).hasClass( 'relatized' )) return
6
+ $(this).text( $.relatizeDate(this) ).addClass( 'relatized' )
7
+ })
8
+ }
9
+
10
+ $.relatizeDate = function(element) {
11
+ return $.relatizeDate.timeAgoInWords( new Date($(element).text()) )
12
+ }
13
+
14
+ // shortcut
15
+ $r = $.relatizeDate
16
+
17
+ $.extend($.relatizeDate, {
18
+ shortDays: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
19
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
20
+ shortMonths: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
21
+ months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
22
+
23
+ /**
24
+ * Given a formatted string, replace the necessary items and return.
25
+ * Example: Time.now().strftime("%B %d, %Y") => February 11, 2008
26
+ * @param {String} format The formatted string used to format the results
27
+ */
28
+ strftime: function(date, format) {
29
+ var day = date.getDay(), month = date.getMonth();
30
+ var hours = date.getHours(), minutes = date.getMinutes();
31
+
32
+ var pad = function(num) {
33
+ var string = num.toString(10);
34
+ return new Array((2 - string.length) + 1).join('0') + string
35
+ };
36
+
37
+ return format.replace(/\%([aAbBcdHImMpSwyY])/g, function(part) {
38
+ switch(part[1]) {
39
+ case 'a': return $r.shortDays[day]; break;
40
+ case 'A': return $r.days[day]; break;
41
+ case 'b': return $r.shortMonths[month]; break;
42
+ case 'B': return $r.months[month]; break;
43
+ case 'c': return date.toString(); break;
44
+ case 'd': return pad(date.getDate()); break;
45
+ case 'H': return pad(hours); break;
46
+ case 'I': return pad((hours + 12) % 12); break;
47
+ case 'm': return pad(month + 1); break;
48
+ case 'M': return pad(minutes); break;
49
+ case 'p': return hours > 12 ? 'PM' : 'AM'; break;
50
+ case 'S': return pad(date.getSeconds()); break;
51
+ case 'w': return day; break;
52
+ case 'y': return pad(date.getFullYear() % 100); break;
53
+ case 'Y': return date.getFullYear().toString(); break;
54
+ }
55
+ })
56
+ },
57
+
58
+ timeAgoInWords: function(targetDate, includeTime) {
59
+ return $r.distanceOfTimeInWords(targetDate, new Date(), includeTime);
60
+ },
61
+
62
+ /**
63
+ * Return the distance of time in words between two Date's
64
+ * Example: '5 days ago', 'about an hour ago'
65
+ * @param {Date} fromTime The start date to use in the calculation
66
+ * @param {Date} toTime The end date to use in the calculation
67
+ * @param {Boolean} Include the time in the output
68
+ */
69
+ distanceOfTimeInWords: function(fromTime, toTime, includeTime) {
70
+ var delta = parseInt((toTime.getTime() - fromTime.getTime()) / 1000, 10);
71
+ if (delta < 60) {
72
+ return 'just now';
73
+ } else if (delta < 120) {
74
+ return 'about a minute ago';
75
+ } else if (delta < (45*60)) {
76
+ return (parseInt(delta / 60, 10)).toString() + ' minutes ago';
77
+ } else if (delta < (120*60)) {
78
+ return 'about an hour ago';
79
+ } else if (delta < (24*60*60)) {
80
+ return 'about ' + (parseInt(delta / 3600, 10)).toString() + ' hours ago';
81
+ } else if (delta < (48*60*60)) {
82
+ return '1 day ago';
83
+ } else {
84
+ var days = (parseInt(delta / 86400, 10)).toString();
85
+ if (days > 5) {
86
+ var fmt = '%B %d, %Y'
87
+ if (includeTime) fmt += ' %I:%M %p'
88
+ return $r.strftime(fromTime, fmt);
89
+ } else {
90
+ return days + " days ago"
91
+ }
92
+ }
93
+ }
94
+ })
95
+ })(jQuery);
Binary file
@@ -0,0 +1,78 @@
1
+ $(function() {
2
+ var poll_interval = 2
3
+
4
+ var relatizer = function(){
5
+ var dt = $(this).text(), relatized = $.relatizeDate(this)
6
+ if ($(this).parents("a").length > 0 || $(this).is("a")) {
7
+ $(this).relatizeDate()
8
+ if (!$(this).attr('title')) {
9
+ $(this).attr('title', dt)
10
+ }
11
+ } else {
12
+ $(this)
13
+ .text('')
14
+ .append( $('<a href="#" class="toggle_format" title="' + dt + '" />')
15
+ .append('<span class="date_time">' + dt +
16
+ '</span><span class="relatized_time">' +
17
+ relatized + '</span>') )
18
+ }
19
+ };
20
+
21
+ $('.time').each(relatizer);
22
+
23
+ $('.time a.toggle_format .date_time').hide()
24
+
25
+ var format_toggler = function(){
26
+ $('.time a.toggle_format span').toggle()
27
+ $(this).attr('title', $('span:hidden',this).text())
28
+ return false
29
+ };
30
+
31
+ $('.time a.toggle_format').click(format_toggler);
32
+
33
+ $('.backtrace').click(function() {
34
+ $(this).next().toggle()
35
+ return false
36
+ })
37
+
38
+ $('a[rel=poll]').click(function() {
39
+ var href = $(this).attr('href')
40
+ $(this).parent().text('Starting...')
41
+ $("#main").addClass('polling')
42
+
43
+ setInterval(function() {
44
+ $.ajax({dataType: 'text', type: 'get', url: href,
45
+ success: function(data) {
46
+ $('#main').html(data)
47
+ $('#main .time').relatizeDate()
48
+ },
49
+ error: function(data) {
50
+ if (data.status == '401') { window.location.href = '/' }
51
+ }
52
+ })
53
+ }, poll_interval * 1000)
54
+
55
+ return false
56
+ })
57
+
58
+ $('ul.failed li').hover(function() {
59
+ $(this).addClass('hover');
60
+ }, function() {
61
+ $(this).removeClass('hover');
62
+ })
63
+
64
+ $('ul.failed a[rel=retry]').click(function() {
65
+ var href = $(this).attr('href');
66
+ $(this).text('Retrying...');
67
+ var parent = $(this).parent();
68
+ $.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
69
+ parent.html('Retried <b><span class="time">' + data + '</span></b>');
70
+ relatizer.apply($('.time', parent));
71
+ $('.date_time', parent).hide();
72
+ $('a.toggle_format span', parent).click(format_toggler);
73
+ }});
74
+ return false;
75
+ })
76
+
77
+
78
+ })
@@ -0,0 +1,44 @@
1
+ html, body, div, span, applet, object, iframe,
2
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre,
3
+ a, abbr, acronym, address, big, cite, code,
4
+ del, dfn, em, font, img, ins, kbd, q, s, samp,
5
+ small, strike, strong, sub, sup, tt, var,
6
+ dl, dt, dd, ul, li,
7
+ form, label, legend,
8
+ table, caption, tbody, tfoot, thead, tr, th, td {
9
+ margin: 0;
10
+ padding: 0;
11
+ border: 0;
12
+ outline: 0;
13
+ font-weight: inherit;
14
+ font-style: normal;
15
+ font-size: 100%;
16
+ font-family: inherit;
17
+ }
18
+
19
+ body {
20
+ line-height: 1;
21
+ }
22
+
23
+ ul {
24
+ list-style: none;
25
+ }
26
+
27
+ table {
28
+ border-collapse: collapse;
29
+ border-spacing: 0;
30
+ }
31
+
32
+ caption, th, td {
33
+ text-align: left;
34
+ font-weight: normal;
35
+ }
36
+
37
+ blockquote:before, blockquote:after,
38
+ q:before, q:after {
39
+ content: "";
40
+ }
41
+
42
+ blockquote, q {
43
+ quotes: "" "";
44
+ }
@@ -0,0 +1,91 @@
1
+ html { background:#efefef; font-family:Arial, Verdana, sans-serif; font-size:13px; }
2
+ body { padding:0; margin:0; }
3
+
4
+ .header { background:#000; padding:8px 5% 0 5%; border-bottom:1px solid #444;border-bottom:5px solid #ce1212;}
5
+ .header h1 { color:#333; font-size:90%; font-weight:bold; margin-bottom:6px;}
6
+ .header ul li { display:inline;}
7
+ .header ul li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; padding:8px; -webkit-border-top-right-radius:6px; -webkit-border-top-left-radius:6px; -moz-border-radius-topleft:6px; -moz-border-radius-topright:6px; }
8
+ .header ul li a:hover { background:#333;}
9
+ .header ul li.current a { background:#ce1212; font-weight:bold; color:#fff;}
10
+
11
+ .header .namespace { position: absolute; right: 75px; top: 10px; color: #7A7A7A; }
12
+
13
+ .subnav { padding:2px 5% 7px 5%; background:#ce1212; font-size:90%;}
14
+ .subnav li { display:inline;}
15
+ .subnav li a { color:#fff; text-decoration:none; margin-right:10px; display:inline-block; background:#dd5b5b; padding:5px; -webkit-border-radius:3px; -moz-border-radius:3px;}
16
+ .subnav li.current a { background:#fff; font-weight:bold; color:#ce1212;}
17
+ .subnav li a:active { background:#b00909;}
18
+
19
+ #main { padding:10px 5%; background:#fff; overflow:hidden; }
20
+ #main .logo { float:right; margin:10px;}
21
+ #main span.hl { background:#efefef; padding:2px;}
22
+ #main h1 { margin:10px 0; font-size:190%; font-weight:bold; color:#ce1212;}
23
+ #main h2 { margin:10px 0; font-size:130%;}
24
+ #main table { width:100%; margin:10px 0;}
25
+ #main table tr td, #main table tr th { border:1px solid #ccc; padding:6px;}
26
+ #main table tr th { background:#efefef; color:#888; font-size:15px; font-weight:bold;}
27
+ #main table tr td.no-data { text-align:center; padding:40px 0; color:#999; font-style:italic; font-size:130%;}
28
+ #main a { color:#111;}
29
+ #main p { margin:5px 0;}
30
+ #main p.intro { margin-bottom:15px; font-size:85%; color:#999; margin-top:0; line-height:1.3;}
31
+ #main h1.wi { margin-bottom:5px;}
32
+ #main p.sub { font-size:95%; color:#999;}
33
+
34
+ #main table.queues { width:40%;}
35
+ #main table.queues td.queue { font-weight:bold; width:50%;}
36
+ #main table.queues tr.failure td { background:#ffecec; font-size:90%; color:#d37474;}
37
+ #main table.queues tr.failure td a{ color:#d37474;}
38
+ #main table.queues tr.first_failure td { border-top:2px solid #d37474; }
39
+
40
+ #main table.jobs td.class { font-family:Monaco, "Courier New", monospace; font-size:90%; width:50%;}
41
+ #main table.jobs td.args{ width:50%;}
42
+
43
+ #main table.workers td.icon {width:1%; background:#efefef;text-align:center;}
44
+ #main table.workers td.icon img { height: 16px; width: 16px; }
45
+ #main table.workers td.where { width:25%;}
46
+ #main table.workers td.queues { width:35%;}
47
+ #main .queue-tag { background:#b1d2e9; padding:2px; margin:0 3px; font-size:80%; text-decoration:none; text-transform:uppercase; font-weight:bold; color:#3274a2; -webkit-border-radius:4px; -moz-border-radius:4px;}
48
+ #main .queue-tag a { color: #3274A2; text-decoration: none; }
49
+ #main table.workers td.queues.queue { width:10%;}
50
+ #main table.workers td.process { width:35%;}
51
+ #main table.workers td.process span.waiting { color:#999; font-size:90%;}
52
+ #main table.workers td.process small { font-size:80%; margin-left:5px;}
53
+ #main table.workers td.process code { font-family:Monaco, "Courier New", monospace; font-size:90%;}
54
+ #main table.workers td.process small a { color:#999;}
55
+ #main.polling table.workers tr.working td { background:#f4ffe4; color:#7ac312;}
56
+ #main.polling table.workers tr.working td.where a { color:#7ac312;}
57
+ #main.polling table.workers tr.working td.process code { font-weight:bold;}
58
+
59
+
60
+ #main table.stats th { font-size:100%; width:40%; color:#000;}
61
+ #main hr { border:0; border-top:5px solid #efefef; margin:15px 0;}
62
+
63
+ #footer { padding:10px 5%; background:#efefef; color:#999; font-size:85%; line-height:1.5; border-top:5px solid #ccc; padding-top:10px;}
64
+ #footer p a { color:#999;}
65
+
66
+ #main p.poll { background:url(poll.png) no-repeat 0 2px; padding:3px 0; padding-left:23px; float:right; font-size:85%; }
67
+
68
+ #main ul.failed {}
69
+ #main ul.failed li {background:-webkit-gradient(linear, left top, left bottom, from(#efefef), to(#fff)) #efefef; margin-top:10px; padding:10px; overflow:hidden; -webkit-border-radius:5px; border:1px solid #ccc; }
70
+ #main ul.failed li dl dt {font-size:80%; color:#999; width:60px; float:left; padding-top:1px; text-align:right;}
71
+ #main ul.failed li dl dd {margin-bottom:10px; margin-left:70px;}
72
+ #main ul.failed li dl dd .retried { float:right; text-align: right; }
73
+ #main ul.failed li dl dd .retried .remove { display:none; margin-top: 8px; }
74
+ #main ul.failed li.hover dl dd .retried .remove { display:block; }
75
+ #main ul.failed li dl dd .controls { display:none; float:right; }
76
+ #main ul.failed li.hover dl dd .controls { display:block; }
77
+ #main ul.failed li dl dd code, #main ul.failed li dl dd pre { font-family:Monaco, "Courier New", monospace; font-size:90%; white-space: pre-wrap;}
78
+ #main ul.failed li dl dd.error a {font-family:Monaco, "Courier New", monospace; font-size:90%; }
79
+ #main ul.failed li dl dd.error pre { margin-top:3px; line-height:1.3;}
80
+
81
+ #main p.pagination { background:#efefef; padding:10px; overflow:hidden; text-align:center}
82
+ #main p.pagination a.less { float:left;}
83
+ #main p.pagination a.more { float:right;}
84
+
85
+ #main form {float:right; margin-top:-10px;margin-left:10px;}
86
+
87
+ #main .time a.toggle_format {text-decoration:none;}
88
+
89
+ #failed tr.total td {background-color: #FFECEC; color: #D37474; font-size: 15px; font-weight: bold;}
90
+ #failed .center {text-align: center;}
91
+ #failed .failed_class { padding-left: 20px; font-size:12px; }
@@ -0,0 +1,19 @@
1
+ require 'rack/test'
2
+ require 'resque/server'
3
+
4
+ module Resque
5
+ module TestHelper
6
+ class Test::Unit::TestCase
7
+ include Rack::Test::Methods
8
+ def app
9
+ Resque::Server.new
10
+ end
11
+
12
+ def self.should_respond_with_success
13
+ test "should respond with success" do
14
+ assert last_response.ok?, last_response.errors
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1 @@
1
+ <h1 style="font-size:110%;font-family:Arial, sans-serif;"><%= error %></h1>
@@ -0,0 +1,29 @@
1
+ <% if failed_multiple_queues? && !params[:queue] %>
2
+ <h1>All Failed Queues: <%= Resque::Failure.queues.size %> total</h1>
3
+ <% else %>
4
+ <h1>Failed Jobs <%= "on '#{params[:queue]}'" if params[:queue] %> <%= "with class '#{params[:class]}'" if params[:class] %></h1>
5
+ <% end %>
6
+
7
+ <% unless failed_size.zero? %>
8
+ <form method="POST" action="<%= u "failed#{'/' + params[:queue] if params[:queue]}/clear" %>">
9
+ <input type="submit" name="" value="Clear <%= params[:queue] ? "'#{params[:queue]}'" : 'Failed' %> Jobs" />
10
+ </form>
11
+ <form method="POST" action="<%= u "failed#{'/' + params[:queue] if params[:queue]}/requeue/all" %>">
12
+ <input type="submit" name="" value="Retry <%= params[:queue] ? "'#{params[:queue]}'" : 'Failed' %> Jobs" />
13
+ </form>
14
+ <% end %>
15
+
16
+ <% if failed_multiple_queues? && !params[:queue] %>
17
+ <%= partial :failed_queues_overview %>
18
+ <% else %>
19
+ <p class='sub'>Showing <%= failed_start_at %> to <%= failed_end_at %> of <b><%= failed_size %></b> jobs</p>
20
+
21
+
22
+ <ul class='failed'>
23
+ <% Resque::Failure.each(failed_start_at, failed_per_page, params[:queue], params[:class], failed_order) do |id, job| %>
24
+ <%= partial :failed_job, :id => id, :job => job %>
25
+ <% end %>
26
+ </ul>
27
+
28
+ <%= partial :next_more, :start => failed_start_at, :size => failed_size, :per_page => failed_per_page %>
29
+ <% end %>
@@ -0,0 +1,50 @@
1
+ <li>
2
+ <dl>
3
+ <% if job.nil? %>
4
+ <dt>Error</dt>
5
+ <dd>Job <%= id %> could not be parsed; perhaps it contains invalid JSON?</dd>
6
+ <% else %>
7
+ <dt>Worker</dt>
8
+ <dd>
9
+ <a href="<%= u(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= Time.parse(job['failed_at']).strftime(failed_date_format) %></span></b>
10
+ <% if job['retried_at'] %>
11
+ <div class='retried'>
12
+ Retried <b><span class="time"><%= Time.parse(job['retried_at']).strftime(failed_date_format) %></span></b>
13
+ <a href="<%= u "failed/remove/#{id}" %>" class="remove" rel="remove">Remove</a>
14
+ </div>
15
+ <% else %>
16
+ <div class='controls'>
17
+ <a href="<%= u "failed/requeue/#{id}" %>" rel="retry">Retry</a>
18
+ or
19
+ <a href="<%= u "failed/remove/#{id}" %>" rel="remove">Remove</a>
20
+ </div>
21
+ <% end %>
22
+ </dd>
23
+ <dt>Class</dt>
24
+ <dd>
25
+ <% if job['payload'] && job['payload']['class'] %>
26
+ <a href="<%= u "failed/#{params[:queue]}?class=#{job['payload']['class']}" %>">
27
+ <code><%= job['payload']['class'] %></code>
28
+ </a>
29
+ <% else %>
30
+ <code>nil</code>
31
+ <% end %>
32
+ </dd>
33
+ <dt>Arguments</dt>
34
+ <dd><pre><%=h job['payload'] ? show_args(job['payload']['args']) : 'nil' %></pre></dd>
35
+ <dt>Exception</dt>
36
+ <dd><code><%= job['exception'] %></code></dd>
37
+ <dt>Error</dt>
38
+ <dd class='error'>
39
+ <% if job['backtrace'] %>
40
+ <a href="#" class="backtrace"><%= h(job['error']) %></a>
41
+ <pre style='display:none'><%=h job['backtrace'].join("\n") %></pre>
42
+ <% else %>
43
+ <%=h job['error'] %>
44
+ <% end %>
45
+ </dd>
46
+ <% end %>
47
+ </dl>
48
+ <div class='r'>
49
+ </div>
50
+ </li>
@@ -0,0 +1,24 @@
1
+ <table id="failed">
2
+ <tbody>
3
+ <tr class="total">
4
+ <td class='queue'>Total Failed</td>
5
+ <td class='center'><%= Resque::Failure.count %></td>
6
+ </tr>
7
+
8
+ <% Resque::Failure.queues.sort.each do |queue| %>
9
+ <tr>
10
+ <th><b class="queue-tag"><a href="/failed/<%= queue %>"><%= queue %></a></b></th>
11
+ <th style="width:75px;" class="center"><%= Resque::Failure.count(queue) %></th>
12
+ </tr>
13
+
14
+ <% failed_class_counts(queue).sort_by { |name,_| name }.each do |k, v| %>
15
+ <tr id="<%= k %>">
16
+ <td>
17
+ <a href="/failed/<%= "#{queue}?class=#{k}" %>"><span class="failed failed_class"><%= k %></span></a>
18
+ </td>
19
+ <td style="text-align: center;" class="failed<%= (v.to_i > 1000) ? '_many' : '' %>"><%= v %></td>
20
+ </tr>
21
+ <% end %>
22
+ <% end %>
23
+ </tbody>
24
+ </table>