resque-status 0.1.5 → 0.2.0
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.rdoc +15 -0
- data/lib/resque/job_with_status.rb +15 -1
- data/lib/resque/server/views/statuses.erb +4 -0
- data/lib/resque/status.rb +54 -30
- data/lib/resque/status_server.rb +4 -1
- data/resque-status.gemspec +2 -2
- data/test/test_resque-status.rb +23 -0
- metadata +12 -4
data/README.rdoc
CHANGED
@@ -94,6 +94,21 @@ Its also possible to get a list of current/recent job statuses:
|
|
94
94
|
|
95
95
|
Resque::Status.statuses #=> [#<Resque::Status>, ...]
|
96
96
|
|
97
|
+
=== Passing back data from the job
|
98
|
+
|
99
|
+
You may want to save data from inside the job to access it from outside the job.
|
100
|
+
|
101
|
+
A common use-case is web-triggered jobs that create files, later available for
|
102
|
+
download by the user.
|
103
|
+
|
104
|
+
A Status is actually just a hash, so inside a job you can do:
|
105
|
+
|
106
|
+
status['filename'] = '/myfilename'
|
107
|
+
|
108
|
+
Also, all the status setting methods take any number of hash arguments. So you could do:
|
109
|
+
|
110
|
+
complete('filename' => '/myfilename')
|
111
|
+
|
97
112
|
=== Kill! Kill! Kill!
|
98
113
|
|
99
114
|
Because we're tracking UUIDs per instance, and we're checking in/updating the status
|
@@ -87,6 +87,13 @@ module Resque
|
|
87
87
|
instance.safe_perform!
|
88
88
|
instance
|
89
89
|
end
|
90
|
+
|
91
|
+
# Wrapper API to forward a Resque::Job creation API call into a JobWithStatus call.
|
92
|
+
# This is needed to be used with resque scheduler
|
93
|
+
# http://github.com/bvandenbos/resque-scheduler
|
94
|
+
def self.scheduled(queue, klass, *args)
|
95
|
+
create(args)
|
96
|
+
end
|
90
97
|
|
91
98
|
# Create a new instance with <tt>uuid</tt> and <tt>options</tt>
|
92
99
|
def initialize(uuid, options = {})
|
@@ -99,15 +106,22 @@ module Resque
|
|
99
106
|
# If an error occurs within the job's work, it will set the status as failed and
|
100
107
|
# re-raise the error.
|
101
108
|
def safe_perform!
|
109
|
+
set_status({'status' => 'working'})
|
102
110
|
perform
|
103
111
|
completed unless status && status.completed?
|
112
|
+
on_success if respond_to?(:on_success)
|
104
113
|
rescue Killed
|
105
114
|
logger.info "Job #{self} Killed at #{Time.now}"
|
106
115
|
Resque::Status.killed(uuid)
|
116
|
+
on_killed if respond_to?(:on_killed)
|
107
117
|
rescue => e
|
108
118
|
logger.error e
|
109
119
|
failed("The task failed because of an error: #{e}")
|
110
|
-
|
120
|
+
if respond_to?(:on_failure)
|
121
|
+
on_failure(e)
|
122
|
+
else
|
123
|
+
raise e
|
124
|
+
end
|
111
125
|
end
|
112
126
|
|
113
127
|
# Returns a Redisk::Logger object scoped to this paticular job/uuid
|
data/lib/resque/status.rb
CHANGED
@@ -3,11 +3,11 @@ require 'redisk'
|
|
3
3
|
require 'uuid'
|
4
4
|
|
5
5
|
module Resque
|
6
|
-
# Resque::Status is a Hash object that has helper methods for dealing with
|
7
|
-
# the common status attributes. It also has a number of class methods for
|
6
|
+
# Resque::Status is a Hash object that has helper methods for dealing with
|
7
|
+
# the common status attributes. It also has a number of class methods for
|
8
8
|
# creating/updating/retrieving status objects from Redis
|
9
9
|
class Status < Hash
|
10
|
-
VERSION = '0.
|
10
|
+
VERSION = '0.2.0'
|
11
11
|
|
12
12
|
extend Resque::Helpers
|
13
13
|
|
@@ -33,18 +33,18 @@ module Resque
|
|
33
33
|
val = Resque::Status.new(uuid, *messages)
|
34
34
|
redis.set(status_key(uuid), encode(val))
|
35
35
|
if expire_in
|
36
|
-
redis.expire(status_key(uuid), expire_in)
|
36
|
+
redis.expire(status_key(uuid), expire_in)
|
37
37
|
end
|
38
38
|
val
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
def self.clear
|
42
42
|
status_ids.each do |id|
|
43
43
|
redis.del(status_key(id))
|
44
44
|
redis.zrem(set_key, id)
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# returns a Redisk::Logger scoped to the UUID. Any options passed are passed
|
49
49
|
# to the logger initialization.
|
50
50
|
#
|
@@ -54,53 +54,77 @@ module Resque
|
|
54
54
|
Redisk::Logger.new(logger_key(uuid), options)
|
55
55
|
end
|
56
56
|
|
57
|
+
def self.count
|
58
|
+
redis.zcard(set_key)
|
59
|
+
end
|
60
|
+
|
57
61
|
# Return <tt>num</tt> Resque::Status objects in reverse chronological order.
|
58
62
|
# By default returns the entire set.
|
59
|
-
|
60
|
-
|
63
|
+
# @param [Numeric] range_start The optional starting range
|
64
|
+
# @param [Numeric] range_end The optional ending range
|
65
|
+
# @example retuning the last 20 statuses
|
66
|
+
# Resque::Status.statuses(0, 20)
|
67
|
+
def self.statuses(range_start=nil, range_end=nil)
|
68
|
+
status_ids(range_start, range_end).collect do |id|
|
61
69
|
get(id)
|
62
70
|
end.compact
|
63
71
|
end
|
64
|
-
|
72
|
+
|
65
73
|
# Return the <tt>num</tt> most recent status/job UUIDs in reverse chronological order.
|
66
|
-
def self.status_ids(
|
67
|
-
|
74
|
+
def self.status_ids(range_start=nil, range_end=nil)
|
75
|
+
unless range_end && range_start
|
76
|
+
# Because we want a reverse chronological order, we need to get a range starting
|
77
|
+
# by the higest negative number.
|
78
|
+
redis.zrevrange(set_key, 0, -1) || []
|
79
|
+
else
|
80
|
+
# Because we want a reverse chronological order, we need to get a range starting
|
81
|
+
# by the higest negative number. The ordering is transparent from the API user's
|
82
|
+
# perspective so we need to convert the passed params
|
83
|
+
if range_start == 0
|
84
|
+
range_start = -1
|
85
|
+
else
|
86
|
+
range_end -= 1
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
(redis.zrevrange(set_key, -(range_end.abs), -(range_start.abs)) || []).reverse
|
91
|
+
end
|
68
92
|
end
|
69
|
-
|
93
|
+
|
70
94
|
# Kill the job at UUID on its next iteration this works by adding the UUID to a
|
71
|
-
# kill list (a.k.a. a list of jobs to be killed. Each iteration the job checks
|
95
|
+
# kill list (a.k.a. a list of jobs to be killed. Each iteration the job checks
|
72
96
|
# if it _should_ be killed by calling <tt>tick</tt> or <tt>at</tt>. If so, it raises
|
73
97
|
# a <tt>Resque::JobWithStatus::Killed</tt> error and sets the status to 'killed'.
|
74
|
-
def self.kill(uuid)
|
98
|
+
def self.kill(uuid)
|
75
99
|
redis.sadd(kill_key, uuid)
|
76
100
|
end
|
77
|
-
|
101
|
+
|
78
102
|
# Remove the job at UUID from the kill list
|
79
103
|
def self.killed(uuid)
|
80
104
|
redis.srem(kill_key, uuid)
|
81
105
|
end
|
82
|
-
|
106
|
+
|
83
107
|
# Return the UUIDs of the jobs on the kill list
|
84
108
|
def self.kill_ids
|
85
109
|
redis.smembers(kill_key)
|
86
110
|
end
|
87
|
-
|
111
|
+
|
88
112
|
# Check whether a job with UUID is on the kill list
|
89
113
|
def self.should_kill?(uuid)
|
90
114
|
redis.sismember(kill_key, uuid)
|
91
115
|
end
|
92
|
-
|
116
|
+
|
93
117
|
# The time in seconds that jobs and statuses should expire from Redis (after
|
94
118
|
# the last time they are touched/updated)
|
95
119
|
def self.expire_in
|
96
120
|
@expire_in
|
97
121
|
end
|
98
|
-
|
122
|
+
|
99
123
|
# Set the <tt>expire_in</tt> time in seconds
|
100
124
|
def self.expire_in=(seconds)
|
101
125
|
@expire_in = seconds.nil? ? nil : seconds.to_i
|
102
126
|
end
|
103
|
-
|
127
|
+
|
104
128
|
def self.status_key(uuid)
|
105
129
|
"status:#{uuid}"
|
106
130
|
end
|
@@ -111,7 +135,7 @@ module Resque
|
|
111
135
|
|
112
136
|
def self.kill_key
|
113
137
|
"_kill"
|
114
|
-
end
|
138
|
+
end
|
115
139
|
|
116
140
|
def self.logger_key(uuid)
|
117
141
|
"_log:#{uuid}"
|
@@ -122,7 +146,7 @@ module Resque
|
|
122
146
|
end
|
123
147
|
|
124
148
|
def self.hash_accessor(name, options = {})
|
125
|
-
options[:default] ||= nil
|
149
|
+
options[:default] ||= nil
|
126
150
|
coerce = options[:coerce] ? ".#{options[:coerce]}" : ""
|
127
151
|
module_eval <<-EOT
|
128
152
|
def #{name}
|
@@ -151,10 +175,10 @@ module Resque
|
|
151
175
|
|
152
176
|
hash_accessor :num
|
153
177
|
hash_accessor :total
|
154
|
-
|
178
|
+
|
155
179
|
# Create a new Resque::Status object. If multiple arguments are passed
|
156
180
|
# it is assumed the first argument is the UUID and the rest are status objects.
|
157
|
-
# All arguments are subsequentily merged in order. Strings are assumed to
|
181
|
+
# All arguments are subsequentily merged in order. Strings are assumed to
|
158
182
|
# be messages.
|
159
183
|
def initialize(*args)
|
160
184
|
super nil
|
@@ -169,7 +193,7 @@ module Resque
|
|
169
193
|
end
|
170
194
|
self.replace(status_hash)
|
171
195
|
end
|
172
|
-
|
196
|
+
|
173
197
|
# calculate the % completion of the job based on <tt>status</tt>, <tt>num</tt>
|
174
198
|
# and <tt>total</tt>
|
175
199
|
def pct_complete
|
@@ -182,7 +206,7 @@ module Resque
|
|
182
206
|
(((num || 0).to_f / t.to_f) * 100).to_i
|
183
207
|
end
|
184
208
|
end
|
185
|
-
|
209
|
+
|
186
210
|
# Return the time of the status initialization. If set returns a <tt>Time</tt>
|
187
211
|
# object, otherwise returns nil
|
188
212
|
def time
|
@@ -191,22 +215,22 @@ module Resque
|
|
191
215
|
|
192
216
|
STATUSES.each do |status|
|
193
217
|
define_method("#{status}?") do
|
194
|
-
self['status'] === status
|
218
|
+
self['status'] === status
|
195
219
|
end
|
196
220
|
end
|
197
|
-
|
221
|
+
|
198
222
|
# Can the job be killed? 'failed', 'completed', and 'killed' jobs cant be killed
|
199
223
|
# (for pretty obvious reasons)
|
200
224
|
def killable?
|
201
225
|
!['failed', 'completed', 'killed'].include?(self.status)
|
202
226
|
end
|
203
|
-
|
227
|
+
|
204
228
|
unless method_defined?(:to_json)
|
205
229
|
def to_json(*args)
|
206
230
|
json
|
207
231
|
end
|
208
232
|
end
|
209
|
-
|
233
|
+
|
210
234
|
# Return a JSON representation of the current object.
|
211
235
|
def json
|
212
236
|
h = self.dup
|
data/lib/resque/status_server.rb
CHANGED
@@ -8,7 +8,10 @@ module Resque
|
|
8
8
|
def self.registered(app)
|
9
9
|
|
10
10
|
app.get '/statuses' do
|
11
|
-
@
|
11
|
+
@start = params[:start].to_i
|
12
|
+
@end = @start + (params[:per_page] || 50)
|
13
|
+
@statuses = Resque::Status.statuses(@start, @end)
|
14
|
+
@size = @statuses.size
|
12
15
|
status_view(:statuses)
|
13
16
|
end
|
14
17
|
|
data/resque-status.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{resque-status}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Aaron Quint"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-10-11}
|
13
13
|
s.description = %q{resque-status is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Status class which can set/get the statuses of jobs and a Resque::JobWithStatus class that when subclassed provides easily trackable/killable jobs.}
|
14
14
|
s.email = %q{aaron@quirkey.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/test/test_resque-status.rb
CHANGED
@@ -87,8 +87,21 @@ class TestResqueStatus < Test::Unit::TestCase
|
|
87
87
|
end
|
88
88
|
|
89
89
|
context ".status_ids" do
|
90
|
+
|
91
|
+
setup do
|
92
|
+
@uuids = []
|
93
|
+
30.times{ Resque::Status.create }
|
94
|
+
end
|
95
|
+
|
90
96
|
should "return an array of job ids" do
|
91
97
|
assert Resque::Status.status_ids.is_a?(Array)
|
98
|
+
assert_equal 32, Resque::Status.status_ids.size # 30 + 2
|
99
|
+
end
|
100
|
+
|
101
|
+
should "let you paginate through the statuses" do
|
102
|
+
assert_equal Resque::Status.status_ids.reverse[0, 10], Resque::Status.status_ids(0, 10)
|
103
|
+
assert_equal Resque::Status.status_ids.reverse[9, 10], Resque::Status.status_ids(10, 20)
|
104
|
+
# assert_equal Resque::Status.status_ids.reverse[0, 10], Resque::Status.status_ids(0, 10)
|
92
105
|
end
|
93
106
|
end
|
94
107
|
|
@@ -102,6 +115,16 @@ class TestResqueStatus < Test::Unit::TestCase
|
|
102
115
|
|
103
116
|
end
|
104
117
|
|
118
|
+
# context ".count" do
|
119
|
+
#
|
120
|
+
# should "return a count of statuses" do
|
121
|
+
# statuses = Resque::Status.statuses
|
122
|
+
# assert_equal 2, statuses.size
|
123
|
+
# assert_equal statuses.size, Resque::Status.count
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# end
|
127
|
+
|
105
128
|
context ".logger" do
|
106
129
|
setup do
|
107
130
|
@logger = Resque::Status.logger(@uuid)
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-status
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Aaron Quint
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-10-11 00:00:00 -07:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 11
|
28
30
|
segments:
|
29
31
|
- 2
|
30
32
|
- 0
|
@@ -40,6 +42,7 @@ dependencies:
|
|
40
42
|
requirements:
|
41
43
|
- - ">="
|
42
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 25
|
43
46
|
segments:
|
44
47
|
- 1
|
45
48
|
- 3
|
@@ -55,6 +58,7 @@ dependencies:
|
|
55
58
|
requirements:
|
56
59
|
- - ">="
|
57
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 21
|
58
62
|
segments:
|
59
63
|
- 0
|
60
64
|
- 2
|
@@ -70,6 +74,7 @@ dependencies:
|
|
70
74
|
requirements:
|
71
75
|
- - ">="
|
72
76
|
- !ruby/object:Gem::Version
|
77
|
+
hash: 35
|
73
78
|
segments:
|
74
79
|
- 2
|
75
80
|
- 10
|
@@ -85,6 +90,7 @@ dependencies:
|
|
85
90
|
requirements:
|
86
91
|
- - ">="
|
87
92
|
- !ruby/object:Gem::Version
|
93
|
+
hash: 43
|
88
94
|
segments:
|
89
95
|
- 0
|
90
96
|
- 9
|
@@ -134,6 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
134
140
|
requirements:
|
135
141
|
- - ">="
|
136
142
|
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
137
144
|
segments:
|
138
145
|
- 0
|
139
146
|
version: "0"
|
@@ -142,6 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
149
|
requirements:
|
143
150
|
- - ">="
|
144
151
|
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
145
153
|
segments:
|
146
154
|
- 0
|
147
155
|
version: "0"
|