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 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
@@ -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
+
@@ -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
- The `before_fork` hook will be run in the **parent** process. So, be
668
- careful - any changes you make will be permanent for the lifespan of
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
- All hooks can also be set using a setter, e.g.
660
+ For a list of available plugins see
661
+ <http://wiki.github.com/defunkt/resque/plugins>.
682
662
 
683
- Resque.after_fork = proc { puts "called" }
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
@@ -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
- args ? payload_class.perform(*args) : payload_class.perform
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.
@@ -20,7 +20,7 @@ module Resque
20
20
  end
21
21
 
22
22
  def current_page
23
- url request.path_info.sub('/','').downcase
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
- redirect u('failed')
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
  })
@@ -77,3 +77,5 @@ body { padding:0; margin:0; }
77
77
  #main p.pagination a.more { float:right;}
78
78
 
79
79
  #main form {float:right; margin-top:-10px;}
80
+
81
+ #main .time a.toggle_format {text-decoration:none;}
@@ -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
+
@@ -1,3 +1,3 @@
1
1
  module Resque
2
- Version = '1.6.1'
2
+ Version = '1.7.0'
3
3
  end
@@ -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
+
@@ -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.6.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-25 00:00:00 -07:00
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