resque 1.6.1 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of resque might be problematic. Click here for more details.
- data/HISTORY.md +7 -0
- data/HOOKS.md +120 -0
- data/README.markdown +20 -30
- data/Rakefile +5 -0
- data/lib/resque/job.rb +62 -1
- data/lib/resque/server.rb +6 -2
- data/lib/resque/server/public/ranger.js +50 -7
- data/lib/resque/server/public/style.css +2 -0
- data/lib/resque/server/views/failed.erb +2 -1
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +1 -1
- data/test/job_hooks_test.rb +302 -0
- data/test/test_helper.rb +9 -0
- metadata +5 -2
data/HISTORY.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 1.7.0 (2010-03-31)
|
2
|
+
|
3
|
+
* Job hooks API. See HOOKS.md.
|
4
|
+
* web: Hovering over dates shows a timestamp
|
5
|
+
* web: AJAXify retry action for failed jobs
|
6
|
+
* web bugfix: Fix pagination bug
|
7
|
+
|
1
8
|
## 1.6.1 (2010-03-25)
|
2
9
|
|
3
10
|
* Bugfix: Workers may not be clearing their state correctly on
|
data/HOOKS.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
Resque Hooks
|
2
|
+
============
|
3
|
+
|
4
|
+
You can customize Resque or write plugins using its hook API. In many
|
5
|
+
cases you can use a hook rather than mess with Resque's internals.
|
6
|
+
|
7
|
+
For a list of available plugins see
|
8
|
+
<http://wiki.github.com/defunkt/resque/plugins>.
|
9
|
+
|
10
|
+
|
11
|
+
Worker Hooks
|
12
|
+
------------
|
13
|
+
|
14
|
+
If you wish to have a Proc called before the worker forks for the
|
15
|
+
first time, you can add it in the initializer like so:
|
16
|
+
|
17
|
+
Resque.before_first_fork do
|
18
|
+
puts "Call me once before the worker forks the first time"
|
19
|
+
end
|
20
|
+
|
21
|
+
You can also run a hook before _every_ fork:
|
22
|
+
|
23
|
+
Resque.before_fork do |job|
|
24
|
+
puts "Call me before the worker forks"
|
25
|
+
end
|
26
|
+
|
27
|
+
The `before_fork` hook will be run in the **parent** process. So, be
|
28
|
+
careful - any changes you make will be permanent for the lifespan of
|
29
|
+
the worker.
|
30
|
+
|
31
|
+
And after forking:
|
32
|
+
|
33
|
+
Resque.after_fork do |job|
|
34
|
+
puts "Call me after the worker forks"
|
35
|
+
end
|
36
|
+
|
37
|
+
The `after_fork` hook will be run in the child process and is passed
|
38
|
+
the current job. Any changes you make, therefor, will only live as
|
39
|
+
long as the job currently being processes.
|
40
|
+
|
41
|
+
All worker hooks can also be set using a setter, e.g.
|
42
|
+
|
43
|
+
Resque.after_fork = proc { puts "called" }
|
44
|
+
|
45
|
+
|
46
|
+
Job Hooks
|
47
|
+
---------
|
48
|
+
|
49
|
+
Plugins can utilize job hooks to provide additional behavior. A job
|
50
|
+
hook is a method name in the following format:
|
51
|
+
|
52
|
+
HOOKNAME_IDENTIFIER
|
53
|
+
|
54
|
+
For example, a `before_perform` hook which adds locking may be defined
|
55
|
+
like this:
|
56
|
+
|
57
|
+
def before_perform_with_lock(*args)
|
58
|
+
set_lock!
|
59
|
+
end
|
60
|
+
|
61
|
+
Once this hook is made available to your job (either by way of
|
62
|
+
inheritence or `extend`), it will be run before the job's `perform`
|
63
|
+
method is called.
|
64
|
+
|
65
|
+
The available hooks are:
|
66
|
+
|
67
|
+
* `before_perform`: Called with the job args before perform. If it raises
|
68
|
+
`Resque::Job::DontPerform`, the job is aborted. If other exceptions
|
69
|
+
are raised, they will be propagated up the the `Resque::Failure`
|
70
|
+
backend.
|
71
|
+
|
72
|
+
* `after_perform`: Called with the job args after it performs. Uncaught
|
73
|
+
exceptions will propagate up to the `Resque::Failure` backend.
|
74
|
+
|
75
|
+
* `around_perform`: Called with the job args. It is expected to yield in order
|
76
|
+
to perform the job (but is not required to do so). It may handle exceptions
|
77
|
+
thrown by `perform`, but any that are not caught will propagate up to the
|
78
|
+
`Resque::Failure` backend.
|
79
|
+
|
80
|
+
* `on_failure`: Called with the exception and job args if any exception occurs
|
81
|
+
while performing the job (or hooks).
|
82
|
+
|
83
|
+
Hooks are easily implemented with superclasses or modules. A superclass could
|
84
|
+
look something like this.
|
85
|
+
|
86
|
+
class LoggedJob
|
87
|
+
def self.before_perform_log_job(*args)
|
88
|
+
Logger.info "About to perform #{self} with #{args.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class MyJob < LoggedJob
|
93
|
+
def self.perform(*args)
|
94
|
+
...
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
Modules are even better because jobs can use many of them.
|
99
|
+
|
100
|
+
module LoggedJob
|
101
|
+
def before_perform_log_job(*args)
|
102
|
+
Logger.info "About to perform #{self} with #{args.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
module RetriedJob
|
107
|
+
def on_failure_retry(e, *args)
|
108
|
+
Logger.info "Performing #{self} caused an exception (#{e}). Retrying..."
|
109
|
+
Resque.enqueue self, *args
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class MyJob
|
114
|
+
extend LoggedJob
|
115
|
+
extend RetriedJob
|
116
|
+
def self.perform(*args)
|
117
|
+
...
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
data/README.markdown
CHANGED
@@ -485,6 +485,8 @@ tool that's best for your app.
|
|
485
485
|
Installing Redis
|
486
486
|
----------------
|
487
487
|
|
488
|
+
Resque requires Redis 0.900 or higher.
|
489
|
+
|
488
490
|
Resque uses Redis' lists for its queues. It also stores worker state
|
489
491
|
data in Redis.
|
490
492
|
|
@@ -529,6 +531,9 @@ Resque Dependencies
|
|
529
531
|
If you cannot install `yajl-ruby` (JRuby?), you can install the `json`
|
530
532
|
gem and Resque will use it instead.
|
531
533
|
|
534
|
+
When problems arise, make sure you have the newest versions of the
|
535
|
+
`redis` and `redis-namespace` gems.
|
536
|
+
|
532
537
|
|
533
538
|
Installing Resque
|
534
539
|
-----------------
|
@@ -648,39 +653,16 @@ this way we can tell our Sinatra app about the config file:
|
|
648
653
|
|
649
654
|
Now everyone is on the same page.
|
650
655
|
|
651
|
-
Worker Hooks
|
652
|
-
------------
|
653
|
-
|
654
|
-
If you wish to have a Proc called before the worker forks for the
|
655
|
-
first time, you can add it in the initializer like so:
|
656
|
-
|
657
|
-
Resque.before_first_fork do
|
658
|
-
puts "CALL ME ONCE BEFORE THE WORKER FORKS THE FIRST TIME"
|
659
|
-
end
|
660
|
-
|
661
|
-
You can also run a hook before _every_ fork:
|
662
|
-
|
663
|
-
Resque.before_fork do |job|
|
664
|
-
puts "CALL ME BEFORE THE WORKER FORKS"
|
665
|
-
end
|
666
656
|
|
667
|
-
|
668
|
-
|
669
|
-
the worker.
|
670
|
-
|
671
|
-
And after forking:
|
672
|
-
|
673
|
-
Resque.after_fork do |job|
|
674
|
-
puts "CALL ME AFTER THE WORKER FORKS"
|
675
|
-
end
|
676
|
-
|
677
|
-
The `after_fork` hook will be run in the child process and is passed
|
678
|
-
the current job. Any changes you make, therefor, will only live as
|
679
|
-
long as the job currently being processes.
|
657
|
+
Plugins and Hooks
|
658
|
+
-----------------
|
680
659
|
|
681
|
-
|
660
|
+
For a list of available plugins see
|
661
|
+
<http://wiki.github.com/defunkt/resque/plugins>.
|
682
662
|
|
683
|
-
|
663
|
+
If you'd like to write your own plugin, or want to customize Resque
|
664
|
+
using hooks (such as `Resque.after_fork`), see
|
665
|
+
[HOOKS.md](http://github.com/defunkt/resque/blob/master/HOOKS.md).
|
684
666
|
|
685
667
|
|
686
668
|
Namespaces
|
@@ -714,10 +696,18 @@ Try it out by looking at the README, found at `examples/demo/README.markdown`.
|
|
714
696
|
Monitoring
|
715
697
|
----------
|
716
698
|
|
699
|
+
### god
|
700
|
+
|
717
701
|
If you're using god to monitor Resque, we have provided example
|
718
702
|
configs in `examples/god/`. One is for starting / stopping workers,
|
719
703
|
the other is for killing workers that have been running too long.
|
720
704
|
|
705
|
+
### monit
|
706
|
+
|
707
|
+
If you're using monit, `examples/monit/resque.monit` is provided free
|
708
|
+
of charge. This is **not** used by GitHub in production, so please
|
709
|
+
send patches for any tweaks or improvements you can make to it.
|
710
|
+
|
721
711
|
|
722
712
|
Development
|
723
713
|
-----------
|
data/Rakefile
CHANGED
@@ -7,6 +7,11 @@ task :default => :test
|
|
7
7
|
|
8
8
|
desc "Run tests"
|
9
9
|
task :test do
|
10
|
+
begin
|
11
|
+
require 'redgreen'
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
10
15
|
# Don't use the rake/testtask because it loads a new
|
11
16
|
# Ruby interpreter - we want to run tests with the current
|
12
17
|
# `rake` so our library manager still works
|
data/lib/resque/job.rb
CHANGED
@@ -15,6 +15,10 @@ module Resque
|
|
15
15
|
include Helpers
|
16
16
|
extend Helpers
|
17
17
|
|
18
|
+
# Raise Resque::Job::DontPerform from a before_perform hook to
|
19
|
+
# abort the job.
|
20
|
+
DontPerform = Class.new(StandardError)
|
21
|
+
|
18
22
|
# The worker object which is currently processing this job.
|
19
23
|
attr_accessor :worker
|
20
24
|
|
@@ -101,7 +105,64 @@ module Resque
|
|
101
105
|
# Calls #perform on the class given in the payload with the
|
102
106
|
# arguments given in the payload.
|
103
107
|
def perform
|
104
|
-
|
108
|
+
job = payload_class
|
109
|
+
job_args = args || []
|
110
|
+
job_was_performed = false
|
111
|
+
|
112
|
+
before_hooks = job.methods.grep(/^before_perform/)
|
113
|
+
around_hooks = job.methods.grep(/^around_perform/)
|
114
|
+
after_hooks = job.methods.grep(/^after_perform/)
|
115
|
+
failure_hooks = job.methods.grep(/^on_failure/)
|
116
|
+
|
117
|
+
begin
|
118
|
+
# Execute before_perform hook. Abort the job gracefully if
|
119
|
+
# Resque::DontPerform is raised.
|
120
|
+
begin
|
121
|
+
before_hooks.each do |hook|
|
122
|
+
job.send(hook, *job_args)
|
123
|
+
end
|
124
|
+
rescue DontPerform
|
125
|
+
return false
|
126
|
+
end
|
127
|
+
|
128
|
+
# Execute the job. Do it in an around_perform hook if available.
|
129
|
+
if around_hooks.empty?
|
130
|
+
job.perform(*job_args)
|
131
|
+
job_was_performed = true
|
132
|
+
else
|
133
|
+
# We want to nest all around_perform plugins, with the last one
|
134
|
+
# finally calling perform
|
135
|
+
stack = around_hooks.inject(nil) do |last_hook, hook|
|
136
|
+
if last_hook
|
137
|
+
lambda do
|
138
|
+
job.send(hook, *job_args) { last_hook.call }
|
139
|
+
end
|
140
|
+
else
|
141
|
+
lambda do
|
142
|
+
job.send(hook, *job_args) do
|
143
|
+
job.perform(*job_args)
|
144
|
+
job_was_performed = true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
stack.call
|
150
|
+
end
|
151
|
+
|
152
|
+
# Execute after_perform hook
|
153
|
+
after_hooks.each do |hook|
|
154
|
+
job.send(hook, *job_args)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Return true if the job was performed
|
158
|
+
return job_was_performed
|
159
|
+
|
160
|
+
# If an exception occurs during the job execution, look for an
|
161
|
+
# on_failure hook then re-raise.
|
162
|
+
rescue Object => e
|
163
|
+
failure_hooks.each { |hook| job.send(hook, e, *job_args) }
|
164
|
+
raise e
|
165
|
+
end
|
105
166
|
end
|
106
167
|
|
107
168
|
# Returns the actual class constant represented in this job's payload.
|
data/lib/resque/server.rb
CHANGED
@@ -20,7 +20,7 @@ module Resque
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def current_page
|
23
|
-
url request.path_info.sub('/','')
|
23
|
+
url request.path_info.sub('/','')
|
24
24
|
end
|
25
25
|
|
26
26
|
def url(*path_parts)
|
@@ -153,7 +153,11 @@ module Resque
|
|
153
153
|
|
154
154
|
get "/failed/requeue/:index" do
|
155
155
|
Resque::Failure.requeue(params[:index])
|
156
|
-
|
156
|
+
if request.xhr?
|
157
|
+
return Resque::Failure.all(params[:index])['retried_at']
|
158
|
+
else
|
159
|
+
redirect u('failed')
|
160
|
+
end
|
157
161
|
end
|
158
162
|
|
159
163
|
get "/stats" do
|
@@ -1,24 +1,67 @@
|
|
1
|
-
var poll_interval = 2;
|
2
|
-
|
3
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);
|
4
32
|
|
5
|
-
$('.time').relatizeDate()
|
6
33
|
$('.backtrace').click(function() {
|
7
34
|
$(this).next().toggle()
|
8
35
|
return false
|
9
36
|
})
|
10
|
-
|
37
|
+
|
11
38
|
$('a[rel=poll]').click(function() {
|
12
39
|
var href = $(this).attr('href')
|
13
40
|
$(this).parent().text('Starting...')
|
14
41
|
$("#main").addClass('polling')
|
42
|
+
|
15
43
|
setInterval(function() {
|
16
|
-
$.ajax({dataType:'text', type:'get', url:href, success:function(data) {
|
17
|
-
$('#main').html(data)
|
44
|
+
$.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
|
45
|
+
$('#main').html(data)
|
18
46
|
$('#main .time').relatizeDate()
|
19
47
|
}})
|
20
48
|
}, poll_interval * 1000)
|
49
|
+
|
21
50
|
return false
|
22
51
|
})
|
23
|
-
|
52
|
+
|
53
|
+
$('ul.failed a[rel=retry]').click(function() {
|
54
|
+
var href = $(this).attr('href');
|
55
|
+
$(this).text('Retrying...');
|
56
|
+
var parent = $(this).parent();
|
57
|
+
$.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
|
58
|
+
parent.html('Retried <b><span class="time">' + data + '</span></b>');
|
59
|
+
relatizer.apply($('.time', parent));
|
60
|
+
$('.date_time', parent).hide();
|
61
|
+
$('a.toggle_format span', parent).click(format_toggler);
|
62
|
+
}});
|
63
|
+
return false;
|
64
|
+
})
|
65
|
+
|
66
|
+
|
24
67
|
})
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<% if job['retried_at'] %>
|
24
24
|
Retried <b><span class="time"><%= job['retried_at'] %></span></b>
|
25
25
|
<% else %>
|
26
|
-
<a href="<%= u "failed/requeue/#{start + index - 1}" %>">Retry</a>
|
26
|
+
<a href="<%= u "failed/requeue/#{start + index - 1}" %>" rel="retry">Retry</a>
|
27
27
|
<% end %>
|
28
28
|
</div>
|
29
29
|
</dd>
|
@@ -46,3 +46,4 @@
|
|
46
46
|
</ul>
|
47
47
|
|
48
48
|
<%= partial :next_more, :start => start, :size => size %>
|
49
|
+
|
data/lib/resque/version.rb
CHANGED
data/lib/resque/worker.rb
CHANGED
@@ -143,6 +143,7 @@ module Resque
|
|
143
143
|
return unless job ||= reserve
|
144
144
|
|
145
145
|
begin
|
146
|
+
job.worker = self
|
146
147
|
run_hook :after_fork, job
|
147
148
|
working_on job
|
148
149
|
job.perform
|
@@ -332,7 +333,6 @@ module Resque
|
|
332
333
|
# Given a job, tells Redis we're working on it. Useful for seeing
|
333
334
|
# what workers are doing and when.
|
334
335
|
def working_on(job)
|
335
|
-
job.worker = self
|
336
336
|
data = encode \
|
337
337
|
:queue => job.queue,
|
338
338
|
:run_at => Time.now.to_s,
|
@@ -0,0 +1,302 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
context "Resque::Job before_perform" do
|
4
|
+
include PerformJob
|
5
|
+
|
6
|
+
class BeforePerformJob
|
7
|
+
def self.before_perform_record_history(history)
|
8
|
+
history << :before_perform
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.perform(history)
|
12
|
+
history << :perform
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test "it runs before_perform before perform" do
|
17
|
+
result = perform_job(BeforePerformJob, history=[])
|
18
|
+
assert_equal true, result, "perform returned true"
|
19
|
+
assert_equal history, [:before_perform, :perform]
|
20
|
+
end
|
21
|
+
|
22
|
+
class BeforePerformJobFails
|
23
|
+
def self.before_perform_fail_job(history)
|
24
|
+
history << :before_perform
|
25
|
+
raise StandardError
|
26
|
+
end
|
27
|
+
def self.perform(history)
|
28
|
+
history << :perform
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
test "raises an error and does not perform if before_perform fails" do
|
33
|
+
history = []
|
34
|
+
assert_raises StandardError do
|
35
|
+
perform_job(BeforePerformJobFails, history)
|
36
|
+
end
|
37
|
+
assert_equal history, [:before_perform], "Only before_perform was run"
|
38
|
+
end
|
39
|
+
|
40
|
+
class BeforePerformJobAborts
|
41
|
+
def self.before_perform_abort(history)
|
42
|
+
history << :before_perform
|
43
|
+
raise Resque::Job::DontPerform
|
44
|
+
end
|
45
|
+
def self.perform(history)
|
46
|
+
history << :perform
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
test "does not perform if before_perform raises Resque::Job::DontPerform" do
|
51
|
+
result = perform_job(BeforePerformJobAborts, history=[])
|
52
|
+
assert_equal false, result, "perform returned false"
|
53
|
+
assert_equal history, [:before_perform], "Only before_perform was run"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "Resque::Job after_perform" do
|
58
|
+
include PerformJob
|
59
|
+
|
60
|
+
class AfterPerformJob
|
61
|
+
def self.perform(history)
|
62
|
+
history << :perform
|
63
|
+
end
|
64
|
+
def self.after_perform_record_history(history)
|
65
|
+
history << :after_perform
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
test "it runs after_perform after perform" do
|
70
|
+
result = perform_job(AfterPerformJob, history=[])
|
71
|
+
assert_equal true, result, "perform returned true"
|
72
|
+
assert_equal history, [:perform, :after_perform]
|
73
|
+
end
|
74
|
+
|
75
|
+
class AfterPerformJobFails
|
76
|
+
def self.perform(history)
|
77
|
+
history << :perform
|
78
|
+
end
|
79
|
+
def self.after_perform_fail_job(history)
|
80
|
+
history << :after_perform
|
81
|
+
raise StandardError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
test "raises an error but has already performed if after_perform fails" do
|
86
|
+
history = []
|
87
|
+
assert_raises StandardError do
|
88
|
+
perform_job(AfterPerformJobFails, history)
|
89
|
+
end
|
90
|
+
assert_equal history, [:perform, :after_perform], "Only after_perform was run"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "Resque::Job around_perform" do
|
95
|
+
include PerformJob
|
96
|
+
|
97
|
+
class AroundPerformJob
|
98
|
+
def self.perform(history)
|
99
|
+
history << :perform
|
100
|
+
end
|
101
|
+
def self.around_perform_record_history(history)
|
102
|
+
history << :start_around_perform
|
103
|
+
yield
|
104
|
+
history << :finish_around_perform
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
test "it runs around_perform then yields in order to perform" do
|
109
|
+
result = perform_job(AroundPerformJob, history=[])
|
110
|
+
assert_equal true, result, "perform returned true"
|
111
|
+
assert_equal history, [:start_around_perform, :perform, :finish_around_perform]
|
112
|
+
end
|
113
|
+
|
114
|
+
class AroundPerformJobFailsBeforePerforming
|
115
|
+
def self.perform(history)
|
116
|
+
history << :perform
|
117
|
+
end
|
118
|
+
def self.around_perform_fail(history)
|
119
|
+
history << :start_around_perform
|
120
|
+
raise StandardError
|
121
|
+
yield
|
122
|
+
history << :finish_around_perform
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
test "raises an error and does not perform if around_perform fails before yielding" do
|
127
|
+
history = []
|
128
|
+
assert_raises StandardError do
|
129
|
+
perform_job(AroundPerformJobFailsBeforePerforming, history)
|
130
|
+
end
|
131
|
+
assert_equal history, [:start_around_perform], "Only part of around_perform was run"
|
132
|
+
end
|
133
|
+
|
134
|
+
class AroundPerformJobFailsWhilePerforming
|
135
|
+
def self.perform(history)
|
136
|
+
history << :perform
|
137
|
+
raise StandardError
|
138
|
+
end
|
139
|
+
def self.around_perform_fail_in_yield(history)
|
140
|
+
history << :start_around_perform
|
141
|
+
begin
|
142
|
+
yield
|
143
|
+
ensure
|
144
|
+
history << :ensure_around_perform
|
145
|
+
end
|
146
|
+
history << :finish_around_perform
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
test "raises an error but may handle exceptions if perform fails" do
|
151
|
+
history = []
|
152
|
+
assert_raises StandardError do
|
153
|
+
perform_job(AroundPerformJobFailsWhilePerforming, history)
|
154
|
+
end
|
155
|
+
assert_equal history, [:start_around_perform, :perform, :ensure_around_perform], "Only part of around_perform was run"
|
156
|
+
end
|
157
|
+
|
158
|
+
class AroundPerformJobDoesNotHaveToYield
|
159
|
+
def self.perform(history)
|
160
|
+
history << :perform
|
161
|
+
end
|
162
|
+
def self.around_perform_dont_yield(history)
|
163
|
+
history << :start_around_perform
|
164
|
+
history << :finish_around_perform
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
test "around_perform is not required to yield" do
|
169
|
+
history = []
|
170
|
+
result = perform_job(AroundPerformJobDoesNotHaveToYield, history)
|
171
|
+
assert_equal false, result, "perform returns false"
|
172
|
+
assert_equal history, [:start_around_perform, :finish_around_perform], "perform was not run"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context "Resque::Job on_failure" do
|
177
|
+
include PerformJob
|
178
|
+
|
179
|
+
class FailureJobThatDoesNotFail
|
180
|
+
def self.perform(history)
|
181
|
+
history << :perform
|
182
|
+
end
|
183
|
+
def self.on_failure_record_failure(exception, history)
|
184
|
+
history << exception.message
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
test "it does not call on_failure if no failures occur" do
|
189
|
+
result = perform_job(FailureJobThatDoesNotFail, history=[])
|
190
|
+
assert_equal true, result, "perform returned true"
|
191
|
+
assert_equal history, [:perform]
|
192
|
+
end
|
193
|
+
|
194
|
+
class FailureJobThatFails
|
195
|
+
def self.perform(history)
|
196
|
+
history << :perform
|
197
|
+
raise StandardError, "oh no"
|
198
|
+
end
|
199
|
+
def self.on_failure_record_failure(exception, history)
|
200
|
+
history << exception.message
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
test "it calls on_failure with the exception and then re-raises the exception" do
|
205
|
+
history = []
|
206
|
+
assert_raises StandardError do
|
207
|
+
perform_job(FailureJobThatFails, history)
|
208
|
+
end
|
209
|
+
assert_equal history, [:perform, "oh no"]
|
210
|
+
end
|
211
|
+
|
212
|
+
class FailureJobThatFailsBadly
|
213
|
+
def self.perform(history)
|
214
|
+
history << :perform
|
215
|
+
raise SyntaxError, "oh no"
|
216
|
+
end
|
217
|
+
def self.on_failure_record_failure(exception, history)
|
218
|
+
history << exception.message
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
test "it calls on_failure even with bad exceptions" do
|
223
|
+
history = []
|
224
|
+
assert_raises SyntaxError do
|
225
|
+
perform_job(FailureJobThatFailsBadly, history)
|
226
|
+
end
|
227
|
+
assert_equal history, [:perform, "oh no"]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "Resque::Job all hooks" do
|
232
|
+
include PerformJob
|
233
|
+
|
234
|
+
class VeryHookyJob
|
235
|
+
def self.before_perform_record_history(history)
|
236
|
+
history << :before_perform
|
237
|
+
end
|
238
|
+
def self.around_perform_record_history(history)
|
239
|
+
history << :start_around_perform
|
240
|
+
yield
|
241
|
+
history << :finish_around_perform
|
242
|
+
end
|
243
|
+
def self.perform(history)
|
244
|
+
history << :perform
|
245
|
+
end
|
246
|
+
def self.after_perform_record_history(history)
|
247
|
+
history << :after_perform
|
248
|
+
end
|
249
|
+
def self.on_failure_record_history(exception, history)
|
250
|
+
history << exception.message
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
test "the complete hook order" do
|
255
|
+
result = perform_job(VeryHookyJob, history=[])
|
256
|
+
assert_equal true, result, "perform returned true"
|
257
|
+
assert_equal history, [
|
258
|
+
:before_perform,
|
259
|
+
:start_around_perform,
|
260
|
+
:perform,
|
261
|
+
:finish_around_perform,
|
262
|
+
:after_perform
|
263
|
+
]
|
264
|
+
end
|
265
|
+
|
266
|
+
class VeryHookyJobThatFails
|
267
|
+
def self.before_perform_record_history(history)
|
268
|
+
history << :before_perform
|
269
|
+
end
|
270
|
+
def self.around_perform_record_history(history)
|
271
|
+
history << :start_around_perform
|
272
|
+
yield
|
273
|
+
history << :finish_around_perform
|
274
|
+
end
|
275
|
+
def self.perform(history)
|
276
|
+
history << :perform
|
277
|
+
end
|
278
|
+
def self.after_perform_record_history(history)
|
279
|
+
history << :after_perform
|
280
|
+
raise StandardError, "oh no"
|
281
|
+
end
|
282
|
+
def self.on_failure_record_history(exception, history)
|
283
|
+
history << exception.message
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
test "the complete hook order with a failure at the last minute" do
|
288
|
+
history = []
|
289
|
+
assert_raises StandardError do
|
290
|
+
perform_job(VeryHookyJobThatFails, history)
|
291
|
+
end
|
292
|
+
assert_equal history, [
|
293
|
+
:before_perform,
|
294
|
+
:start_around_perform,
|
295
|
+
:perform,
|
296
|
+
:finish_around_perform,
|
297
|
+
:after_perform,
|
298
|
+
"oh no"
|
299
|
+
]
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
data/test/test_helper.rb
CHANGED
@@ -63,6 +63,15 @@ def context(*args, &block)
|
|
63
63
|
klass.class_eval &block
|
64
64
|
end
|
65
65
|
|
66
|
+
##
|
67
|
+
# Helper to perform job classes
|
68
|
+
#
|
69
|
+
module PerformJob
|
70
|
+
def perform_job(klass, *args)
|
71
|
+
resque_job = Resque::Job.new(:testqueue, 'class' => klass, 'args' => args)
|
72
|
+
resque_job.perform
|
73
|
+
end
|
74
|
+
end
|
66
75
|
|
67
76
|
#
|
68
77
|
# fixture classes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Wanstrath
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-03-
|
12
|
+
date: 2010-03-31 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- .kick
|
78
78
|
- CONTRIBUTORS
|
79
79
|
- HISTORY.md
|
80
|
+
- HOOKS.md
|
80
81
|
- LICENSE
|
81
82
|
- README.markdown
|
82
83
|
- Rakefile
|
@@ -132,6 +133,7 @@ files:
|
|
132
133
|
- lib/resque/worker.rb
|
133
134
|
- tasks/redis.rake
|
134
135
|
- tasks/resque.rake
|
136
|
+
- test/job_hooks_test.rb
|
135
137
|
- test/redis-test.conf
|
136
138
|
- test/resque-web_test.rb
|
137
139
|
- test/resque_test.rb
|
@@ -166,6 +168,7 @@ signing_key:
|
|
166
168
|
specification_version: 3
|
167
169
|
summary: ""
|
168
170
|
test_files:
|
171
|
+
- test/job_hooks_test.rb
|
169
172
|
- test/resque-web_test.rb
|
170
173
|
- test/resque_test.rb
|
171
174
|
- test/test_helper.rb
|