resque-cleaner 0.1.1 → 0.2.0

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.
data/CHANGELOG.md CHANGED
@@ -1,7 +1,11 @@
1
+ ## 0.2.0 (2011-04-06)
2
+
3
+ * Extended with resque-web
4
+
1
5
  ## 0.1.1 (2010-12-30)
2
6
 
3
- * Fix for ruby 1.9.2
4
- * Fix a bug on #retried? method
7
+ * Fixed for ruby 1.9.2
8
+ * Fixed a bug on #retried? method
5
9
 
6
10
  ## 0.1.0 (2010-11-24)
7
11
 
data/README.markdown CHANGED
@@ -8,16 +8,13 @@ Description
8
8
  -----------
9
9
 
10
10
  ResqueCleaner is a [Resque](https://github.com/defunkt/resque) plugin which
11
- helps you to deal with failed jobs on Resque by:
11
+ aims to help you to clean up failed jobs on Resque by:
12
12
 
13
13
  * Showing stats of failed jobs
14
14
  * Retrying failed jobs
15
15
  * Removing failed jobs
16
16
  * Filtering failed jobs
17
17
 
18
- Although ResqueCleaner has not integrated with Resque's web-based interface yet,
19
- it is pretty easy to use on irb(console).
20
-
21
18
 
22
19
  Installation
23
20
  ------------
@@ -27,8 +24,22 @@ Install as a gem:
27
24
  $ gem install resque-cleaner
28
25
 
29
26
 
30
- Usage
31
- -----
27
+ Resque-Web integration
28
+ ----------------------
29
+
30
+ ![Screen 1](https://github.com/ono/resque-cleaner/raw/master/misc/resque-cleaner-main.png)
31
+ ![Screen 2](https://github.com/ono/resque-cleaner/raw/master/misc/resque-cleaner-list.png)
32
+
33
+ You have to load ResqueCleaner to enable the Cleaner tab.
34
+
35
+ require 'resque-cleaner'
36
+
37
+ Console
38
+ -------
39
+
40
+ Hopefully a situation of your failed jobs is simple enough to get figured out through
41
+ the web interface. But, if not, a powerful filtering feature of ResqueCleaner may help
42
+ you to understand what is going on with your console(irb).
32
43
 
33
44
  **Create Instance**
34
45
 
@@ -234,11 +245,4 @@ application; it should be quick even if there are huge number of failed jobs.
234
245
  > cleaner.limiter.on?
235
246
  => false
236
247
 
237
- TODO
238
- ----
239
-
240
- * Integration with Resque's sinatra based front end.
241
- * More stats.
242
-
243
- Any suggestion or idea are welcomed.
244
248
 
@@ -0,0 +1,61 @@
1
+
2
+ #main .cleaner .title h1 { float: left }
3
+
4
+ #main .cleaner .sub_header {color: #888; font-size: 80%; font-weight: bold}
5
+
6
+ #main .cleaner .control_panel {
7
+ margin: 5px 0 10px; padding: 5px; background: #eee; display: inline-block;
8
+ -webkit-border-radius:5px; border:1px solid #ccc;
9
+ }
10
+
11
+ #main .cleaner #exec { color: black; }
12
+ #main .cleaner #exec select { margin-right: 20px }
13
+ #main .cleaner #exec a { margin-right: 5px}
14
+ #main .cleaner #exec a.disabled { color: #aaa}
15
+
16
+ #main .cleaner table.class_list { width: auto }
17
+ #main .cleaner td.number { text-align: right }
18
+ #main .cleaner tr.total { border-top: 3px double #ccc}
19
+
20
+ #main .cleaner form { float: none; margin: 0}
21
+
22
+ #main .cleaner .warning { margin: 30px 0; padding: 10px; background: #fdd; display: inline-block; }
23
+
24
+ #main .clearfix:after {
25
+ content: ".";
26
+ display: block;
27
+ height: 0;
28
+ clear: both;
29
+ visibility: hidden;
30
+ }
31
+ #main .cleaner ul.failed {
32
+ margin-bottom: 10px
33
+ }
34
+
35
+ #main .cleaner .list_info{
36
+ color: #999; text-align: center;
37
+ }
38
+ #main .cleaner .list_info a{
39
+ color: #666
40
+ }
41
+ #main .cleaner .list_info .list_summary{
42
+ vertical-align: baseline;
43
+ display: inline-block;
44
+ margin: 0; padding:0;
45
+ }
46
+ #main .cleaner ul.pagination {
47
+ display: inline-block;
48
+ vertical-align: baseline;
49
+ margin:0; padding:0;
50
+ }
51
+ #main .cleaner ul.pagination li {
52
+ list-style:none; float: left; padding: 0 3px;
53
+ }
54
+ #main .cleaner ul.pagination li.current {
55
+ color: #333; font-weight: bold;
56
+ }
57
+ #main .cleaner ul.pagination li.summary{
58
+ padding-right: 10px
59
+ }
60
+
61
+
@@ -0,0 +1,13 @@
1
+ <div class="warning">
2
+ <p>
3
+ There are more than <%= @cleaner.limiter.maximum%> jobs. ResqueCleaner handles only recent <%= @cleaner.limiter.maximum %> jobs. See the Limiter section on <a href="https://github.com/ono/resque-cleaner" target="_blank">README</a> for more detail.
4
+ </p>
5
+ <p>
6
+ <form method="post" action="cleaner_stale">
7
+ <input type="hidden" name="action" value="clear_stale" />
8
+ <input type="submit" onclick="return confirm('Are you sure?')" value="Clear all jobs older than <%= @cleaner.limiter.count %>th job."/>
9
+ (<%= @cleaner.failure.count - @cleaner.limiter.maximum %> jobs will be cleared)
10
+ </form>
11
+ </p>
12
+ </div>
13
+
@@ -0,0 +1,50 @@
1
+ <div class="list_info clearfix">
2
+
3
+ <ul class="pagination">
4
+ <li class="summary">
5
+ <%= @paginate.first_index%> - <%= @paginate.last_index%> / <%= @paginate.total_size%>
6
+ </li>
7
+ <% if @paginate.max_page>1 %>
8
+ <% if @paginate.first_page? %>
9
+ <li class="prev off">&lt;&lt;newer</li>
10
+ <% else %>
11
+ <li class="prev"><a href="<%= @paginate.page_url(:prev)%>">&lt;&lt;newer</a></li>
12
+ <% end %>
13
+
14
+ <% 1.upto(6<@paginate.max_page ? 6 : @paginate.max_page) do |pg| %>
15
+ <% if @paginate.page==pg %>
16
+ <li class="current"><%=pg%></li>
17
+ <% else %>
18
+ <li><a href="<%= @paginate.page_url(pg)%>"><%=pg%></a></li>
19
+ <% end %>
20
+ <% end %>
21
+
22
+ <% if @paginate.max_page > 6 %>
23
+ <% if @paginate.max_page!=@paginate.page %>
24
+ <% if @paginate.page>7 %>
25
+ <li class="off">...</li>
26
+ <% end %>
27
+ <% if @paginate.page>6 %>
28
+ <li class="current"><%=@paginate.page%></li>
29
+ <% end %>
30
+ <% end %>
31
+ <% if @paginate.max_page>7 && @paginate.page+1!=@paginate.max_page %>
32
+ <li class="off">...</li>
33
+ <% end %>
34
+
35
+ <% if @paginate.max_page==@paginate.page %>
36
+ <li class="current"><%=@paginate.max_page%></li>
37
+ <% else %>
38
+ <li><a href="<%= @paginate.page_url(@paginate.max_page)%>"><%=@paginate.max_page%></a></li>
39
+ <% end %>
40
+ <% end %>
41
+
42
+ <% if @paginate.last_page? %>
43
+ <li class="next off">older&gt;&gt;</li>
44
+ <% else %>
45
+ <li class="next"><a href="<%= @paginate.page_url(:next)%>">older&gt;&gt;</a></li>
46
+ <% end %>
47
+ <% end %>
48
+ </ul>
49
+ </div>
50
+
@@ -0,0 +1,43 @@
1
+ <link href="/cleaner/public/cleaner.css" media="screen" rel="stylesheet" type="text/css">
2
+
3
+ <div class="cleaner">
4
+ <div class="title clearfix">
5
+ <h1>Job Classes Failed</h1>
6
+ </div>
7
+
8
+ <table class="class_list">
9
+ <tr>
10
+ <th>Class</th>
11
+ <th>Failed</th>
12
+ <th>In last 1 hour</th>
13
+ <th>In last 3 hours</th>
14
+ <th>In last 24 hours</th>
15
+ <th>In last 3 days</th>
16
+ <th>In last 7 days</th>
17
+ </tr>
18
+ <% @stats.each do |klass,count| %>
19
+ <tr>
20
+ <td><%= klass %></td>
21
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>"><%= count["total"] %></a></td>
22
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>&f=1"><%= count["1h"] %></a></td>
23
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>&f=3"><%= count["3h"] %></a></td>
24
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>&f=24"><%= count["1d"] %></a></td>
25
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>&f=72"><%= count["3d"] %></a></td>
26
+ <td class="number"><a href="/cleaner_list?c=<%=klass%>&f=168"><%= count["7d"] %></a></td>
27
+ </tr>
28
+ <% end %>
29
+ <tr class="total">
30
+ <td>Total</td>
31
+ <td class="number"><a href="/cleaner_list"><%= @total["total"] %></a></td>
32
+ <td class="number"><a href="/cleaner_list?f=1"><%= @total["1h"] %></a></td>
33
+ <td class="number"><a href="/cleaner_list?f=3"><%= @total["3h"] %></a></td>
34
+ <td class="number"><a href="/cleaner_list?f=24"><%= @total["1d"] %></a></td>
35
+ <td class="number"><a href="/cleaner_list?f=72"><%= @total["3d"] %></a></td>
36
+ <td class="number"><a href="/cleaner_list?f=168"><%= @total["7d"] %></a></td>
37
+ </tr>
38
+ </table>
39
+
40
+ <% if @cleaner.limiter.on? %>
41
+ <%= erb File.read(ResqueCleaner::Server.erb_path("_limiter.erb")) %>
42
+ <% end %>
43
+ </div>
@@ -0,0 +1,8 @@
1
+ <link href="/cleaner/public/cleaner.css" media="screen" rel="stylesheet" type="text/css">
2
+
3
+ <div class="cleaner">
4
+ <p class="message"><%= @msg %></p>
5
+ <p class="back_to_list">
6
+ <a href="<%=@url%>">Back to List</a>
7
+ </p>
8
+ </div>
@@ -0,0 +1,169 @@
1
+ <link href="/cleaner/public/cleaner.css" media="screen" rel="stylesheet" type="text/css">
2
+
3
+ <!-- Many code was copied from failed.erb of the original resque -->
4
+ <div class="cleaner">
5
+ <div class="title clearfix">
6
+ <h1>Failed Jobs</h1>
7
+ </div>
8
+
9
+ <div class="clearfix">
10
+ <div class="control_panel sub_header">
11
+ <form method="get">
12
+ <span class="class_filter">
13
+ Class: <%= class_filter("filter_class","c",@klasses,@klass)%>
14
+ </span>
15
+ <span class="time_filter">
16
+ From: <%= time_filter("filter_from","f",@from)%>
17
+ </span>
18
+ <span class="time_filter">
19
+ To: <%= time_filter("filter_to","t",@to)%>
20
+ </span>
21
+ <input type="submit" value="Filter" />
22
+ </form>
23
+ </div>
24
+ </div>
25
+
26
+ <% if @count > 0 %>
27
+ <div class="clearfix">
28
+ <div class="control_panel sub_header">
29
+ <form method="post" id="exec" action="/cleaner_exec">
30
+ <input type="hidden" name="c" value="<%=@klass%>" />
31
+ <input type="hidden" name="f" value="<%=@from%>" />
32
+ <input type="hidden" name="t" value="<%=@to%>" />
33
+ <input type="hidden" name="p" value="<%=@paginate.page%>" />
34
+ <select id="form_action" name="action">
35
+ <option id="default_option" value="" selected="selected">-- Select Action --</option>
36
+ <option value="clear">Clear</option>
37
+ <option value="retry_and_clear">Retry and Clear</option>
38
+ <option value="retry">Retry</option>
39
+ </select>
40
+ <a href="#" id="select_all">select all</a>
41
+ <a href="#" id="reset_all">reset</a>
42
+
43
+ <% if @paginate.max_page > 1 %>
44
+ <input type="checkbox" name="select_all_pages" value="1" id="select_all_pages" />Select all <%=@count%> jobs
45
+ <% end %>
46
+ <input type="hidden" name="sha1" id="sha1_list" />
47
+
48
+ </form>
49
+ </div>
50
+ </div>
51
+ <% end %>
52
+
53
+ <% start = 0 %>
54
+ <% failed = @paginate.paginated_jobs%>
55
+ <% index = 0 %>
56
+
57
+ <% if @paginate.max_page > 0 %>
58
+ <%= erb File.read(ResqueCleaner::Server.erb_path("_paginate.erb")) %>
59
+ <ul class='failed'>
60
+ <%for job in failed%>
61
+ <% index += 1 %>
62
+ <li>
63
+ <dl>
64
+ <% if job.nil? %>
65
+ <dt>Error</dt>
66
+ <dd>Job <%= index%> could not be parsed; perhaps it contains invalid JSON?</dd>
67
+ <% else %>
68
+ <dt>
69
+ <input type="checkbox" id="<%=Digest::SHA1.hexdigest job.to_json %>" />
70
+ </dt>
71
+ <dd>&nbsp;</dd>
72
+ <dt>Worker</dt>
73
+ <dd>
74
+ <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"><%= job['failed_at'] %></span></b>
75
+ <% if job['retried_at'] %>
76
+ <div class='retried'>
77
+ Retried <b><span class="time"><%= job['retried_at'] %></span></b>
78
+ </div>
79
+ <% end %>
80
+ </dd>
81
+ <dt>Class</dt>
82
+ <dd><code><%= job['payload'] ? job['payload']['class'] : 'nil' %></code></dd>
83
+ <dt>Arguments</dt>
84
+ <dd><pre><%=h job['payload'] ? show_args(job['payload']['args']) : 'nil' %></pre></dd>
85
+ <dt>Exception</dt>
86
+ <dd><code><%= job['exception'] %></code></dd>
87
+ <dt>Error</dt>
88
+ <dd class='error'>
89
+ <% if job['backtrace'] %>
90
+ <a href="#" class="backtrace"><%= h(job['error']) %></a>
91
+ <pre style='display:none'><%=h job['backtrace'].join("\n") %></pre>
92
+ <% else %>
93
+ <%=h job['error'] %>
94
+ <% end %>
95
+ </dd>
96
+ <% end %>
97
+ </dl>
98
+ <div class='r'>
99
+ </div>
100
+ </li>
101
+ <%end%>
102
+ </ul>
103
+ <%= erb File.read(ResqueCleaner::Server.erb_path("_paginate.erb")) %>
104
+ <% else %>
105
+ Clean!
106
+ <% end %>
107
+ </div>
108
+
109
+ <script>
110
+ $(document).ready(function(){
111
+ $('#select_all_pages').click(function() {
112
+ updateCheckboxStaus();
113
+ });
114
+
115
+ $('#select_all').click(function() {
116
+ if (!$(this).hasClass('disabled')) {
117
+ $('.failed input').attr('checked','checked');
118
+ }
119
+ return false;
120
+ });
121
+ $('#reset_all').click(function() {
122
+ if (!$(this).hasClass('disabled')) {
123
+ $('.failed input').removeAttr('checked');
124
+ }
125
+ return false;
126
+ });
127
+
128
+ $('#form_action').change( function() {
129
+ if ($('#form_action option:selected').val()=='') return;
130
+
131
+ if ($('#select_all_pages:checked, .failed input:checked').length==0) {
132
+ alert('Please select jobs.');
133
+ $('#default_option').attr('selected','selected');
134
+ return false;
135
+ }
136
+
137
+ if (!confirm('Do you really want to proceed?')) {
138
+ $('#default_option').attr('selected','selected');
139
+ return false;
140
+ }
141
+
142
+ if ($('#select_all_pages:checked').length==0) {
143
+ setSha1();
144
+ }
145
+ $('#exec').submit();
146
+ });
147
+ });
148
+
149
+ function setSha1() {
150
+ var sha1 = "";
151
+ $('.failed input:checked').each( function() {
152
+ if (sha1.length>0) sha1 += ",";
153
+ sha1 += $(this).attr("id");
154
+ });
155
+
156
+ $('#sha1_list').val(sha1);
157
+ }
158
+
159
+ function updateCheckboxStaus() {
160
+ if ($('#select_all_pages:checked').length==1) {
161
+ $('#exec a').addClass('disabled');
162
+ $('.failed input').attr('disabled','disabled');
163
+ } else {
164
+ $('#exec a').removeClass('disabled');
165
+ $('.failed input').removeAttr('disabled');
166
+ }
167
+ };
168
+
169
+ </script>
@@ -0,0 +1,205 @@
1
+ # Extends Resque Web Based UI.
2
+ # Structure has been borrowed from ResqueScheduler.
3
+ module ResqueCleaner
4
+ module Server
5
+
6
+ begin
7
+ require 'yajl/json_gem'
8
+ rescue Exception
9
+ require 'json'
10
+ end
11
+
12
+ def self.erb_path(filename)
13
+ File.join(File.dirname(__FILE__), 'server', 'views', filename)
14
+ end
15
+ def self.public_path(filename)
16
+ File.join(File.dirname(__FILE__), 'server', 'public', filename)
17
+ end
18
+
19
+ # Pagination helpr for list page.
20
+ class Paginate
21
+ attr_accessor :page_size, :page, :jobs, :url
22
+ def initialize(jobs, url, page=1, page_size=20)
23
+ @jobs = jobs
24
+ @url = url
25
+ @page = (!page || page < 1) ? 1 : page
26
+ @page_size = 20
27
+ end
28
+
29
+ def first_index
30
+ @page_size * (@page-1)
31
+ end
32
+
33
+ def last_index
34
+ last = first_index + @page_size - 1
35
+ last > @jobs.size-1 ? @jobs.size-1 : last
36
+ end
37
+
38
+ def paginated_jobs
39
+ @jobs[first_index,@page_size]
40
+ end
41
+
42
+ def first_page?
43
+ @page <= 1
44
+ end
45
+
46
+ def last_page?
47
+ @page >= max_page
48
+ end
49
+
50
+ def page_url(page)
51
+ u = @url
52
+ u += @url.include?("?") ? "&" : "?"
53
+ if page.is_a?(Symbol)
54
+ page = @page - 1 if page==:prev
55
+ page = @page + 1 if page==:next
56
+ end
57
+ u += "p=#{page}"
58
+ end
59
+
60
+ def total_size
61
+ @jobs.size
62
+ end
63
+
64
+ def max_page
65
+ ((total_size-1) / @page_size) + 1
66
+ end
67
+ end
68
+
69
+ def self.included(base)
70
+ require 'digest/sha1'
71
+ base.class_eval do
72
+ helpers do
73
+ def time_filter(id, name, value)
74
+ html = "<select id=\"#{id}\" name=\"#{name}\">"
75
+ html += "<option value=\"\">-</option>"
76
+ [1, 3, 6, 12, 24].each do |h|
77
+ selected = h.to_s == value ? 'selected="selected"' : ''
78
+ html += "<option #{selected} value=\"#{h}\">#{h} #{h==1 ? "hour" : "hours"} ago</option>"
79
+ end
80
+ [3, 7, 14, 28].each do |d|
81
+ selected = (d*24).to_s == value ? 'selected="selected"' : ''
82
+ html += "<option #{selected} value=\"#{d*24}\">#{d} days ago</option>"
83
+ end
84
+ html += "</select>"
85
+ end
86
+
87
+ def class_filter(id, name, klasses, value)
88
+ html = "<select id=\"#{id}\" name=\"#{name}\">"
89
+ html += "<option value=\"\">-</option>"
90
+ klasses.each do |k|
91
+ selected = k == value ? 'selected="selected"' : ''
92
+ html += "<option #{selected} value=\"#{k}\">#{k}</option>"
93
+ end
94
+ html += "</select>"
95
+ end
96
+ end
97
+
98
+ get "/cleaner" do
99
+ load_cleaner_filter
100
+
101
+ @jobs = cleaner.select
102
+ @stats, @total = {}, {"total" => 0, "1h" => 0, "3h" => 0, "1d" => 0, "3d" => 0, "7d" => 0}
103
+ @jobs.each do |job|
104
+ klass = job["payload"]["class"]
105
+ failed_at = Time.parse job["failed_at"]
106
+
107
+ @stats[klass] ||= {"total" => 0, "1h" => 0, "3h" => 0, "1d" => 0, "3d" => 0, "7d" => 0}
108
+ items = [@stats[klass],@total]
109
+
110
+ items.each{|a| a["total"] += 1}
111
+ items.each{|a| a["1h"] += 1} if failed_at >= hours_ago(1)
112
+ items.each{|a| a["3h"] += 1} if failed_at >= hours_ago(3)
113
+ items.each{|a| a["1d"] += 1} if failed_at >= hours_ago(24)
114
+ items.each{|a| a["3d"] += 1} if failed_at >= hours_ago(24*3)
115
+ items.each{|a| a["7d"] += 1} if failed_at >= hours_ago(24*7)
116
+ end
117
+
118
+ erb File.read(ResqueCleaner::Server.erb_path('cleaner.erb'))
119
+ end
120
+
121
+ get "/cleaner_list" do
122
+ load_cleaner_filter
123
+
124
+ block = lambda{|j|
125
+ (!@from || j.after?(hours_ago(@from))) &&
126
+ (!@to || j.before?(hours_ago(@to))) &&
127
+ (!@klass || j.klass?(@klass))
128
+ }
129
+
130
+ @failed = cleaner.select(&block).reverse
131
+
132
+ url = "cleaner_list?c=#{@klass}&f=#{@from}&t=#{@to}"
133
+ @paginate = Paginate.new(@failed, url, params[:p].to_i)
134
+
135
+ @klasses = cleaner.stats_by_class.keys
136
+ @count = cleaner.select(&block).size
137
+
138
+ erb File.read(ResqueCleaner::Server.erb_path('cleaner_list.erb'))
139
+ end
140
+
141
+ post "/cleaner_exec" do
142
+ load_cleaner_filter
143
+
144
+ @sha1 = nil
145
+ if params[:select_all_pages]!="1"
146
+ @sha1 = {}
147
+ params[:sha1].split(",").each do |s|
148
+ @sha1[s] = true
149
+ end
150
+ end
151
+
152
+ block = lambda{|j|
153
+ (!@from || j.after?(hours_ago(@from))) &&
154
+ (!@to || j.before?(hours_ago(@to))) &&
155
+ (!@klass || j.klass?(@klass))
156
+ (!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)])
157
+ }
158
+
159
+ count =
160
+ case params[:action]
161
+ when "clear" then cleaner.clear(&block)
162
+ when "retry_and_clear" then cleaner.requeue(true,&block)
163
+ when "retry" then cleaner.requeue(false,{},&block)
164
+ end
165
+
166
+ @msg = "processed #{count} jobs."
167
+ @url = "cleaner_list?c=#{@klass}&f=#{@from}&t=#{@to}"
168
+ erb File.read(ResqueCleaner::Server.erb_path('cleaner_exec.erb'))
169
+ end
170
+
171
+ post "/cleaner_stale" do
172
+ cleaner.clear_stale
173
+ redirect "/cleaner"
174
+ end
175
+
176
+ get /cleaner\/public\/([a-z]+\.[a-z]+)/ do
177
+ send_file ResqueCleaner::Server.public_path(params[:captures].first)
178
+ end
179
+ end
180
+
181
+ end
182
+
183
+ def cleaner
184
+ @cleaner ||= Resque::Plugins::ResqueCleaner.new
185
+ @cleaner.print_message = false
186
+ @cleaner
187
+ end
188
+
189
+ def load_cleaner_filter
190
+ @from = params[:f]=="" ? nil : params[:f]
191
+ @to = params[:t]=="" ? nil : params[:t]
192
+ @klass = params[:c]=="" ? nil : params[:c]
193
+ end
194
+
195
+ def hours_ago(h)
196
+ Time.now - h.to_i*60*60
197
+ end
198
+ Resque::Server.tabs << 'Cleaner'
199
+ end
200
+ end
201
+
202
+ Resque::Server.class_eval do
203
+ include ResqueCleaner::Server
204
+ end
205
+
@@ -1,4 +1,7 @@
1
1
  require 'time'
2
+ require 'resque'
3
+ require 'resque/server'
4
+
2
5
  module Resque
3
6
  module Plugins
4
7
  # ResqueCleaner class provides useful functionalities to retry or clean
@@ -258,5 +261,5 @@ module Resque
258
261
  end
259
262
  end
260
263
 
261
-
264
+ require 'resque_cleaner/server'
262
265
 
@@ -1,19 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
2
  require 'time'
3
3
  context "ResqueCleaner" do
4
- def create_and_process_jobs(queue,worker,num,date,job,*args)
5
- Timecop.freeze(date) do
6
- num.times do
7
- Resque::Job.create(queue, job, *args)
8
- end
9
- worker.work(0)
10
- end
11
- end
12
-
13
- def queue_size(*queues)
14
- queues.inject(0){|sum,queue| sum + Resque.size(queue).to_i}
15
- end
16
-
17
4
  setup do
18
5
  Resque.redis.flushall
19
6
 
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ # Pull in the server test_helper from resque
4
+ require 'resque/server/test_helper.rb'
5
+
6
+ def setup_some_failed_jobs
7
+ Resque.redis.flushall
8
+
9
+ @worker = Resque::Worker.new(:jobs,:jobs2)
10
+
11
+ 10.times {|i|
12
+ create_and_process_jobs :jobs, @worker, 1, Time.now, BadJob, "test_#{i}"
13
+ }
14
+
15
+ @cleaner = Resque::Plugins::ResqueCleaner.new
16
+ @cleaner.print_message = false
17
+ end
18
+
19
+ context "resque-web" do
20
+ setup do
21
+ setup_some_failed_jobs
22
+ end
23
+
24
+ test "#cleaner should respond with success" do
25
+ get "/cleaner_list"
26
+ assert last_response.ok?, last_response.errors
27
+ end
28
+
29
+ test "#cleaner_list should respond with success" do
30
+ get "/cleaner_list"
31
+ assert last_response.ok?, last_response.errors
32
+ end
33
+
34
+ test '#cleaner_list shows the failed jobs' do
35
+ get "/cleaner_list"
36
+ assert last_response.body.include?('BadJob')
37
+ end
38
+
39
+ test '#cleaner_exec clears job' do
40
+ post "/cleaner_exec", :action => "clear", :sha1 => Digest::SHA1.hexdigest(@cleaner.select[0].to_json)
41
+ assert_equal 9, @cleaner.select.size
42
+ end
43
+ end
44
+
data/test/test_helper.rb CHANGED
@@ -120,3 +120,20 @@ class BadJobWithSyntaxError
120
120
  raise SyntaxError, "Extra Bad job!"
121
121
  end
122
122
  end
123
+
124
+ #
125
+ # helper methods
126
+ #
127
+
128
+ def create_and_process_jobs(queue,worker,num,date,job,*args)
129
+ Timecop.freeze(date) do
130
+ num.times do
131
+ Resque::Job.create(queue, job, *args)
132
+ end
133
+ worker.work(0)
134
+ end
135
+ end
136
+
137
+ def queue_size(*queues)
138
+ queues.inject(0){|sum,queue| sum + Resque.size(queue).to_i}
139
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-cleaner
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Tatsuya Ono
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-12-30 00:00:00 +00:00
17
+ date: 2011-04-06 00:00:00 +01:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ~>
28
27
  - !ruby/object:Gem::Version
29
- hash: 15
30
28
  segments:
31
29
  - 1
32
30
  - 0
@@ -49,9 +47,17 @@ files:
49
47
  - Rakefile
50
48
  - LICENSE
51
49
  - lib/resque-cleaner.rb
50
+ - lib/resque_cleaner/server/public/cleaner.css
51
+ - lib/resque_cleaner/server/views/_limiter.erb
52
+ - lib/resque_cleaner/server/views/_paginate.erb
53
+ - lib/resque_cleaner/server/views/cleaner.erb
54
+ - lib/resque_cleaner/server/views/cleaner_exec.erb
55
+ - lib/resque_cleaner/server/views/cleaner_list.erb
56
+ - lib/resque_cleaner/server.rb
52
57
  - lib/resque_cleaner.rb
53
58
  - test/redis-test.conf
54
59
  - test/resque_cleaner_test.rb
60
+ - test/resque_web_test.rb
55
61
  - test/test_helper.rb
56
62
  has_rdoc: true
57
63
  homepage: http://github.com/ono/resque-cleaner
@@ -67,7 +73,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
73
  requirements:
68
74
  - - ">="
69
75
  - !ruby/object:Gem::Version
70
- hash: 3
71
76
  segments:
72
77
  - 0
73
78
  version: "0"
@@ -76,7 +81,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
81
  requirements:
77
82
  - - ">="
78
83
  - !ruby/object:Gem::Version
79
- hash: 3
80
84
  segments:
81
85
  - 0
82
86
  version: "0"