resque-cleaner 0.2.10 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CHANGELOG.md +35 -0
- data/{README.markdown → README.md} +9 -2
- data/lib/resque_cleaner/server/views/_cleaner_styles.erb +63 -0
- data/lib/resque_cleaner/server/views/_stats.erb +44 -0
- data/lib/resque_cleaner/server/views/cleaner.erb +15 -33
- data/lib/resque_cleaner/server/views/cleaner_exec.erb +2 -2
- data/lib/resque_cleaner/server/views/cleaner_list.erb +6 -2
- data/lib/resque_cleaner/server.rb +50 -29
- data/lib/resque_cleaner.rb +19 -10
- data/test/dump.rdb +0 -0
- data/test/redis-test.conf +1 -1
- data/test/resque_cleaner_test.rb +33 -21
- data/test/resque_web_test.rb +28 -12
- data/test/test_helper.rb +14 -36
- metadata +45 -18
- data/lib/resque_cleaner/server/public/cleaner.css +0 -64
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDVkZjMyNjQ1NTM5ZDEyOWEzMTNmY2QyN2ZiOWFhZTZjYmQ2ZjIzYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZDk3M2IxN2M2YTU3NDA2ZmZiOTVhMTFlNTU3NmVjOWJmYzQzNzA0Nw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjEyZWM0MmQ1MDRhY2U2ZWQ3NzI3ODJjYTBhMTkwZDU5MGEzZjY4ZDg5ZmNk
|
10
|
+
NTgyMzZjMjJlZTJmZjU4OWM2MmZkNDkyY2QyMDVmOTQyMTBhYmExOTk3NWJj
|
11
|
+
ZTc2MWJkZWFjNjg3YjliMTc4YjVmNzMyNDliNjQ4YTU1NGYyMzc=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDA5M2FkY2Q0ZmVjYmM2YTdkNjQ0YTE1MjhjMjhjMzZiOGI2YjY0NWRiN2Jh
|
14
|
+
MDRiMmU2NTdiMjA5NTgxNGEyOTQ3Mzc3OWVkYjg2NGYzZWUxN2U4YzgwOGU3
|
15
|
+
M2UyZDA5OWU0OTlhN2FlZjkzZGZhNDJjZTBlMThhNjEyM2IxZDU=
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,38 @@
|
|
1
|
+
## 0.4.1 (2018-01-25)
|
2
|
+
|
3
|
+
* Remove Requeue version lock (#46)
|
4
|
+
|
5
|
+
## 0.4.0 (2017-11-20)
|
6
|
+
|
7
|
+
* Render CSS as ERB partial to make Rails 5 compatible (#45)
|
8
|
+
|
9
|
+
## 0.3.2 (2016-03-09)
|
10
|
+
|
11
|
+
* Handle failure JSON with nil on payload safely (#40)
|
12
|
+
|
13
|
+
## 0.3.1 (2016-01-28)
|
14
|
+
|
15
|
+
* Bug fix: fix the issue regex is not applied on bulk clearance (#36)
|
16
|
+
|
17
|
+
## 0.3.0 (2014-05-27)
|
18
|
+
|
19
|
+
* Search by regex (#27)
|
20
|
+
* Show stats by exception (#29)
|
21
|
+
* Stop supporting ruby 1.8.x and 1.9.2
|
22
|
+
* Escape query parameters (#30)
|
23
|
+
|
24
|
+
## 0.2.12 (2013-12-03)
|
25
|
+
|
26
|
+
* Remove Resque::Helpers include (#23)
|
27
|
+
* Tweak Gemspec (#24)
|
28
|
+
* Don't use yaml format to show arguments
|
29
|
+
* Tweak README
|
30
|
+
|
31
|
+
## 0.2.11 (2013-07-19)
|
32
|
+
|
33
|
+
* Use transaction for retry-and-clear (#22).
|
34
|
+
* Fix for CI
|
35
|
+
|
1
36
|
## 0.2.10 (2012-10-15)
|
2
37
|
|
3
38
|
* Bug fix: use URL helper to support resque-web which is not hosted under '/'.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
ResqueCleaner
|
1
|
+
ResqueCleaner
|
2
2
|
=============
|
3
3
|
|
4
4
|
[github.com/ono/resque-cleaner](https://github.com/ono/resque-cleaner)
|
@@ -27,7 +27,7 @@ Resque-Web integration
|
|
27
27
|
----------------------
|
28
28
|
|
29
29
|
![Screen 1](https://github.com/ono/resque-cleaner/raw/master/misc/resque-cleaner-main.png)
|
30
|
-
![Screen 2](
|
30
|
+
![Screen 2](misc/resque-cleaner-list-with-regex.png)
|
31
31
|
|
32
32
|
|
33
33
|
Configuration
|
@@ -117,6 +117,13 @@ e.g. Show stats only of jobs entered with some arguments:
|
|
117
117
|
=> {'2009/03/10' => 3, ...}
|
118
118
|
```
|
119
119
|
|
120
|
+
A convenience helper for searching for failed jobs which satisfy a
|
121
|
+
regular expression:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
cleaner.select_by_regex(/Jason/) # => all failed jobs that have "Jason" in them
|
125
|
+
```
|
126
|
+
|
120
127
|
**Retry(Requeue) Jobs**
|
121
128
|
|
122
129
|
You can retry all failed jobs with this method.
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<style type="text/css" media="screen">
|
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
|
+
#main .cleaner div.dump{
|
61
|
+
float: right
|
62
|
+
}
|
63
|
+
</style>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<table class="class_list">
|
2
|
+
<tr>
|
3
|
+
<th><%= type.gsub('klass','class').capitalize %></th>
|
4
|
+
<th>Failed</th>
|
5
|
+
<th>In last 1 hour</th>
|
6
|
+
<th>In last 3 hours</th>
|
7
|
+
<th>In last 24 hours</th>
|
8
|
+
<th>In last 3 days</th>
|
9
|
+
<th>In last 7 days</th>
|
10
|
+
</tr>
|
11
|
+
<% @stats[type.to_sym].each do |field,count| %>
|
12
|
+
<tr>
|
13
|
+
<% filter = "#{q}=#{URI.encode(field)}" %>
|
14
|
+
<td><%= field %></td>
|
15
|
+
<td class="number">
|
16
|
+
<a href="cleaner_list?<%=filter%>"><%= count[:total] %></a>
|
17
|
+
</td>
|
18
|
+
<td class="number">
|
19
|
+
<a href="cleaner_list?<%=filter%>&f=1"><%= count[:h1] %></a>
|
20
|
+
</td>
|
21
|
+
<td class="number">
|
22
|
+
<a href="cleaner_list?<%=filter%>&f=3"><%= count[:h3] %></a>
|
23
|
+
</td>
|
24
|
+
<td class="number">
|
25
|
+
<a href="cleaner_list?<%=filter%>&f=24"><%= count[:d1] %></a>
|
26
|
+
</td>
|
27
|
+
<td class="number">
|
28
|
+
<a href="cleaner_list?<%=filter%>&f=72"><%= count[:d3] %></a>
|
29
|
+
</td>
|
30
|
+
<td class="number">
|
31
|
+
<a href="cleaner_list?<%=filter%>&f=168"><%= count[:d7] %></a>
|
32
|
+
</td>
|
33
|
+
</tr>
|
34
|
+
<% end %>
|
35
|
+
<tr class="total">
|
36
|
+
<td>Total</td>
|
37
|
+
<td class="number"><a href="cleaner_list"><%= @total[:total] %></a></td>
|
38
|
+
<td class="number"><a href="cleaner_list?f=1"><%= @total[:h1] %></a></td>
|
39
|
+
<td class="number"><a href="cleaner_list?f=3"><%= @total[:h3] %></a></td>
|
40
|
+
<td class="number"><a href="cleaner_list?f=24"><%= @total[:d1] %></a></td>
|
41
|
+
<td class="number"><a href="cleaner_list?f=72"><%= @total[:d3] %></a></td>
|
42
|
+
<td class="number"><a href="cleaner_list?f=168"><%= @total[:d7] %></a></td>
|
43
|
+
</tr>
|
44
|
+
</table>
|
@@ -1,41 +1,23 @@
|
|
1
|
-
|
1
|
+
<%= erb File.read(ResqueCleaner::Server.erb_path("_cleaner_styles.erb")) %>
|
2
2
|
|
3
3
|
<div class="cleaner">
|
4
4
|
<div class="title clearfix">
|
5
|
-
<h1>
|
5
|
+
<h1>Group by class</h1>
|
6
6
|
</div>
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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>
|
8
|
+
<%= erb(
|
9
|
+
File.read(ResqueCleaner::Server.erb_path("_stats.erb")),
|
10
|
+
locals: {q: 'c', type: 'klass'}
|
11
|
+
) %>
|
12
|
+
|
13
|
+
<div class="title clearfix">
|
14
|
+
<h1>Group by exception</h1>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<%= erb(
|
18
|
+
File.read(ResqueCleaner::Server.erb_path("_stats.erb")),
|
19
|
+
locals: {q: 'ex', type: 'exception'}
|
20
|
+
) %>
|
39
21
|
|
40
22
|
<% if @cleaner.limiter.on? %>
|
41
23
|
<%= erb File.read(ResqueCleaner::Server.erb_path("_limiter.erb")) %>
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
<%= erb File.read(ResqueCleaner::Server.erb_path("_cleaner_styles.erb")) %>
|
2
2
|
|
3
3
|
<div class="cleaner">
|
4
4
|
<p class="message">Processed <%= @count %> jobs.</p>
|
5
5
|
<p class="back_to_list">
|
6
|
-
<a href="<%=@
|
6
|
+
<a href="<%=@list_url%>">Back to List</a>
|
7
7
|
</p>
|
8
8
|
</div>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
<%= erb File.read(ResqueCleaner::Server.erb_path("_cleaner_styles.erb")) %>
|
2
2
|
|
3
3
|
<!-- Many code was copied from failed.erb of the original resque -->
|
4
4
|
<div class="cleaner">
|
@@ -21,6 +21,9 @@
|
|
21
21
|
<span class="time_filter">
|
22
22
|
To: <%= time_filter("filter_to","t",@to)%>
|
23
23
|
</span>
|
24
|
+
<span class="regex_filter">
|
25
|
+
Regex: <%= text_filter("filter_regex", "regex", Rack::Utils.escape_html(@regex))%>
|
26
|
+
</span>
|
24
27
|
<input type="submit" value="Filter" />
|
25
28
|
</form>
|
26
29
|
</div>
|
@@ -35,6 +38,7 @@
|
|
35
38
|
<input type="hidden" name="t" value="<%=@to%>" />
|
36
39
|
<input type="hidden" name="p" value="<%=@paginate.page%>" />
|
37
40
|
<input type="hidden" name="ex" value="<%=@exception%>" />
|
41
|
+
<input type="hidden" name="regex" value="<%=Rack::Utils.escape_html(@regex)%>" />
|
38
42
|
<select id="form_action" name="action">
|
39
43
|
<option id="default_option" value="" selected="selected">-- Select Action --</option>
|
40
44
|
<option value="clear">Clear</option>
|
@@ -87,7 +91,7 @@
|
|
87
91
|
<dt>Class</dt>
|
88
92
|
<dd><code><%= job['payload'] ? job['payload']['class'] : 'nil' %></code></dd>
|
89
93
|
<dt>Arguments</dt>
|
90
|
-
<dd><pre><%=h job['payload'] ?
|
94
|
+
<dd><pre><%=h job['payload'] ? show_job_args(job['payload']['args']) : 'nil' %></pre></dd>
|
91
95
|
<dt>Exception</dt>
|
92
96
|
<dd><code><%= job['exception'] %></code></dd>
|
93
97
|
<dt>Error</dt>
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
# Extends Resque Web Based UI.
|
2
4
|
# Structure has been borrowed from ResqueScheduler.
|
3
5
|
module ResqueCleaner
|
@@ -6,9 +8,6 @@ module ResqueCleaner
|
|
6
8
|
def self.erb_path(filename)
|
7
9
|
File.join(File.dirname(__FILE__), 'server', 'views', filename)
|
8
10
|
end
|
9
|
-
def self.public_path(filename)
|
10
|
-
File.join(File.dirname(__FILE__), 'server', 'public', filename)
|
11
|
-
end
|
12
11
|
|
13
12
|
# Pagination helper for list page.
|
14
13
|
class Paginate
|
@@ -97,6 +96,15 @@ module ResqueCleaner
|
|
97
96
|
end
|
98
97
|
html += "</select>"
|
99
98
|
end
|
99
|
+
|
100
|
+
def show_job_args(args)
|
101
|
+
Array(args).map { |a| a.inspect }.join("\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
def text_filter(id, name, value)
|
105
|
+
html = "<input id=\"#{id}\" type=\"text\" name=\"#{name}\" value=\"#{value}\">"
|
106
|
+
html += "</input>"
|
107
|
+
end
|
100
108
|
end
|
101
109
|
|
102
110
|
mime_type :json, 'application/json'
|
@@ -106,24 +114,28 @@ module ResqueCleaner
|
|
106
114
|
load_cleaner_filter
|
107
115
|
|
108
116
|
@jobs = cleaner.select
|
109
|
-
@stats
|
117
|
+
@stats = { :klass => {}, :exception => {} }
|
118
|
+
@total = Hash.new(0)
|
110
119
|
@jobs.each do |job|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
"UNKNOWN"
|
115
|
-
end
|
120
|
+
payload = job["payload"] || {}
|
121
|
+
klass = payload["class"] || 'UNKNOWN'
|
122
|
+
exception = job["exception"] || 'UNKNOWN'
|
116
123
|
failed_at = Time.parse job["failed_at"]
|
117
|
-
|
118
|
-
@stats[
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
124
|
+
@stats[:klass][klass] ||= Hash.new(0)
|
125
|
+
@stats[:exception][exception] ||= Hash.new(0)
|
126
|
+
|
127
|
+
[
|
128
|
+
@stats[:klass][klass],
|
129
|
+
@stats[:exception][exception],
|
130
|
+
@total
|
131
|
+
].each do |stat|
|
132
|
+
stat[:total] += 1
|
133
|
+
stat[:h1] += 1 if failed_at >= hours_ago(1)
|
134
|
+
stat[:h3] += 1 if failed_at >= hours_ago(3)
|
135
|
+
stat[:d1] += 1 if failed_at >= hours_ago(24)
|
136
|
+
stat[:d3] += 1 if failed_at >= hours_ago(24*3)
|
137
|
+
stat[:d7] += 1 if failed_at >= hours_ago(24*7)
|
138
|
+
end
|
127
139
|
end
|
128
140
|
|
129
141
|
erb File.read(ResqueCleaner::Server.erb_path('cleaner.erb'))
|
@@ -132,14 +144,13 @@ module ResqueCleaner
|
|
132
144
|
get "/cleaner_list" do
|
133
145
|
load_library
|
134
146
|
load_cleaner_filter
|
147
|
+
build_urls
|
135
148
|
|
136
149
|
block = filter_block
|
137
150
|
|
138
151
|
@failed = cleaner.select(&block).reverse
|
139
152
|
|
140
|
-
|
141
|
-
@dump_url = "cleaner_dump?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
|
142
|
-
@paginate = Paginate.new(@failed, url, params[:p].to_i)
|
153
|
+
@paginate = Paginate.new(@failed, @list_url, params[:p].to_i)
|
143
154
|
|
144
155
|
@klasses = cleaner.stats_by_class.keys
|
145
156
|
@exceptions = cleaner.stats_by_exception.keys
|
@@ -151,6 +162,7 @@ module ResqueCleaner
|
|
151
162
|
post "/cleaner_exec" do
|
152
163
|
load_library
|
153
164
|
load_cleaner_filter
|
165
|
+
build_urls
|
154
166
|
|
155
167
|
if params[:select_all_pages]!="1"
|
156
168
|
@sha1 = {}
|
@@ -166,7 +178,6 @@ module ResqueCleaner
|
|
166
178
|
when "retry" then cleaner.requeue(false,{},&block)
|
167
179
|
end
|
168
180
|
|
169
|
-
@url = "cleaner_list?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
|
170
181
|
erb File.read(ResqueCleaner::Server.erb_path('cleaner_exec.erb'))
|
171
182
|
end
|
172
183
|
|
@@ -185,10 +196,6 @@ module ResqueCleaner
|
|
185
196
|
cleaner.clear_stale
|
186
197
|
redirect url_path(:cleaner)
|
187
198
|
end
|
188
|
-
|
189
|
-
get /cleaner\/public\/([a-z]+\.[a-z]+)/ do
|
190
|
-
send_file ResqueCleaner::Server.public_path(params[:captures].first)
|
191
|
-
end
|
192
199
|
end
|
193
200
|
|
194
201
|
end
|
@@ -213,6 +220,20 @@ module ResqueCleaner
|
|
213
220
|
@to = params[:t]=="" ? nil : params[:t]
|
214
221
|
@klass = params[:c]=="" ? nil : params[:c]
|
215
222
|
@exception = params[:ex]=="" ? nil : params[:ex]
|
223
|
+
@regex = params[:regex]=="" ? nil : params[:regex]
|
224
|
+
end
|
225
|
+
|
226
|
+
def build_urls
|
227
|
+
params = {
|
228
|
+
c: @klass,
|
229
|
+
ex: @exception,
|
230
|
+
f: @from,
|
231
|
+
t: @to,
|
232
|
+
regex: @regex
|
233
|
+
}.map {|key,value| "#{key}=#{URI.encode(value.to_s)}"}.join("&")
|
234
|
+
|
235
|
+
@list_url = "cleaner_list?#{params}"
|
236
|
+
@dump_url = "cleaner_dump?#{params}"
|
216
237
|
end
|
217
238
|
|
218
239
|
def filter_block
|
@@ -221,7 +242,8 @@ module ResqueCleaner
|
|
221
242
|
(!@to || j.before?(hours_ago(@to))) &&
|
222
243
|
(!@klass || j.klass?(@klass)) &&
|
223
244
|
(!@exception || j.exception?(@exception)) &&
|
224
|
-
(!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)])
|
245
|
+
(!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)]) &&
|
246
|
+
(!@regex || j.to_s =~ /#{@regex}/)
|
225
247
|
}
|
226
248
|
end
|
227
249
|
|
@@ -235,4 +257,3 @@ end
|
|
235
257
|
Resque::Server.class_eval do
|
236
258
|
include ResqueCleaner::Server
|
237
259
|
end
|
238
|
-
|
data/lib/resque_cleaner.rb
CHANGED
@@ -7,7 +7,6 @@ module Resque
|
|
7
7
|
# ResqueCleaner class provides useful functionalities to retry or clean
|
8
8
|
# failed jobs. Let's clean up your failed list!
|
9
9
|
class ResqueCleaner
|
10
|
-
include Resque::Helpers
|
11
10
|
# ResqueCleaner fetches all elements from Redis and checks them
|
12
11
|
# by linear when filtering them. Since there is a performance concern,
|
13
12
|
# ResqueCleaner handles only the latest x(default 1000) jobs.
|
@@ -91,6 +90,12 @@ module Resque
|
|
91
90
|
end
|
92
91
|
alias :failure_jobs :select
|
93
92
|
|
93
|
+
def select_by_regex(regex)
|
94
|
+
select do |job|
|
95
|
+
job.to_s =~ regex
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
94
99
|
# Clears every jobs for which block evaluates to true.
|
95
100
|
def clear(&block)
|
96
101
|
cleared = 0
|
@@ -118,17 +123,21 @@ module Resque
|
|
118
123
|
if !block_given? || block.call(job)
|
119
124
|
index = @limiter.start_index + i - requeued
|
120
125
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
value = redis.lindex(:failed, index)
|
127
|
+
redis.multi do
|
128
|
+
Job.create(queue||job['queue'], job['payload']['class'], *job['payload']['args'])
|
129
|
+
|
130
|
+
if clear_after_requeue
|
131
|
+
# remove job
|
132
|
+
# TODO: should use ltrim. not sure why i used lrem here...
|
133
|
+
redis.lrem(:failed, 1, value)
|
134
|
+
else
|
135
|
+
# mark retried
|
136
|
+
job['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
137
|
+
redis.lset(:failed, @limiter.start_index+i, Resque.encode(job))
|
138
|
+
end
|
129
139
|
end
|
130
140
|
|
131
|
-
Job.create(queue||job['queue'], job['payload']['class'], *job['payload']['args'])
|
132
141
|
requeued += 1
|
133
142
|
end
|
134
143
|
end
|
data/test/dump.rdb
ADDED
Binary file
|
data/test/redis-test.conf
CHANGED
@@ -112,4 +112,4 @@ databases 16
|
|
112
112
|
# Glue small output buffers together in order to send small replies in a
|
113
113
|
# single TCP packet. Uses a bit more CPU but most of the times it is a win
|
114
114
|
# in terms of number of queries per second. Use 'yes' if unsure.
|
115
|
-
glueoutputbuf yes
|
115
|
+
#glueoutputbuf yes
|
data/test/resque_cleaner_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
require 'time'
|
3
|
-
|
4
|
-
|
3
|
+
describe "ResqueCleaner" do
|
4
|
+
before do
|
5
5
|
Resque.redis.flushall
|
6
6
|
|
7
7
|
@worker = Resque::Worker.new(:jobs,:jobs2)
|
@@ -28,41 +28,53 @@ context "ResqueCleaner" do
|
|
28
28
|
@cleaner.print_message = false
|
29
29
|
end
|
30
30
|
|
31
|
-
|
31
|
+
it "#select_by_regex returns only Jason jobs" do
|
32
|
+
ret = @cleaner.select_by_regex(/Jason/)
|
33
|
+
assert_equal 13, ret.size
|
34
|
+
end
|
35
|
+
|
36
|
+
it "#select_by_regex returns an empty array if passed a non-regex" do
|
37
|
+
['string', nil, 13, Class.new].each do |non_regex|
|
38
|
+
ret = @cleaner.select_by_regex(nil)
|
39
|
+
assert_equal 0, ret.size
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "#select returns failure jobs" do
|
32
44
|
ret = @cleaner.select
|
33
45
|
assert_equal 42, ret.size
|
34
46
|
end
|
35
47
|
|
36
|
-
|
48
|
+
it "#select works with a limit" do
|
37
49
|
@cleaner.limiter.maximum = 10
|
38
50
|
ret = @cleaner.select
|
39
51
|
|
40
52
|
# only maximum number
|
41
53
|
assert_equal 10, ret.size
|
42
54
|
|
43
|
-
#
|
55
|
+
# lait one
|
44
56
|
assert_equal Time.parse(ret[0]['failed_at']), Time.parse('2010-08-13')
|
45
57
|
end
|
46
58
|
|
47
|
-
|
59
|
+
it "#select with a block returns failure jobs which the block evaluates true" do
|
48
60
|
ret = @cleaner.select {|job| job["payload"]["args"][0]=="Jason"}
|
49
61
|
assert_equal 13, ret.size
|
50
62
|
end
|
51
63
|
|
52
|
-
|
64
|
+
it "#clear deletes failure jobs" do
|
53
65
|
cleared = @cleaner.clear
|
54
66
|
assert_equal 42, cleared
|
55
67
|
assert_equal 0, @cleaner.select.size
|
56
68
|
end
|
57
69
|
|
58
|
-
|
70
|
+
it "#clear with a block deletes failure jobs which the block evaluates true" do
|
59
71
|
cleared = @cleaner.clear{|job| job["payload"]["args"][0]=="Jason"}
|
60
72
|
assert_equal 13, cleared
|
61
73
|
assert_equal 42-13, @cleaner.select.size
|
62
74
|
assert_equal 0, @cleaner.select{|job| job["payload"]["args"][0]=="Jason"}.size
|
63
75
|
end
|
64
76
|
|
65
|
-
|
77
|
+
it "#requeue retries failure jobs" do
|
66
78
|
assert_equal 0, queue_size(:jobs,:jobs2)
|
67
79
|
requeued = @cleaner.requeue
|
68
80
|
assert_equal 42, requeued
|
@@ -70,13 +82,13 @@ context "ResqueCleaner" do
|
|
70
82
|
assert_equal 42, queue_size(:jobs,:jobs2)
|
71
83
|
end
|
72
84
|
|
73
|
-
|
85
|
+
it "#requeue with a block retries failure jobs which the block evaluates true" do
|
74
86
|
requeued = @cleaner.requeue{|job| job["payload"]["args"][0]=="Jason"}
|
75
87
|
assert_equal 13, requeued
|
76
88
|
assert_equal 13, queue_size(:jobs,:jobs2)
|
77
89
|
end
|
78
90
|
|
79
|
-
|
91
|
+
it "#requeue with clear option requeues and deletes failure jobs" do
|
80
92
|
assert_equal 0, queue_size(:jobs,:jobs2)
|
81
93
|
requeued = @cleaner.requeue(true)
|
82
94
|
assert_equal 42, requeued
|
@@ -84,7 +96,7 @@ context "ResqueCleaner" do
|
|
84
96
|
assert_equal 0, @cleaner.select.size
|
85
97
|
end
|
86
98
|
|
87
|
-
|
99
|
+
it "#requeue with :queue option requeues the jobs to the queue" do
|
88
100
|
assert_equal 0, queue_size(:jobs,:jobs2,:retry)
|
89
101
|
requeued = @cleaner.requeue false, :queue => :retry
|
90
102
|
assert_equal 42, requeued
|
@@ -93,14 +105,14 @@ context "ResqueCleaner" do
|
|
93
105
|
assert_equal 42, queue_size(:retry)
|
94
106
|
end
|
95
107
|
|
96
|
-
|
108
|
+
it "#clear_stale deletes failure jobs which is queued before the last x enqueued" do
|
97
109
|
@cleaner.limiter.maximum = 10
|
98
110
|
@cleaner.clear_stale
|
99
111
|
assert_equal 10, @cleaner.failure.count
|
100
112
|
assert_equal Time.parse(@cleaner.failure_jobs[0]['failed_at']), Time.parse('2010-08-13')
|
101
113
|
end
|
102
114
|
|
103
|
-
|
115
|
+
it "FailedJobEx module extends job and provides some useful methods" do
|
104
116
|
# before 2009-04-01
|
105
117
|
ret = @cleaner.select {|j| j.before?('2009-04-01')}
|
106
118
|
assert_equal 6, ret.size
|
@@ -135,7 +147,7 @@ context "ResqueCleaner" do
|
|
135
147
|
assert_equal 1, ret.size
|
136
148
|
end
|
137
149
|
|
138
|
-
|
150
|
+
it "#stats_by_date returns stats grouped by date" do
|
139
151
|
ret = @cleaner.stats_by_date
|
140
152
|
assert_equal 6, ret['2009/03/13']
|
141
153
|
assert_equal 14, ret['2009/11/13']
|
@@ -147,25 +159,25 @@ context "ResqueCleaner" do
|
|
147
159
|
assert_equal 11, ret['2010/08/13']
|
148
160
|
end
|
149
161
|
|
150
|
-
|
162
|
+
it "#stats_by_class returns stats grouped by class" do
|
151
163
|
ret = @cleaner.stats_by_class
|
152
164
|
assert_equal 35, ret['BadJob']
|
153
165
|
assert_equal 7, ret['BadJobWithSyntaxError']
|
154
166
|
end
|
155
167
|
|
156
|
-
|
168
|
+
it "#stats_by_class works with broken log" do
|
157
169
|
add_empty_payload_failure
|
158
170
|
ret = @cleaner.stats_by_class
|
159
171
|
assert_equal 1, ret['UNKNOWN']
|
160
172
|
end
|
161
173
|
|
162
|
-
|
174
|
+
it "#stats_by_exception returns stats grouped by exception" do
|
163
175
|
ret = @cleaner.stats_by_exception
|
164
176
|
assert_equal 35, ret['RuntimeError']
|
165
177
|
assert_equal 7, ret['SyntaxError']
|
166
178
|
end
|
167
179
|
|
168
|
-
|
180
|
+
it "#lock ensures that a new failure job doesn't affect in a limit mode" do
|
169
181
|
@cleaner.limiter.maximum = 23
|
170
182
|
@cleaner.limiter.lock do
|
171
183
|
first = @cleaner.select[0]
|
@@ -180,9 +192,9 @@ context "ResqueCleaner" do
|
|
180
192
|
assert_equal "Jack", first["payload"]["args"][0]
|
181
193
|
end
|
182
194
|
|
183
|
-
|
195
|
+
it "allows you to configure limiter" do
|
184
196
|
c = Resque::Plugins::ResqueCleaner.new
|
185
|
-
|
197
|
+
refute_equal c.limiter.maximum, 10_000
|
186
198
|
|
187
199
|
module Resque::Plugins
|
188
200
|
ResqueCleaner::Limiter.default_maximum = 10_000
|
data/test/resque_web_test.rb
CHANGED
@@ -1,15 +1,23 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
2
|
|
3
|
-
# Pull in the server test_helper from resque
|
4
|
-
require 'resque/server/test_helper.rb'
|
5
3
|
require 'digest/sha1'
|
6
4
|
require 'json'
|
5
|
+
require 'rack/test'
|
6
|
+
|
7
|
+
class Minitest::Spec
|
8
|
+
include Rack::Test::Methods
|
9
|
+
def app
|
10
|
+
Resque::Server.new
|
11
|
+
end
|
12
|
+
end
|
7
13
|
|
8
14
|
def setup_some_failed_jobs
|
9
15
|
Resque.redis.flushall
|
10
16
|
|
11
17
|
@worker = Resque::Worker.new(:jobs,:jobs2)
|
12
18
|
|
19
|
+
create_and_process_jobs :jobs, @worker, 1, Time.now, BadJobWithSyntaxError, "great_args"
|
20
|
+
|
13
21
|
10.times {|i|
|
14
22
|
create_and_process_jobs :jobs, @worker, 1, Time.now, BadJob, "test_#{i}"
|
15
23
|
}
|
@@ -18,31 +26,39 @@ def setup_some_failed_jobs
|
|
18
26
|
@cleaner.print_message = false
|
19
27
|
end
|
20
28
|
|
21
|
-
|
22
|
-
|
29
|
+
describe "resque-web" do
|
30
|
+
before do
|
23
31
|
setup_some_failed_jobs
|
24
32
|
end
|
25
33
|
|
26
|
-
|
27
|
-
get "/
|
28
|
-
assert last_response.
|
34
|
+
it "#cleaner should respond with success" do
|
35
|
+
get "/cleaner"
|
36
|
+
assert last_response.body.include?('BadJob')
|
37
|
+
assert last_response.body =~ /\bException\b/
|
29
38
|
end
|
30
39
|
|
31
|
-
|
40
|
+
it "#cleaner_list should respond with success" do
|
32
41
|
get "/cleaner_list"
|
33
42
|
assert last_response.ok?, last_response.errors
|
34
43
|
end
|
35
44
|
|
36
|
-
|
45
|
+
it '#cleaner_list shows the failed jobs' do
|
37
46
|
get "/cleaner_list"
|
38
47
|
assert last_response.body.include?('BadJob')
|
39
48
|
end
|
40
49
|
|
41
|
-
|
50
|
+
it "#cleaner_list shows the failed jobs when we use a select_by_regex" do
|
51
|
+
get "/cleaner_list", :regex => "BadJob*"
|
52
|
+
assert last_response.body.include?('"BadJobWithSyntaxError"')
|
53
|
+
assert last_response.body.include?('"BadJob"')
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
it '#cleaner_exec clears job' do
|
42
58
|
post "/cleaner_exec", :action => "clear", :sha1 => Digest::SHA1.hexdigest(@cleaner.select[0].to_json)
|
43
|
-
assert_equal
|
59
|
+
assert_equal 10, @cleaner.select.size
|
44
60
|
end
|
45
|
-
|
61
|
+
it "#cleaner_dump should respond with success" do
|
46
62
|
get "/cleaner_dump"
|
47
63
|
assert last_response.ok?, last_response.errors
|
48
64
|
end
|
data/test/test_helper.rb
CHANGED
@@ -4,8 +4,10 @@
|
|
4
4
|
dir = File.dirname(File.expand_path(__FILE__))
|
5
5
|
$LOAD_PATH.unshift dir + '/../lib'
|
6
6
|
$TESTING = true
|
7
|
-
require 'test/unit'
|
8
7
|
require 'rubygems'
|
8
|
+
require 'minitest'
|
9
|
+
require 'minitest/spec'
|
10
|
+
require 'minitest/autorun'
|
9
11
|
require 'resque'
|
10
12
|
require 'timecop'
|
11
13
|
|
@@ -16,6 +18,8 @@ end
|
|
16
18
|
require 'resque'
|
17
19
|
require 'resque_cleaner'
|
18
20
|
|
21
|
+
$TEST_PID = Process.pid
|
22
|
+
|
19
23
|
#
|
20
24
|
# make sure we can run redis
|
21
25
|
#
|
@@ -31,48 +35,22 @@ end
|
|
31
35
|
# start our own redis when the tests start,
|
32
36
|
# kill it when they end
|
33
37
|
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
MiniTest.after_run {
|
39
|
+
if Process.pid == $TEST_PID
|
40
|
+
processes = `ps -A -o pid,command | grep [r]edis-test`.split($/)
|
41
|
+
pids = processes.map { |process| process.split(" ")[0] }
|
42
|
+
puts "Killing test redis server..."
|
43
|
+
pids.each { |pid| Process.kill("TERM", pid.to_i) }
|
44
|
+
dump = "test/dump.rdb"
|
45
|
+
File.delete(dump) if File.exist?(dump)
|
42
46
|
end
|
43
|
-
|
44
|
-
pid = `ps -A -o pid,command | grep [r]edis-test`.split(" ")[0]
|
45
|
-
puts "Killing test redis server..."
|
46
|
-
`rm -f #{dir}/dump.rdb`
|
47
|
-
Process.kill("KILL", pid.to_i)
|
48
|
-
exit exit_code
|
49
|
-
end
|
47
|
+
}
|
50
48
|
|
51
49
|
puts "Starting redis for testing at localhost:9736..."
|
52
50
|
`redis-server #{dir}/redis-test.conf`
|
53
51
|
Resque.redis = 'localhost:9736'
|
54
52
|
|
55
53
|
|
56
|
-
##
|
57
|
-
# test/spec/mini 3
|
58
|
-
# http://gist.github.com/25455
|
59
|
-
# chris@ozmm.org
|
60
|
-
#
|
61
|
-
def context(*args, &block)
|
62
|
-
return super unless (name = args.first) && block
|
63
|
-
require 'test/unit'
|
64
|
-
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
|
65
|
-
def self.test(name, &block)
|
66
|
-
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
67
|
-
end
|
68
|
-
def self.xtest(*args) end
|
69
|
-
def self.setup(&block) define_method(:setup, &block) end
|
70
|
-
def self.teardown(&block) define_method(:teardown, &block) end
|
71
|
-
end
|
72
|
-
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
73
|
-
klass.class_eval &block
|
74
|
-
end
|
75
|
-
|
76
54
|
##
|
77
55
|
# Helper to perform job classes
|
78
56
|
#
|
metadata
CHANGED
@@ -1,32 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-cleaner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Tatsuya Ono
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2018-01-25 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: resque
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ! '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
19
|
+
version: '0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
26
30
|
requirements:
|
27
31
|
- - ~>
|
28
32
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
33
|
+
version: '5.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack-test
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.6.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.6.0
|
30
55
|
description: ! ' resque-cleaner maintains the cleanliness of failed jobs on Resque.
|
31
56
|
|
32
57
|
'
|
@@ -35,50 +60,52 @@ executables: []
|
|
35
60
|
extensions: []
|
36
61
|
extra_rdoc_files:
|
37
62
|
- LICENSE
|
38
|
-
- README.
|
63
|
+
- README.md
|
39
64
|
- CHANGELOG.md
|
40
65
|
files:
|
41
|
-
- README.
|
66
|
+
- README.md
|
42
67
|
- CHANGELOG.md
|
43
68
|
- Rakefile
|
44
69
|
- LICENSE
|
45
70
|
- lib/resque-cleaner.rb
|
46
|
-
- lib/resque_cleaner/server/
|
71
|
+
- lib/resque_cleaner/server/views/_cleaner_styles.erb
|
47
72
|
- lib/resque_cleaner/server/views/_limiter.erb
|
48
73
|
- lib/resque_cleaner/server/views/_paginate.erb
|
74
|
+
- lib/resque_cleaner/server/views/_stats.erb
|
49
75
|
- lib/resque_cleaner/server/views/cleaner.erb
|
50
76
|
- lib/resque_cleaner/server/views/cleaner_exec.erb
|
51
77
|
- lib/resque_cleaner/server/views/cleaner_list.erb
|
52
78
|
- lib/resque_cleaner/server.rb
|
53
79
|
- lib/resque_cleaner.rb
|
80
|
+
- test/dump.rdb
|
54
81
|
- test/redis-test.conf
|
55
82
|
- test/resque_cleaner_test.rb
|
56
83
|
- test/resque_web_test.rb
|
57
84
|
- test/test_helper.rb
|
58
|
-
homepage:
|
59
|
-
licenses:
|
85
|
+
homepage: https://github.com/ono/resque-cleaner
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
metadata: {}
|
60
89
|
post_install_message:
|
61
90
|
rdoc_options:
|
62
91
|
- --charset=UTF-8
|
63
92
|
require_paths:
|
64
93
|
- lib
|
65
94
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
-
none: false
|
67
95
|
requirements:
|
68
96
|
- - ! '>='
|
69
97
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
98
|
+
version: 1.9.3
|
71
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
-
none: false
|
73
100
|
requirements:
|
74
101
|
- - ! '>='
|
75
102
|
- !ruby/object:Gem::Version
|
76
103
|
version: '0'
|
77
104
|
requirements: []
|
78
105
|
rubyforge_project:
|
79
|
-
rubygems_version: 1.
|
106
|
+
rubygems_version: 2.1.11
|
80
107
|
signing_key:
|
81
|
-
specification_version:
|
108
|
+
specification_version: 4
|
82
109
|
summary: Resque plugin cleaning up failed jobs.
|
83
110
|
test_files: []
|
84
111
|
has_rdoc:
|
@@ -1,64 +0,0 @@
|
|
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
|
-
#main .cleaner div.dump{
|
61
|
-
float: right
|
62
|
-
}
|
63
|
-
|
64
|
-
|