wireframe-resque_unit 0.4.1.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/README.md +130 -0
- data/lib/resque_unit.rb +13 -0
- data/lib/resque_unit/assertions.rb +81 -0
- data/lib/resque_unit/errors.rb +17 -0
- data/lib/resque_unit/helpers.rb +57 -0
- data/lib/resque_unit/plugin.rb +70 -0
- data/lib/resque_unit/resque.rb +224 -0
- data/lib/resque_unit/scheduler.rb +41 -0
- data/lib/resque_unit/scheduler_assertions.rb +49 -0
- data/lib/resque_unit_scheduler.rb +4 -0
- data/test/resque_test.rb +61 -0
- data/test/resque_unit_scheduler_test.rb +201 -0
- data/test/resque_unit_test.rb +460 -0
- data/test/sample_jobs.rb +167 -0
- data/test/test_helper.rb +9 -0
- metadata +96 -0
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
ResqueUnit
|
2
|
+
==========
|
3
|
+
|
4
|
+
ResqueUnit provides some extra assertions and a mock Resque for
|
5
|
+
testing Rails code that depends on Resque. You can install it as
|
6
|
+
either a gem or a plugin:
|
7
|
+
|
8
|
+
gem install resque_unit
|
9
|
+
|
10
|
+
and in your test.rb:
|
11
|
+
|
12
|
+
config.gem 'resque_unit'
|
13
|
+
|
14
|
+
If you'd rather install it as a plugin, you should be able to run
|
15
|
+
|
16
|
+
script/plugin install git://github.com/justinweiss/resque_unit.git
|
17
|
+
|
18
|
+
inside your Rails projects.
|
19
|
+
|
20
|
+
Examples
|
21
|
+
========
|
22
|
+
|
23
|
+
ResqueUnit provides some extra assertions for your unit tests. For
|
24
|
+
example, if you have code that queues a resque job:
|
25
|
+
|
26
|
+
class MyJob
|
27
|
+
@queue = :low
|
28
|
+
|
29
|
+
def self.perform(x)
|
30
|
+
# do stuff
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def queue_job
|
35
|
+
Resque.enqueue(MyJob, 1)
|
36
|
+
end
|
37
|
+
|
38
|
+
You can write a unit test for code that queues this job:
|
39
|
+
|
40
|
+
def test_job_queued
|
41
|
+
queue_job
|
42
|
+
assert_queued(MyJob) # assert that MyJob was queued in the :low queue
|
43
|
+
end
|
44
|
+
|
45
|
+
You can also verify that a job was queued with arguments:
|
46
|
+
|
47
|
+
def test_job_queued_with_arguments
|
48
|
+
queue_job
|
49
|
+
assert_queued(MyJob, [1])
|
50
|
+
end
|
51
|
+
|
52
|
+
And you can run all the jobs in the queue, so you can verify that they
|
53
|
+
run correctly:
|
54
|
+
|
55
|
+
def test_job_runs
|
56
|
+
queue_job
|
57
|
+
Resque.run!
|
58
|
+
assert stuff_was_done, "Job didn't run"
|
59
|
+
end
|
60
|
+
|
61
|
+
You can also access the queues directly:
|
62
|
+
|
63
|
+
def test_jobs_in_queue
|
64
|
+
queue_job
|
65
|
+
assert_equal 1, Resque.queue(:low).length
|
66
|
+
end
|
67
|
+
|
68
|
+
Finally, you can enable hooks:
|
69
|
+
|
70
|
+
Resque.enable_hooks!
|
71
|
+
|
72
|
+
class MyJobWithHooks
|
73
|
+
@queue = :hooked
|
74
|
+
|
75
|
+
def self.perform(x)
|
76
|
+
# do stuff
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.after_enqueue_mark(*args)
|
80
|
+
# called when the job is enqueued
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.before_perform_mark(*args)
|
84
|
+
# called just before the +perform+ method
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.after_perform_mark(*args)
|
88
|
+
# called just after the +perform+ method
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.failure_perform_mark(*args)
|
92
|
+
# called if the +perform+ method raised
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
def queue_job
|
98
|
+
Resque.enqueue(MyJobWithHooks, 1)
|
99
|
+
end
|
100
|
+
|
101
|
+
Caveats
|
102
|
+
=======
|
103
|
+
|
104
|
+
* You should make sure that you call `Resque.reset!` in your test's
|
105
|
+
setup method to clear all of the test queues.
|
106
|
+
* Hooks support is optional. Just because you probably don't want to call
|
107
|
+
them during unit tests if they play with a DB. Call `Resque.enable_hooks!`
|
108
|
+
in your tests's setup method to enable hooks. To disable hooks, call
|
109
|
+
`Resque.disable_hooks!`.
|
110
|
+
|
111
|
+
Resque-Scheduler Support
|
112
|
+
========================
|
113
|
+
|
114
|
+
By calling `require 'resque_unit_scheduler'`, ResqueUnit will provide
|
115
|
+
mocks for [resque-scheduler's](http://github.com/bvandenbos/resque-scheduler)
|
116
|
+
`enqueue_at` and `enqueue_in` methods, along with a few extra
|
117
|
+
assertions. These are used like this:
|
118
|
+
|
119
|
+
Resque.enqueue_in(600, MediumPriorityJob) # enqueues MediumPriorityJob in 600 seconds
|
120
|
+
assert_queued_in(600, MediumPriorityJob) # will pass
|
121
|
+
assert_not_queued_in(300, MediumPriorityJob) # will also pass
|
122
|
+
|
123
|
+
Resque.enqueue_at(Time.now + 10, MediumPriorityJob) # enqueues MediumPriorityJob at 10 seconds from now
|
124
|
+
assert_queued_at(Time.now + 10, MediumPriorityJob) # will pass
|
125
|
+
assert_not_queued_at(Time.now + 1, MediumPriorityJob) # will also pass
|
126
|
+
|
127
|
+
For now, `assert_queued` and `assert_not_queued` will pass for any
|
128
|
+
scheduled job. `Resque.run!` will run all scheduled jobs as well.
|
129
|
+
|
130
|
+
Copyright (c) 2010 Justin Weiss, released under the MIT license
|
data/lib/resque_unit.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# These are a group of assertions you can use in your unit tests to
|
2
|
+
# verify that your code is using Resque correctly.
|
3
|
+
module ResqueUnit::Assertions
|
4
|
+
|
5
|
+
# Asserts that +klass+ has been queued into its appropriate queue at
|
6
|
+
# least once. If +args+ is nil, it only asserts that the klass has
|
7
|
+
# been queued. Otherwise, it asserts that the klass has been queued
|
8
|
+
# with the correct arguments. Pass an empty array for +args+ if you
|
9
|
+
# want to assert that klass has been queued without arguments. Pass a block
|
10
|
+
# if you want to assert something was queued within its execution.
|
11
|
+
def assert_queued(klass, args = nil, message = nil, &block)
|
12
|
+
queue_name = Resque.queue_for(klass)
|
13
|
+
assert_job_created(queue_name, klass, args, message, &block)
|
14
|
+
end
|
15
|
+
alias assert_queues assert_queued
|
16
|
+
|
17
|
+
# The opposite of +assert_queued+.
|
18
|
+
def assert_not_queued(klass = nil, args = nil, message = nil, &block)
|
19
|
+
queue_name = Resque.queue_for(klass)
|
20
|
+
|
21
|
+
queue = if block_given?
|
22
|
+
snapshot = Resque.size(queue_name)
|
23
|
+
yield
|
24
|
+
Resque.all(queue_name)[snapshot..-1]
|
25
|
+
else
|
26
|
+
Resque.all(queue_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
assert_with_custom_message(!in_queue?(queue, klass, args),
|
30
|
+
message || "#{klass}#{args ? " with #{args.inspect}" : ""} should not have been queued in #{queue_name}.")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Asserts no jobs were queued within the block passed.
|
34
|
+
def assert_nothing_queued(message = nil, &block)
|
35
|
+
snapshot = Resque.size
|
36
|
+
yield
|
37
|
+
present = Resque.size
|
38
|
+
assert_equal snapshot, present, message || "No jobs should have been queued"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Asserts that a job was created and queued into the specified queue
|
42
|
+
def assert_job_created(queue_name, klass, args = nil, message = nil, &block)
|
43
|
+
queue = if block_given?
|
44
|
+
snapshot = Resque.size(queue_name)
|
45
|
+
yield
|
46
|
+
Resque.all(queue_name)[snapshot..-1]
|
47
|
+
else
|
48
|
+
Resque.all(queue_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
assert_with_custom_message(in_queue?(queue, klass, args),
|
52
|
+
message || "#{klass}#{args ? " with #{args.inspect}" : ""} should have been queued in #{queue_name}: #{queue.inspect}.")
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# In Test::Unit, +assert_block+ displays only the message on a test
|
58
|
+
# failure and +assert+ always appends a message to the end of the
|
59
|
+
# passed-in assertion message. In MiniTest, it's the other way
|
60
|
+
# around. This abstracts those differences and never appends a
|
61
|
+
# message to the one the user passed in.
|
62
|
+
def assert_with_custom_message(value, message = nil)
|
63
|
+
if defined?(MiniTest::Assertions)
|
64
|
+
assert value, message
|
65
|
+
else
|
66
|
+
assert_block message do
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def in_queue?(queue, klass, args = nil)
|
73
|
+
!matching_jobs(queue, klass, args).empty?
|
74
|
+
end
|
75
|
+
|
76
|
+
def matching_jobs(queue, klass, args = nil)
|
77
|
+
normalized_args = Resque.decode(Resque.encode(args)) if args
|
78
|
+
queue.select {|e| e["class"] == klass.to_s && (!args || e["args"] == normalized_args )}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Re-define errors in from Resque, in case the 'resque' gem was not loaded.
|
2
|
+
module Resque
|
3
|
+
# Raised whenever we need a queue but none is provided.
|
4
|
+
unless defined?(NoQueueError)
|
5
|
+
class NoQueueError < RuntimeError; end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Raised when trying to create a job without a class
|
9
|
+
unless defined?(NoClassError)
|
10
|
+
class NoClassError < RuntimeError; end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Raised when a worker was killed while processing a job.
|
14
|
+
unless defined?(DirtyExit)
|
15
|
+
class DirtyExit < RuntimeError; end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Resque
|
2
|
+
module Helpers
|
3
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
4
|
+
# queue.
|
5
|
+
def encode(object)
|
6
|
+
if defined? Yajl
|
7
|
+
Yajl::Encoder.encode(object)
|
8
|
+
else
|
9
|
+
object.to_json
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Given a string, returns a Ruby object.
|
14
|
+
def decode(object)
|
15
|
+
return unless object
|
16
|
+
|
17
|
+
if defined? Yajl
|
18
|
+
begin
|
19
|
+
Yajl::Parser.parse(object, :check_utf8 => false)
|
20
|
+
rescue Yajl::ParseError
|
21
|
+
end
|
22
|
+
else
|
23
|
+
begin
|
24
|
+
JSON.parse(object)
|
25
|
+
rescue JSON::ParserError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Given a word with dashes, returns a camel cased version of it.
|
31
|
+
#
|
32
|
+
# classify('job-name') # => 'JobName'
|
33
|
+
def classify(dashed_word)
|
34
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
35
|
+
end
|
36
|
+
|
37
|
+
# Given a camel cased word, returns the constant it represents
|
38
|
+
#
|
39
|
+
# constantize('JobName') # => JobName
|
40
|
+
def constantize(camel_cased_word)
|
41
|
+
camel_cased_word = camel_cased_word.to_s
|
42
|
+
|
43
|
+
if camel_cased_word.include?('-')
|
44
|
+
camel_cased_word = classify(camel_cased_word)
|
45
|
+
end
|
46
|
+
|
47
|
+
names = camel_cased_word.split('::')
|
48
|
+
names.shift if names.empty? || names.first.empty?
|
49
|
+
|
50
|
+
constant = Object
|
51
|
+
names.each do |name|
|
52
|
+
constant = constant.const_get(name) || constant.const_missing(name)
|
53
|
+
end
|
54
|
+
constant
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# A copy of the original Resque:Plugin class from the "resque" gem
|
2
|
+
# No need to redefine this class if the resque gem was already loaded
|
3
|
+
unless defined?(Resque::Plugin)
|
4
|
+
|
5
|
+
module Resque
|
6
|
+
module Plugin
|
7
|
+
extend self
|
8
|
+
|
9
|
+
LintError = Class.new(RuntimeError)
|
10
|
+
|
11
|
+
# Ensure that your plugin conforms to good hook naming conventions.
|
12
|
+
#
|
13
|
+
# Resque::Plugin.lint(MyResquePlugin)
|
14
|
+
def lint(plugin)
|
15
|
+
hooks = before_hooks(plugin) + around_hooks(plugin) + after_hooks(plugin)
|
16
|
+
|
17
|
+
hooks.each do |hook|
|
18
|
+
if hook =~ /perform$/
|
19
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
failure_hooks(plugin).each do |hook|
|
24
|
+
if hook =~ /failure$/
|
25
|
+
raise LintError, "#{plugin}.#{hook} is not namespaced"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Given an object, returns a list `before_perform` hook names.
|
31
|
+
def before_hooks(job)
|
32
|
+
job.methods.grep(/^before_perform/).sort
|
33
|
+
end
|
34
|
+
|
35
|
+
# Given an object, returns a list `around_perform` hook names.
|
36
|
+
def around_hooks(job)
|
37
|
+
job.methods.grep(/^around_perform/).sort
|
38
|
+
end
|
39
|
+
|
40
|
+
# Given an object, returns a list `after_perform` hook names.
|
41
|
+
def after_hooks(job)
|
42
|
+
job.methods.grep(/^after_perform/).sort
|
43
|
+
end
|
44
|
+
|
45
|
+
# Given an object, returns a list `on_failure` hook names.
|
46
|
+
def failure_hooks(job)
|
47
|
+
job.methods.grep(/^on_failure/).sort
|
48
|
+
end
|
49
|
+
|
50
|
+
# Given an object, returns a list `after_enqueue` hook names.
|
51
|
+
def after_enqueue_hooks(job)
|
52
|
+
job.methods.grep(/^after_enqueue/).sort
|
53
|
+
end
|
54
|
+
|
55
|
+
# Given an object, returns a list `before_enqueue` hook names.
|
56
|
+
def before_enqueue_hooks(job)
|
57
|
+
job.methods.grep(/^before_enqueue/).sort
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
unless defined?(Resque::Job::DontPerform)
|
65
|
+
module Resque
|
66
|
+
class Job
|
67
|
+
DontPerform = Class.new(StandardError)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# The fake Resque class. This needs to be loaded after the real Resque
|
2
|
+
# for the assertions in +ResqueUnit::Assertions+ to work.
|
3
|
+
module Resque
|
4
|
+
include Helpers
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Resets all the queues to the empty state. This should be called in
|
8
|
+
# your test's +setup+ method until I can figure out a way for it to
|
9
|
+
# automatically be called.
|
10
|
+
#
|
11
|
+
# If <tt>queue_name</tt> is given, then resets only that queue.
|
12
|
+
def reset!(queue_name = nil)
|
13
|
+
if @queue && queue_name
|
14
|
+
@queue[queue_name] = []
|
15
|
+
else
|
16
|
+
@queue = Hash.new { |h, k| h[k] = [] }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a hash of all the queue names and jobs that have been queued. The
|
21
|
+
# format is <tt>{queue_name => [job, ..]}</tt>.
|
22
|
+
def self.queues
|
23
|
+
@queue || reset!
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns an array of all the jobs that have been queued. Each
|
27
|
+
# element is of the form +{"class" => klass, "args" => args}+ where
|
28
|
+
# +klass+ is the job's class and +args+ is an array of the arguments
|
29
|
+
# passed to the job.
|
30
|
+
def queue(queue_name)
|
31
|
+
queues[queue_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return an array of all jobs' payloads for queue
|
35
|
+
# Elements are decoded
|
36
|
+
def all(queue_name)
|
37
|
+
result = list_range(queue_name, 0, size(queue_name))
|
38
|
+
result.is_a?(Array) ? result : [ result ]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns an array of jobs' payloads for queue.
|
42
|
+
#
|
43
|
+
# start and count should be integer and can be used for pagination.
|
44
|
+
# start is the item to begin, count is how many items to return.
|
45
|
+
#
|
46
|
+
# To get the 3rd page of a 30 item, paginatied list one would use:
|
47
|
+
# Resque.peek('my_list', 59, 30)
|
48
|
+
def peek(queue_name, start = 0, count = 1)
|
49
|
+
list_range(queue_name, start, count)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Gets a range of jobs' payloads from queue.
|
53
|
+
# Returns single element if count equal 1
|
54
|
+
# Elements are decoded
|
55
|
+
def list_range(key, start = 0, count = 1)
|
56
|
+
data = if count == 1
|
57
|
+
decode(queues[key][start])
|
58
|
+
else
|
59
|
+
(queues[key][start...start + count] || []).map { |entry| decode(entry) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Yes, all Resque hooks!
|
64
|
+
def enable_hooks!
|
65
|
+
@hooks_enabled = true
|
66
|
+
end
|
67
|
+
|
68
|
+
def disable_hooks!
|
69
|
+
@hooks_enabled = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
# Executes all jobs in all queues in an undefined order.
|
73
|
+
def run!
|
74
|
+
payloads = []
|
75
|
+
@queue.each do |queue_name, queue|
|
76
|
+
payloads.concat queue.slice!(0, queue.size)
|
77
|
+
end
|
78
|
+
exec_payloads payloads.shuffle
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_for!(queue_name, limit=false)
|
82
|
+
queue = @queue[queue_name]
|
83
|
+
exec_payloads queue.slice!(0, ( limit ? limit : queue.size) ).shuffle
|
84
|
+
end
|
85
|
+
|
86
|
+
def exec_payloads(raw_payloads)
|
87
|
+
raw_payloads.each do |raw_payload|
|
88
|
+
job_payload = decode(raw_payload)
|
89
|
+
@hooks_enabled ? perform_with_hooks(job_payload) : perform_without_hooks(job_payload)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
private :exec_payloads
|
93
|
+
|
94
|
+
# 1. Execute all jobs in all queues in an undefined order,
|
95
|
+
# 2. Check if new jobs were announced, and execute them.
|
96
|
+
# 3. Repeat 3
|
97
|
+
def full_run!
|
98
|
+
run! until empty_queues?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the size of the given queue
|
102
|
+
def size(queue_name = nil)
|
103
|
+
if queue_name
|
104
|
+
queues[queue_name].length
|
105
|
+
else
|
106
|
+
queues.values.flatten.length
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# :nodoc:
|
111
|
+
def enqueue(klass, *args)
|
112
|
+
enqueue_to( queue_for(klass), klass, *args)
|
113
|
+
end
|
114
|
+
|
115
|
+
# :nodoc:
|
116
|
+
def enqueue_to( queue_name, klass, *args )
|
117
|
+
# Behaves like Resque, raise if no queue was specifed
|
118
|
+
raise NoQueueError.new("Jobs must be placed onto a queue.") unless queue_name
|
119
|
+
enqueue_unit(queue_name, {"class" => klass.name, "args" => args })
|
120
|
+
end
|
121
|
+
|
122
|
+
# :nodoc:
|
123
|
+
def queue_for(klass)
|
124
|
+
klass.instance_variable_get(:@queue) || (klass.respond_to?(:queue) && klass.queue)
|
125
|
+
end
|
126
|
+
alias :queue_from_class :queue_for
|
127
|
+
|
128
|
+
# :nodoc:
|
129
|
+
def empty_queues?
|
130
|
+
queues.all? do |k, v|
|
131
|
+
v.empty?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def enqueue_unit(queue_name, hash)
|
136
|
+
klass = constantize(hash["class"])
|
137
|
+
if @hooks_enabled
|
138
|
+
before_hooks = Plugin.before_enqueue_hooks(klass).map do |hook|
|
139
|
+
klass.send(hook, *hash["args"])
|
140
|
+
end
|
141
|
+
return nil if before_hooks.any? { |result| result == false }
|
142
|
+
end
|
143
|
+
queue(queue_name) << encode(hash)
|
144
|
+
if @hooks_enabled
|
145
|
+
Plugin.after_enqueue_hooks(klass).each do |hook|
|
146
|
+
klass.send(hook, *hash["args"])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
queue(queue_name).size
|
150
|
+
end
|
151
|
+
|
152
|
+
# Call perform on the job class
|
153
|
+
def perform_without_hooks(job_payload)
|
154
|
+
constantize(job_payload["class"]).perform(*job_payload["args"])
|
155
|
+
end
|
156
|
+
|
157
|
+
# Call perform on the job class, and adds support for Resque hooks.
|
158
|
+
def perform_with_hooks(job_payload)
|
159
|
+
job_class = constantize(job_payload["class"])
|
160
|
+
before_hooks = Resque::Plugin.before_hooks(job_class)
|
161
|
+
around_hooks = Resque::Plugin.around_hooks(job_class)
|
162
|
+
after_hooks = Resque::Plugin.after_hooks(job_class)
|
163
|
+
failure_hooks = Resque::Plugin.failure_hooks(job_class)
|
164
|
+
|
165
|
+
begin
|
166
|
+
# Execute before_perform hook. Abort the job gracefully if
|
167
|
+
# Resque::DontPerform is raised.
|
168
|
+
begin
|
169
|
+
before_hooks.each do |hook|
|
170
|
+
job_class.send(hook, *job_payload["args"])
|
171
|
+
end
|
172
|
+
rescue Resque::Job::DontPerform
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
|
176
|
+
# Execute the job. Do it in an around_perform hook if available.
|
177
|
+
if around_hooks.empty?
|
178
|
+
perform_without_hooks(job_payload)
|
179
|
+
job_was_performed = true
|
180
|
+
else
|
181
|
+
# We want to nest all around_perform plugins, with the last one
|
182
|
+
# finally calling perform
|
183
|
+
stack = around_hooks.reverse.inject(nil) do |last_hook, hook|
|
184
|
+
if last_hook
|
185
|
+
lambda do
|
186
|
+
job_class.send(hook, *job_payload["args"]) { last_hook.call }
|
187
|
+
end
|
188
|
+
else
|
189
|
+
lambda do
|
190
|
+
job_class.send(hook, *job_payload["args"]) do
|
191
|
+
result = perform_without_hooks(job_payload)
|
192
|
+
job_was_performed = true
|
193
|
+
result
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
stack.call
|
199
|
+
end
|
200
|
+
|
201
|
+
# Execute after_perform hook
|
202
|
+
after_hooks.each do |hook|
|
203
|
+
job_class.send(hook, *job_payload["args"])
|
204
|
+
end
|
205
|
+
|
206
|
+
# Return true if the job was performed
|
207
|
+
return job_was_performed
|
208
|
+
|
209
|
+
# If an exception occurs during the job execution, look for an
|
210
|
+
# on_failure hook then re-raise.
|
211
|
+
rescue Object => e
|
212
|
+
failure_hooks.each { |hook| job_class.send(hook, e, *job_payload["args"]) }
|
213
|
+
raise e
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class Job
|
218
|
+
extend Helpers
|
219
|
+
def self.create(queue, klass_name, *args)
|
220
|
+
Resque.enqueue_unit(queue, {"class" => constantize(klass_name), "args" => args})
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|