resque-cleaner 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Tatsuya Ono
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ Resque-Cleaner
2
+ ==============
3
+
4
+ Resque-Cleaner is a Resque plugin which helps you to deal with failure jobs.
5
+
6
+ (This README will be update in days.)
@@ -0,0 +1,38 @@
1
+ #
2
+ # Setup
3
+ #
4
+
5
+ # load 'tasks/redis.rake'
6
+ #require 'rake/testtask'
7
+
8
+ $LOAD_PATH.unshift 'lib'
9
+ #require 'resque/tasks'
10
+
11
+ def command?(command)
12
+ system("type #{command} > /dev/null 2>&1")
13
+ end
14
+
15
+
16
+ #
17
+ # Tests
18
+ #
19
+
20
+ task :default => :test
21
+
22
+ desc "Run the test suite"
23
+ task :test do
24
+ rg = command?(:rg)
25
+ Dir['test/**/*_test.rb'].each do |f|
26
+ rg ? sh("rg #{f}") : ruby(f)
27
+ end
28
+ end
29
+
30
+ if command? :kicker
31
+ desc "Launch Kicker (like autotest)"
32
+ task :kicker do
33
+ puts "Kicking... (ctrl+c to cancel)"
34
+ exec "kicker -e rake test lib examples"
35
+ end
36
+ end
37
+
38
+
@@ -0,0 +1,275 @@
1
+ require 'time'
2
+ module Resque
3
+ module Plugins
4
+ # ResqueCleaner class provides useful functionalities to retry or clean
5
+ # failed jobs. Let's clean up your failed list!
6
+ class ResqueCleaner
7
+ include Resque::Helpers
8
+ # ResqueCleaner fetches all elements from Redis and checks them
9
+ # by linear when filtering them. Since there is a performance concern,
10
+ # ResqueCleaner handles only the latest x(default 1000) jobs.
11
+ #
12
+ # You can change the value through limiter attribute.
13
+ # e.g. cleaner.limiter.maximum = 5000
14
+ attr_reader :limiter
15
+
16
+ # Set false if you don't show any message.
17
+ attr_accessor :print_message
18
+
19
+ # Initializes instance
20
+ def initialize
21
+ @failure = Resque::Failure.backend
22
+ @print_message = true
23
+ @limiter = Limiter.new self
24
+ end
25
+
26
+ # Returns redis instance.
27
+ def redis
28
+ Resque.redis
29
+ end
30
+
31
+ # Returns failure backend. Only supports redis backend.
32
+ def failure
33
+ @failure
34
+ end
35
+
36
+ # Stats by date.
37
+ def stats_by_date(&block)
38
+ jobs = select(&block)
39
+ summary = {}
40
+ jobs.each do |job|
41
+ date = job["failed_at"][0,10]
42
+ summary[date] ||= 0
43
+ summary[date] += 1
44
+ end
45
+
46
+ if print?
47
+ log too_many_message if @limiter.on?
48
+ summary.keys.sort.each do |k|
49
+ log "%s: %4d" % [k,summary[k]]
50
+ end
51
+ log "%10s: %4d" % ["total", @limiter.count]
52
+ end
53
+ summary
54
+ end
55
+
56
+ # Stats by class.
57
+ def stats_by_class(&block)
58
+ jobs = select(&block)
59
+ summary = {}
60
+ jobs.each do |job|
61
+ klass = job["payload"]["class"]
62
+ summary[klass] ||= 0
63
+ summary[klass] += 1
64
+ end
65
+
66
+ if print?
67
+ log too_many_message if @limiter.on?
68
+ summary.keys.sort.each do |k|
69
+ log "%15s: %4d" % [k,summary[k]]
70
+ end
71
+ log "%15s: %4d" % ["total", @limiter.count]
72
+ end
73
+ summary
74
+ end
75
+
76
+ # Returns every jobs for which block evaluates to true.
77
+ def select(&block)
78
+ jobs = @limiter.jobs
79
+ block_given? ? @limiter.jobs.select(&block) : jobs
80
+ end
81
+ alias :failure_jobs :select
82
+
83
+ # Clears every jobs for which block evaluates to true.
84
+ def clear(&block)
85
+ cleared = 0
86
+ @limiter.lock do
87
+ @limiter.jobs.each_with_index do |job,i|
88
+ if !block_given? || block.call(job)
89
+ index = @limiter.start_index + i - cleared
90
+ # fetches again since you can't ensure that it is always true:
91
+ # a == endode(decode(a))
92
+ value = redis.lindex(:failed, index)
93
+ redis.lrem(:failed, 1, value)
94
+ cleared += 1
95
+ end
96
+ end
97
+ end
98
+ cleared
99
+ end
100
+
101
+ # Retries every jobs for which block evaluates to true.
102
+ def requeue(clear_after_requeue=false, &block)
103
+ requeued = 0
104
+ @limiter.lock do
105
+ @limiter.jobs.each_with_index do |job,i|
106
+ if !block_given? || block.call(job)
107
+ index = @limiter.start_index + i - requeued
108
+
109
+ if clear_after_requeue
110
+ # remove job
111
+ value = redis.lindex(:failed, index)
112
+ redis.lrem(:failed, 1, value)
113
+ else
114
+ # mark retried
115
+ job['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
116
+ redis.lset(:failed, @limiter.start_index+i, Resque.encode(job))
117
+ end
118
+
119
+ Job.create(job['queue'], job['payload']['class'], *job['payload']['args'])
120
+ requeued += 1
121
+ end
122
+ end
123
+ end
124
+ requeued
125
+ end
126
+
127
+ # Clears all jobs except the last X jobs
128
+ def clear_stale
129
+ return 0 unless @limiter.on?
130
+ c = @limiter.maximum
131
+ redis.ltrim(:failed, -c, -1)
132
+ c
133
+ end
134
+
135
+ # Returns Proc which you can add a useful condition easily.
136
+ # e.g.
137
+ # cleaner.clear &cleaner.proc.retried
138
+ # #=> Clears all jobs retried.
139
+ # cleaner.select &cleaner.proc.after(10.days.ago).klass(EmailJob)
140
+ # #=> Selects all EmailJob failed within 10 days.
141
+ # cleaner.select &cleaner.proc{|j| j["exception"]=="RunTimeError"}.klass(EmailJob)
142
+ # #=> Selects all EmailJob failed with RunTimeError.
143
+ def proc(&block)
144
+ FilterProc.new(&block)
145
+ end
146
+
147
+ # Provides typical proc you can filter jobs.
148
+ class FilterProc < Proc
149
+ def retried
150
+ FilterProc.new {|job| self.call(job) && job['retried_at'].blank?}
151
+ end
152
+ alias :requeued :retried
153
+
154
+ def before(time)
155
+ time = Time.parse(time) if time.is_a?(String)
156
+ FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) <= time}
157
+ end
158
+
159
+ def after(time)
160
+ time = Time.parse(time) if time.is_a?(String)
161
+ FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) >= time}
162
+ end
163
+
164
+ def klass(klass_or_name)
165
+ FilterProc.new {|job| self.call(job) && job["payload"]["class"] == klass_or_name.to_s}
166
+ end
167
+
168
+ def queue(queue)
169
+ FilterProc.new {|job| self.call(job) && job["queue"] == queue.to_s}
170
+ end
171
+
172
+ def self.new(&block)
173
+ if block
174
+ super
175
+ else
176
+ super {|job| true}
177
+ end
178
+ end
179
+ end
180
+
181
+ # Through the Limiter class, you accesses only the last x(default 1000)
182
+ # jobs.
183
+ class Limiter
184
+ DEFAULT_MAX_JOBS = 1000
185
+ attr_accessor :maximum
186
+ def initialize(cleaner)
187
+ @cleaner = cleaner
188
+ @maximum = DEFAULT_MAX_JOBS
189
+ @locked = false
190
+ end
191
+
192
+ # Returns true if limiter is ON: number of failed jobs is more than
193
+ # maximum value.
194
+ def on?
195
+ @cleaner.failure.count > @maximum
196
+ end
197
+
198
+ # Returns limited count.
199
+ def count
200
+ if @locked
201
+ @jobs.size
202
+ else
203
+ on? ? @maximum : @cleaner.failure.count
204
+ end
205
+ end
206
+
207
+ # Returns jobs. If numbers of jobs is more than maixum, it returns only
208
+ # the maximum.
209
+ def jobs
210
+ if @locked
211
+ @jobs
212
+ else
213
+ all( - count, count)
214
+ end
215
+ end
216
+
217
+ # wraps Resque's all and returns always array.
218
+ def all(index=0,count=1)
219
+ jobs = @cleaner.failure.all( index, count)
220
+ jobs = [] unless jobs
221
+ jobs = [jobs] unless jobs.is_a?(Array)
222
+ jobs
223
+ end
224
+
225
+ # Returns a start index of jobs in :failed list.
226
+ def start_index
227
+ if @locked
228
+ @start_index
229
+ else
230
+ on? ? @cleaner.failure.count-@maximum : 0
231
+ end
232
+ end
233
+
234
+ # Assuming new failures pushed while cleaner is dealing with failures,
235
+ # you need to lock the range.
236
+ def lock
237
+ old = @locked
238
+
239
+ unless @locked
240
+ total_count = @cleaner.failure.count
241
+ if total_count>@maximum
242
+ @start_index = total_count-@maximum
243
+ @jobs = all( @start_index, @maximum)
244
+ else
245
+ @start_index = 0
246
+ @jobs = all( 0, total_count)
247
+ end
248
+ end
249
+
250
+ @locked = true
251
+ yield
252
+ ensure
253
+ @locked = old
254
+ end
255
+ end
256
+
257
+ # Outputs message. Overrides this method when you want to change a output
258
+ # stream.
259
+ def log(msg)
260
+ puts msg if print?
261
+ end
262
+
263
+ def print?
264
+ @print_message
265
+ end
266
+
267
+ def too_many_message
268
+ "There are too many failed jobs(count=#{@failure.count}). This only looks at last #{@limiter.maximum} jobs."
269
+ end
270
+ end
271
+ end
272
+ end
273
+
274
+
275
+
@@ -0,0 +1,115 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize yes
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile ./test/redis-test.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 9736
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./test/
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################### ADVANCED CONFIG ###############################
111
+
112
+ # Glue small output buffers together in order to send small replies in a
113
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
+ # in terms of number of queries per second. Use 'yes' if unsure.
115
+ glueoutputbuf yes
@@ -0,0 +1,165 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require 'time'
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
+ setup do
18
+ Resque.redis.flushall
19
+
20
+ @worker = Resque::Worker.new(:jobs,:jobs2)
21
+
22
+ # 3 BadJob at 2009-03-13
23
+ create_and_process_jobs :jobs, @worker, 3, Time.parse('2009-03-13'), BadJob
24
+ # 2 BadJob by Jason at 2009-03-13
25
+ create_and_process_jobs :jobs2, @worker, 2, Time.parse('2009-03-13'), BadJob, "Jason"
26
+
27
+ # 1 BadJob by Johnson at 2009-03-13
28
+ create_and_process_jobs :jobs, @worker, 1, Time.parse('2009-03-13'), BadJob, "Johnson"
29
+
30
+ # 7 BadJob at 2009-11-13
31
+ create_and_process_jobs :jobs, @worker, 7, Time.parse('2009-11-13'), BadJobWithSyntaxError
32
+ # 7 BadJob by Freddy at 2009-11-13
33
+ create_and_process_jobs :jobs2, @worker, 7, Time.parse('2009-11-13'), BadJob, "Freddy"
34
+
35
+ # 11 BadJob at 2010-08-13
36
+ create_and_process_jobs :jobs, @worker, 11, Time.parse('2010-08-13'), BadJob
37
+ # 11 BadJob by Jason at 2010-08-13
38
+ create_and_process_jobs :jobs2, @worker, 11, Time.parse('2010-08-13'), BadJob, "Jason"
39
+
40
+ @cleaner = Resque::Plugins::ResqueCleaner.new
41
+ @cleaner.print_message = false
42
+ end
43
+
44
+ test "#select returns failure jobs" do
45
+ ret = @cleaner.select
46
+ assert_equal 42, ret.size
47
+ end
48
+
49
+ test "#select works with a limit" do
50
+ @cleaner.limiter.maximum = 10
51
+ ret = @cleaner.select
52
+
53
+ # only maximum number
54
+ assert_equal 10, ret.size
55
+
56
+ # latest one
57
+ assert_equal Time.parse(ret[0]['failed_at']), Time.parse('2010-08-13')
58
+ end
59
+
60
+ test "#select with a block returns failure jobs which the block evaluates true" do
61
+ ret = @cleaner.select {|job| job["payload"]["args"][0]=="Jason"}
62
+ assert_equal 13, ret.size
63
+ end
64
+
65
+ test "#clear deletes failure jobs" do
66
+ cleared = @cleaner.clear
67
+ assert_equal 42, cleared
68
+ assert_equal 0, @cleaner.select.size
69
+ end
70
+
71
+ test "#clear with a block deletes failure jobs which the block evaluates true" do
72
+ cleared = @cleaner.clear{|job| job["payload"]["args"][0]=="Jason"}
73
+ assert_equal 13, cleared
74
+ assert_equal 42-13, @cleaner.select.size
75
+ assert_equal 0, @cleaner.select{|job| job["payload"]["args"][0]=="Jason"}.size
76
+ end
77
+
78
+ test "#requeue retries failure jobs" do
79
+ assert_equal 0, queue_size(:jobs,:jobs2)
80
+ requeued = @cleaner.requeue
81
+ assert_equal 42, requeued
82
+ assert_equal 42, @cleaner.select.size # it doesn't clear jobs
83
+ assert_equal 42, queue_size(:jobs,:jobs2)
84
+ end
85
+
86
+ test "#requeue with a block retries failure jobs which the block evaluates true" do
87
+ requeued = @cleaner.requeue{|job| job["payload"]["args"][0]=="Jason"}
88
+ assert_equal 13, requeued
89
+ assert_equal 13, queue_size(:jobs,:jobs2)
90
+ end
91
+
92
+ test "#requeue with clear option requeues and deletes failure jobs" do
93
+ assert_equal 0, queue_size(:jobs,:jobs2)
94
+ requeued = @cleaner.requeue(true)
95
+ assert_equal 42, requeued
96
+ assert_equal 42, queue_size(:jobs,:jobs2)
97
+ assert_equal 0, @cleaner.select.size
98
+ end
99
+
100
+ test "#clear_stale deletes failure jobs which is queued before the last x enqueued" do
101
+ @cleaner.limiter.maximum = 10
102
+ @cleaner.clear_stale
103
+ assert_equal 10, @cleaner.failure.count
104
+ assert_equal Time.parse(@cleaner.failure_jobs[0]['failed_at']), Time.parse('2010-08-13')
105
+ end
106
+
107
+ test "#proc gives you handy proc definitions" do
108
+ # before 2009-04-01
109
+ ret = @cleaner.select &@cleaner.proc.before('2009-04-01')
110
+ assert_equal 6, ret.size
111
+
112
+ # after 2010-01-01
113
+ ret = @cleaner.select &@cleaner.proc.after('2010-01-01')
114
+ assert_equal 22, ret.size
115
+
116
+ # filter by class
117
+ ret = @cleaner.select &@cleaner.proc.klass(BadJobWithSyntaxError)
118
+ assert_equal 7, ret.size
119
+
120
+ # filter by queue
121
+ ret = @cleaner.select &@cleaner.proc.queue(:jobs2)
122
+ assert_equal 20, ret.size
123
+
124
+ # you can chain
125
+ ret = @cleaner.select &@cleaner.proc.queue(:jobs2).before('2009-12-01')
126
+ assert_equal 9, ret.size
127
+
128
+ # you can chain with your custom block
129
+ ret = @cleaner.select &@cleaner.proc{|j| j['payload']['args']==['Jason']}.queue(:jobs2)
130
+ assert_equal 13, ret.size
131
+ end
132
+
133
+ test "#stats_by_date returns stats grouped by date" do
134
+ ret = @cleaner.stats_by_date
135
+ assert_equal 6, ret['2009/03/13']
136
+ assert_equal 14, ret['2009/11/13']
137
+
138
+ # with block
139
+ ret = @cleaner.stats_by_date{|j| j['payload']['args']==['Jason']}
140
+ assert_equal 2, ret['2009/03/13']
141
+ assert_equal nil, ret['2009/11/13']
142
+ assert_equal 11, ret['2010/08/13']
143
+ end
144
+
145
+ test "#stats_by_class returns stats grouped by class" do
146
+ ret = @cleaner.stats_by_class
147
+ assert_equal 35, ret['BadJob']
148
+ assert_equal 7, ret['BadJobWithSyntaxError']
149
+ end
150
+
151
+ test "#lock ensures that a new failure job doesn't affect in a limit mode" do
152
+ @cleaner.limiter.maximum = 23
153
+ @cleaner.limiter.lock do
154
+ first = @cleaner.select[0]
155
+ assert_equal "Freddy", first["payload"]["args"][0]
156
+
157
+ create_and_process_jobs :jobs, @worker, 30, Time.parse('2010-10-10'), BadJob, "Jack"
158
+
159
+ first = @cleaner.select[0]
160
+ assert_equal "Freddy", first["payload"]["args"][0]
161
+ end
162
+ first = @cleaner.select[0]
163
+ assert_equal "Jack", first["payload"]["args"][0]
164
+ end
165
+ end
@@ -0,0 +1,122 @@
1
+ # Mostly copied from Resque in order to have similar test environment.
2
+ # https://github.com/defunkt/resque/blob/master/test/test_helper.rb
3
+
4
+ dir = File.dirname(File.expand_path(__FILE__))
5
+ $LOAD_PATH.unshift dir + '/../lib'
6
+ $TESTING = true
7
+ require 'test/unit'
8
+ require 'rubygems'
9
+ require 'resque'
10
+ require 'timecop'
11
+
12
+ begin
13
+ require 'leftright'
14
+ rescue LoadError
15
+ end
16
+ require 'resque'
17
+ require 'resque_cleaner'
18
+
19
+ #
20
+ # make sure we can run redis
21
+ #
22
+
23
+ if !system("which redis-server")
24
+ puts '', "** can't find `redis-server` in your path"
25
+ puts "** try running `sudo rake install`"
26
+ abort ''
27
+ end
28
+
29
+
30
+ #
31
+ # start our own redis when the tests start,
32
+ # kill it when they end
33
+ #
34
+
35
+ at_exit do
36
+ next if $!
37
+
38
+ if defined?(MiniTest)
39
+ exit_code = MiniTest::Unit.new.run(ARGV)
40
+ else
41
+ exit_code = Test::Unit::AutoRunner.run
42
+ 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
50
+
51
+ puts "Starting redis for testing at localhost:9736..."
52
+ `redis-server #{dir}/redis-test.conf`
53
+ Resque.redis = 'localhost:9736'
54
+
55
+
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
+ ##
77
+ # Helper to perform job classes
78
+ #
79
+ module PerformJob
80
+ def perform_job(klass, *args)
81
+ resque_job = Resque::Job.new(:testqueue, 'class' => klass, 'args' => args)
82
+ resque_job.perform
83
+ end
84
+ end
85
+
86
+ #
87
+ # fixture classes
88
+ #
89
+
90
+ class SomeJob
91
+ def self.perform(repo_id, path)
92
+ end
93
+ end
94
+
95
+ class SomeIvarJob < SomeJob
96
+ @queue = :ivar
97
+ end
98
+
99
+ class SomeMethodJob < SomeJob
100
+ def self.queue
101
+ :method
102
+ end
103
+ end
104
+
105
+ class BadJob
106
+ def self.perform(name=nil)
107
+ msg = name ? "Bad job, #{name}" : "Bad job!"
108
+ raise msg
109
+ end
110
+ end
111
+
112
+ class GoodJob
113
+ def self.perform(name)
114
+ "Good job, #{name}"
115
+ end
116
+ end
117
+
118
+ class BadJobWithSyntaxError
119
+ def self.perform
120
+ raise SyntaxError, "Extra Bad job!"
121
+ end
122
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque-cleaner
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Tatsuya Ono
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-18 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: resque
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 1
32
+ - 0
33
+ version: "1.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: " ResqueCleaner is a Resque plugin which helps you clean up failure jobs. It provides the following functionalities.\n\n * Filters failure jobs with an easy and extensible way\n * Retries failure jobs\n * Removes failure jobs\n * Shows stats\n\n"
37
+ email: ononoma@gmail.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - LICENSE
44
+ - README.markdown
45
+ files:
46
+ - README.markdown
47
+ - Rakefile
48
+ - LICENSE
49
+ - lib/resque_cleaner.rb
50
+ - test/redis-test.conf
51
+ - test/resque_cleaner_test.rb
52
+ - test/test_helper.rb
53
+ has_rdoc: true
54
+ homepage: http://github.com/ono/resque-cleaner
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: A Resque plugin cleaning up failure jobs.
87
+ test_files: []
88
+