opengotham_resque 1.8.2
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/.gitignore +2 -0
- data/.kick +26 -0
- data/HISTORY.md +142 -0
- data/LICENSE +20 -0
- data/README.markdown +794 -0
- data/Rakefile +112 -0
- data/bin/resque +57 -0
- data/bin/resque-web +23 -0
- data/config.ru +14 -0
- data/deps.rip +7 -0
- data/docs/HOOKS.md +121 -0
- data/docs/PLUGINS.md +93 -0
- data/examples/async_helper.rb +31 -0
- data/examples/demo/README.markdown +71 -0
- data/examples/demo/Rakefile +8 -0
- data/examples/demo/app.rb +38 -0
- data/examples/demo/config.ru +19 -0
- data/examples/demo/job.rb +22 -0
- data/examples/god/resque.god +53 -0
- data/examples/god/stale.god +26 -0
- data/examples/instance.rb +11 -0
- data/examples/monit/resque.monit +6 -0
- data/examples/simple.rb +30 -0
- data/init.rb +1 -0
- data/lib/resque.rb +287 -0
- data/lib/resque/errors.rb +10 -0
- data/lib/resque/failure.rb +66 -0
- data/lib/resque/failure/base.rb +61 -0
- data/lib/resque/failure/hoptoad.rb +132 -0
- data/lib/resque/failure/multiple.rb +48 -0
- data/lib/resque/failure/redis.rb +40 -0
- data/lib/resque/helpers.rb +63 -0
- data/lib/resque/job.rb +207 -0
- data/lib/resque/plugin.rb +46 -0
- data/lib/resque/server.rb +201 -0
- data/lib/resque/server/public/idle.png +0 -0
- data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
- data/lib/resque/server/public/jquery.relatize_date.js +95 -0
- data/lib/resque/server/public/poll.png +0 -0
- data/lib/resque/server/public/ranger.js +67 -0
- data/lib/resque/server/public/reset.css +48 -0
- data/lib/resque/server/public/style.css +81 -0
- data/lib/resque/server/public/working.png +0 -0
- data/lib/resque/server/test_helper.rb +19 -0
- data/lib/resque/server/views/error.erb +1 -0
- data/lib/resque/server/views/failed.erb +53 -0
- data/lib/resque/server/views/key_sets.erb +20 -0
- data/lib/resque/server/views/key_string.erb +11 -0
- data/lib/resque/server/views/layout.erb +44 -0
- data/lib/resque/server/views/next_more.erb +10 -0
- data/lib/resque/server/views/overview.erb +4 -0
- data/lib/resque/server/views/queues.erb +49 -0
- data/lib/resque/server/views/stats.erb +62 -0
- data/lib/resque/server/views/workers.erb +78 -0
- data/lib/resque/server/views/working.erb +69 -0
- data/lib/resque/stat.rb +53 -0
- data/lib/resque/tasks.rb +39 -0
- data/lib/resque/version.rb +3 -0
- data/lib/resque/worker.rb +478 -0
- data/tasks/redis.rake +159 -0
- data/tasks/resque.rake +2 -0
- data/test/job_hooks_test.rb +302 -0
- data/test/job_plugins_test.rb +209 -0
- data/test/plugin_test.rb +116 -0
- data/test/redis-test.conf +132 -0
- data/test/resque-web_test.rb +54 -0
- data/test/resque_test.rb +225 -0
- data/test/test_helper.rb +111 -0
- data/test/worker_test.rb +302 -0
- metadata +199 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
module Resque
|
2
|
+
# Raised whenever we need a queue but none is provided.
|
3
|
+
class NoQueueError < RuntimeError; end
|
4
|
+
|
5
|
+
# Raised when trying to create a job without a class
|
6
|
+
class NoClassError < RuntimeError; end
|
7
|
+
|
8
|
+
# Raised when a worker was killed while processing a job.
|
9
|
+
class DirtyExit < RuntimeError; end
|
10
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Resque
|
2
|
+
# The Failure module provides an interface for working with different
|
3
|
+
# failure backends.
|
4
|
+
#
|
5
|
+
# You can use it to query the failure backend without knowing which specific
|
6
|
+
# backend is being used. For instance, the Resque web app uses it to display
|
7
|
+
# stats and other information.
|
8
|
+
module Failure
|
9
|
+
# Creates a new failure, which is delegated to the appropriate backend.
|
10
|
+
#
|
11
|
+
# Expects a hash with the following keys:
|
12
|
+
# :exception - The Exception object
|
13
|
+
# :worker - The Worker object who is reporting the failure
|
14
|
+
# :queue - The string name of the queue from which the job was pulled
|
15
|
+
# :payload - The job's payload
|
16
|
+
def self.create(options = {})
|
17
|
+
backend.new(*options.values_at(:exception, :worker, :queue, :payload)).save
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# Sets the current backend. Expects a class descendent of
|
22
|
+
# `Resque::Failure::Base`.
|
23
|
+
#
|
24
|
+
# Example use:
|
25
|
+
# require 'resque/failure/hoptoad'
|
26
|
+
# Resque::Failure.backend = Resque::Failure::Hoptoad
|
27
|
+
def self.backend=(backend)
|
28
|
+
@backend = backend
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the current backend class. If none has been set, falls
|
32
|
+
# back to `Resque::Failure::Redis`
|
33
|
+
def self.backend
|
34
|
+
return @backend if @backend
|
35
|
+
require 'resque/failure/redis'
|
36
|
+
@backend = Failure::Redis
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the int count of how many failures we have seen.
|
40
|
+
def self.count
|
41
|
+
backend.count
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns an array of all the failures, paginated.
|
45
|
+
#
|
46
|
+
# `start` is the int of the first item in the page, `count` is the
|
47
|
+
# number of items to return.
|
48
|
+
def self.all(start = 0, count = 1)
|
49
|
+
backend.all(start, count)
|
50
|
+
end
|
51
|
+
|
52
|
+
# The string url of the backend's web interface, if any.
|
53
|
+
def self.url
|
54
|
+
backend.url
|
55
|
+
end
|
56
|
+
|
57
|
+
# Clear all failure jobs
|
58
|
+
def self.clear
|
59
|
+
backend.clear
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.requeue(index)
|
63
|
+
backend.requeue(index)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Resque
|
2
|
+
module Failure
|
3
|
+
# All Failure classes are expected to subclass Base.
|
4
|
+
#
|
5
|
+
# When a job fails, a new instance of your Failure backend is created
|
6
|
+
# and #save is called.
|
7
|
+
class Base
|
8
|
+
# The exception object raised by the failed job
|
9
|
+
attr_accessor :exception
|
10
|
+
|
11
|
+
# The worker object who detected the failure
|
12
|
+
attr_accessor :worker
|
13
|
+
|
14
|
+
# The string name of the queue from which the failed job was pulled
|
15
|
+
attr_accessor :queue
|
16
|
+
|
17
|
+
# The payload object associated with the failed job
|
18
|
+
attr_accessor :payload
|
19
|
+
|
20
|
+
def initialize(exception, worker, queue, payload)
|
21
|
+
@exception = exception
|
22
|
+
@worker = worker
|
23
|
+
@queue = queue
|
24
|
+
@payload = payload
|
25
|
+
end
|
26
|
+
|
27
|
+
# When a job fails, a new instance of your Failure backend is created
|
28
|
+
# and #save is called.
|
29
|
+
#
|
30
|
+
# This is where you POST or PUT or whatever to your Failure service.
|
31
|
+
def save
|
32
|
+
end
|
33
|
+
|
34
|
+
# The number of failures.
|
35
|
+
def self.count
|
36
|
+
0
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a paginated array of failure objects.
|
40
|
+
def self.all(start = 0, count = 1)
|
41
|
+
[]
|
42
|
+
end
|
43
|
+
|
44
|
+
# A URL where someone can go to view failures.
|
45
|
+
def self.url
|
46
|
+
end
|
47
|
+
|
48
|
+
# Clear all failure objects
|
49
|
+
def self.clear
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.requeue(index)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Logging!
|
56
|
+
def log(message)
|
57
|
+
@worker.log(message)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'builder'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Resque
|
6
|
+
module Failure
|
7
|
+
# A Failure backend that sends exceptions raised by jobs to Hoptoad.
|
8
|
+
#
|
9
|
+
# To use it, put this code in an initializer, Rake task, or wherever:
|
10
|
+
#
|
11
|
+
# require 'resque/failure/hoptoad'
|
12
|
+
#
|
13
|
+
# Resque::Failure::Hoptoad.configure do |config|
|
14
|
+
# config.api_key = 'blah'
|
15
|
+
# config.secure = true
|
16
|
+
#
|
17
|
+
# # optional proxy support
|
18
|
+
# config.proxy_host = 'x.y.z.t'
|
19
|
+
# config.proxy_port = 8080
|
20
|
+
#
|
21
|
+
# # server env support, defaults to RAILS_ENV or RACK_ENV
|
22
|
+
# config.server_environment = "test"
|
23
|
+
# end
|
24
|
+
class Hoptoad < Base
|
25
|
+
# From the hoptoad plugin
|
26
|
+
INPUT_FORMAT = /^([^:]+):(\d+)(?::in `([^']+)')?$/
|
27
|
+
|
28
|
+
class << self
|
29
|
+
attr_accessor :secure, :api_key, :proxy_host, :proxy_port
|
30
|
+
attr_accessor :server_environment
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.count
|
34
|
+
# We can't get the total # of errors from Hoptoad so we fake it
|
35
|
+
# by asking Resque how many errors it has seen.
|
36
|
+
Stat[:failed]
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.configure
|
40
|
+
yield self
|
41
|
+
Resque::Failure.backend = self
|
42
|
+
end
|
43
|
+
|
44
|
+
def save
|
45
|
+
http = use_ssl? ? :https : :http
|
46
|
+
url = URI.parse("#{http}://hoptoadapp.com/notifier_api/v2/notices")
|
47
|
+
|
48
|
+
request = Net::HTTP::Proxy(self.class.proxy_host, self.class.proxy_port)
|
49
|
+
http = request.new(url.host, url.port)
|
50
|
+
headers = {
|
51
|
+
'Content-type' => 'text/xml',
|
52
|
+
'Accept' => 'text/xml, application/xml'
|
53
|
+
}
|
54
|
+
|
55
|
+
http.read_timeout = 5 # seconds
|
56
|
+
http.open_timeout = 2 # seconds
|
57
|
+
|
58
|
+
http.use_ssl = use_ssl?
|
59
|
+
|
60
|
+
begin
|
61
|
+
response = http.post(url.path, xml, headers)
|
62
|
+
rescue TimeoutError => e
|
63
|
+
log "Timeout while contacting the Hoptoad server."
|
64
|
+
end
|
65
|
+
|
66
|
+
case response
|
67
|
+
when Net::HTTPSuccess then
|
68
|
+
log "Hoptoad Success: #{response.class}"
|
69
|
+
else
|
70
|
+
body = response.body if response.respond_to? :body
|
71
|
+
log "Hoptoad Failure: #{response.class}\n#{body}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def xml
|
76
|
+
x = Builder::XmlMarkup.new
|
77
|
+
x.instruct!
|
78
|
+
x.notice :version=>"2.0" do
|
79
|
+
x.tag! "api-key", api_key
|
80
|
+
x.notifier do
|
81
|
+
x.name "Resqueue"
|
82
|
+
x.version "0.1"
|
83
|
+
x.url "http://github.com/defunkt/resque"
|
84
|
+
end
|
85
|
+
x.error do
|
86
|
+
x.class exception.class.name
|
87
|
+
x.message "#{exception.class.name}: #{exception.message}"
|
88
|
+
x.backtrace do
|
89
|
+
fill_in_backtrace_lines(x)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
x.request do
|
93
|
+
x.url queue.to_s
|
94
|
+
x.component worker.to_s
|
95
|
+
x.params do
|
96
|
+
x.var :key=>"payload_class" do
|
97
|
+
x.text! payload["class"].to_s
|
98
|
+
end
|
99
|
+
x.var :key=>"payload_args" do
|
100
|
+
x.text! payload["args"].to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
x.tag!("server-environment") do
|
105
|
+
x.tag!("environment-name",server_environment)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def fill_in_backtrace_lines(x)
|
112
|
+
exception.backtrace.each do |unparsed_line|
|
113
|
+
_, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
|
114
|
+
x.line :file => file,:number => number
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def use_ssl?
|
119
|
+
self.class.secure
|
120
|
+
end
|
121
|
+
|
122
|
+
def api_key
|
123
|
+
self.class.api_key
|
124
|
+
end
|
125
|
+
|
126
|
+
def server_environment
|
127
|
+
return self.class.server_environment if self.class.server_environment
|
128
|
+
defined?(RAILS_ENV) ? RAILS_ENV : (ENV['RACK_ENV'] || 'development')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Resque
|
2
|
+
module Failure
|
3
|
+
# A Failure backend that uses multiple backends
|
4
|
+
# delegates all queries to the first backend
|
5
|
+
class Multiple < Base
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :classes
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configure
|
12
|
+
yield self
|
13
|
+
Resque::Failure.backend = self
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
@backends = self.class.classes.map {|klass| klass.new(*args)}
|
18
|
+
end
|
19
|
+
def save
|
20
|
+
@backends.each(&:save)
|
21
|
+
end
|
22
|
+
|
23
|
+
# The number of failures.
|
24
|
+
def self.count
|
25
|
+
classes.first.count
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a paginated array of failure objects.
|
29
|
+
def self.all(start = 0, count = 1)
|
30
|
+
classes.first.all(start,count)
|
31
|
+
end
|
32
|
+
|
33
|
+
# A URL where someone can go to view failures.
|
34
|
+
def self.url
|
35
|
+
classes.first.url
|
36
|
+
end
|
37
|
+
|
38
|
+
# Clear all failure objects
|
39
|
+
def self.clear
|
40
|
+
classes.first.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.requeue(*args)
|
44
|
+
classes.first.requeue(*args)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Resque
|
2
|
+
module Failure
|
3
|
+
# A Failure backend that stores exceptions in Redis. Very simple but
|
4
|
+
# works out of the box, along with support in the Resque web app.
|
5
|
+
class Redis < Base
|
6
|
+
def save
|
7
|
+
data = {
|
8
|
+
:failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S"),
|
9
|
+
:payload => payload,
|
10
|
+
:exception => exception.class.to_s,
|
11
|
+
:error => exception.to_s,
|
12
|
+
:backtrace => exception.backtrace,
|
13
|
+
:worker => worker.to_s,
|
14
|
+
:queue => queue
|
15
|
+
}
|
16
|
+
data = Resque.encode(data)
|
17
|
+
Resque.redis.rpush(:failed, data)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.count
|
21
|
+
Resque.redis.llen(:failed).to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.all(start = 0, count = 1)
|
25
|
+
Resque.list_range(:failed, start, count)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.clear
|
29
|
+
Resque.redis.del(:failed)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.requeue(index)
|
33
|
+
item = all(index)
|
34
|
+
item['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
|
35
|
+
Resque.redis.lset(:failed, index, Resque.encode(item))
|
36
|
+
Job.create(item['queue'], item['payload']['class'], *item['payload']['args'])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Resque
|
2
|
+
# Methods used by various classes in Resque.
|
3
|
+
module Helpers
|
4
|
+
# Direct access to the Redis instance.
|
5
|
+
def redis
|
6
|
+
Resque.redis
|
7
|
+
end
|
8
|
+
|
9
|
+
# Given a Ruby object, returns a string suitable for storage in a
|
10
|
+
# queue.
|
11
|
+
def encode(object)
|
12
|
+
if defined? Yajl
|
13
|
+
Yajl::Encoder.encode(object)
|
14
|
+
else
|
15
|
+
object.to_json
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Given a string, returns a Ruby object.
|
20
|
+
def decode(object)
|
21
|
+
return unless object
|
22
|
+
|
23
|
+
if defined? Yajl
|
24
|
+
begin
|
25
|
+
Yajl::Parser.parse(object, :check_utf8 => false)
|
26
|
+
rescue Yajl::ParseError
|
27
|
+
end
|
28
|
+
else
|
29
|
+
begin
|
30
|
+
JSON.parse(object)
|
31
|
+
rescue JSON::ParserError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Given a word with dashes, returns a camel cased version of it.
|
37
|
+
#
|
38
|
+
# classify('job-name') # => 'JobName'
|
39
|
+
def classify(dashed_word)
|
40
|
+
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
41
|
+
end
|
42
|
+
|
43
|
+
# Given a camel cased word, returns the constant it represents
|
44
|
+
#
|
45
|
+
# constantize('JobName') # => JobName
|
46
|
+
def constantize(camel_cased_word)
|
47
|
+
camel_cased_word = camel_cased_word.to_s
|
48
|
+
|
49
|
+
if camel_cased_word.include?('-')
|
50
|
+
camel_cased_word = classify(camel_cased_word)
|
51
|
+
end
|
52
|
+
|
53
|
+
names = camel_cased_word.split('::')
|
54
|
+
names.shift if names.empty? || names.first.empty?
|
55
|
+
|
56
|
+
constant = Object
|
57
|
+
names.each do |name|
|
58
|
+
constant = constant.const_get(name) || constant.const_missing(name)
|
59
|
+
end
|
60
|
+
constant
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/resque/job.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
module Resque
|
2
|
+
# A Resque::Job represents a unit of work. Each job lives on a
|
3
|
+
# single queue and has an associated payload object. The payload
|
4
|
+
# is a hash with two attributes: `class` and `args`. The `class` is
|
5
|
+
# the name of the Ruby class which should be used to run the
|
6
|
+
# job. The `args` are an array of arguments which should be passed
|
7
|
+
# to the Ruby class's `perform` class-level method.
|
8
|
+
#
|
9
|
+
# You can manually run a job using this code:
|
10
|
+
#
|
11
|
+
# job = Resque::Job.reserve(:high)
|
12
|
+
# klass = Resque::Job.constantize(job.payload['class'])
|
13
|
+
# klass.perform(*job.payload['args'])
|
14
|
+
class Job
|
15
|
+
include Helpers
|
16
|
+
extend Helpers
|
17
|
+
|
18
|
+
# Raise Resque::Job::DontPerform from a before_perform hook to
|
19
|
+
# abort the job.
|
20
|
+
DontPerform = Class.new(StandardError)
|
21
|
+
|
22
|
+
# The worker object which is currently processing this job.
|
23
|
+
attr_accessor :worker
|
24
|
+
|
25
|
+
# The name of the queue from which this job was pulled (or is to be
|
26
|
+
# placed)
|
27
|
+
attr_reader :queue
|
28
|
+
|
29
|
+
# This job's associated payload object.
|
30
|
+
attr_reader :payload
|
31
|
+
|
32
|
+
def initialize(queue, payload)
|
33
|
+
@queue = queue
|
34
|
+
@payload = payload
|
35
|
+
end
|
36
|
+
|
37
|
+
# Creates a job by placing it on a queue. Expects a string queue
|
38
|
+
# name, a string class name, and an optional array of arguments to
|
39
|
+
# pass to the class' `perform` method.
|
40
|
+
#
|
41
|
+
# Raises an exception if no queue or class is given.
|
42
|
+
def self.create(queue, klass, *args)
|
43
|
+
if !queue
|
44
|
+
raise NoQueueError.new("Jobs must be placed onto a queue.")
|
45
|
+
end
|
46
|
+
|
47
|
+
if klass.to_s.empty?
|
48
|
+
raise NoClassError.new("Jobs must be given a class.")
|
49
|
+
end
|
50
|
+
|
51
|
+
Resque.push(queue, :class => klass.to_s, :args => args)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Removes a job from a queue. Expects a string queue name, a
|
55
|
+
# string class name, and, optionally, args.
|
56
|
+
#
|
57
|
+
# Returns the number of jobs destroyed.
|
58
|
+
#
|
59
|
+
# If no args are provided, it will remove all jobs of the class
|
60
|
+
# provided.
|
61
|
+
#
|
62
|
+
# That is, for these two jobs:
|
63
|
+
#
|
64
|
+
# { 'class' => 'UpdateGraph', 'args' => ['defunkt'] }
|
65
|
+
# { 'class' => 'UpdateGraph', 'args' => ['mojombo'] }
|
66
|
+
#
|
67
|
+
# The following call will remove both:
|
68
|
+
#
|
69
|
+
# Resque::Job.destroy(queue, 'UpdateGraph')
|
70
|
+
#
|
71
|
+
# Whereas specifying args will only remove the 2nd job:
|
72
|
+
#
|
73
|
+
# Resque::Job.destroy(queue, 'UpdateGraph', 'mojombo')
|
74
|
+
#
|
75
|
+
# This method can be potentially very slow and memory intensive,
|
76
|
+
# depending on the size of your queue, as it loads all jobs into
|
77
|
+
# a Ruby array before processing.
|
78
|
+
def self.destroy(queue, klass, *args)
|
79
|
+
klass = klass.to_s
|
80
|
+
queue = "queue:#{queue}"
|
81
|
+
destroyed = 0
|
82
|
+
|
83
|
+
redis.lrange(queue, 0, -1).each do |string|
|
84
|
+
json = decode(string)
|
85
|
+
|
86
|
+
match = json['class'] == klass
|
87
|
+
match &= json['args'] == args unless args.empty?
|
88
|
+
|
89
|
+
if match
|
90
|
+
destroyed += redis.lrem(queue, 0, string).to_i
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
destroyed
|
95
|
+
end
|
96
|
+
|
97
|
+
# Given a string queue name, returns an instance of Resque::Job
|
98
|
+
# if any jobs are available. If not, returns nil.
|
99
|
+
def self.reserve(queue)
|
100
|
+
return unless payload = Resque.pop(queue)
|
101
|
+
new(queue, payload)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Attempts to perform the work represented by this job instance.
|
105
|
+
# Calls #perform on the class given in the payload with the
|
106
|
+
# arguments given in the payload.
|
107
|
+
def perform
|
108
|
+
job = payload_class
|
109
|
+
job_args = args || []
|
110
|
+
job_was_performed = false
|
111
|
+
|
112
|
+
before_hooks = Plugin.before_hooks(job)
|
113
|
+
around_hooks = Plugin.around_hooks(job)
|
114
|
+
after_hooks = Plugin.after_hooks(job)
|
115
|
+
failure_hooks = Plugin.failure_hooks(job)
|
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.reverse.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
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the actual class constant represented in this job's payload.
|
169
|
+
def payload_class
|
170
|
+
@payload_class ||= constantize(@payload['class'])
|
171
|
+
end
|
172
|
+
|
173
|
+
# Returns an array of args represented in this job's payload.
|
174
|
+
def args
|
175
|
+
@payload['args']
|
176
|
+
end
|
177
|
+
|
178
|
+
# Given an exception object, hands off the needed parameters to
|
179
|
+
# the Failure module.
|
180
|
+
def fail(exception)
|
181
|
+
Failure.create \
|
182
|
+
:payload => payload,
|
183
|
+
:exception => exception,
|
184
|
+
:worker => worker,
|
185
|
+
:queue => queue
|
186
|
+
end
|
187
|
+
|
188
|
+
# Creates an identical job, essentially placing this job back on
|
189
|
+
# the queue.
|
190
|
+
def recreate
|
191
|
+
self.class.create(queue, payload_class, *args)
|
192
|
+
end
|
193
|
+
|
194
|
+
# String representation
|
195
|
+
def inspect
|
196
|
+
obj = @payload
|
197
|
+
"(Job{%s} | %s | %s)" % [ @queue, obj['class'], obj['args'].inspect ]
|
198
|
+
end
|
199
|
+
|
200
|
+
# Equality
|
201
|
+
def ==(other)
|
202
|
+
queue == other.queue &&
|
203
|
+
payload_class == other.payload_class &&
|
204
|
+
args == other.args
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|