resque-status 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
- raise e
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
@@ -39,6 +39,10 @@
39
39
  <% end %>
40
40
  </table>
41
41
 
42
+ <% unless @statuses.empty? %>
43
+ <%= partial :next_more, :start => @start, :size => @size %>
44
+ <% end %>
45
+
42
46
  <%= poll %>
43
47
 
44
48
  <script type="text/javascript" charset="utf-8">
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.1.5'
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
- def self.statuses(num = -1)
60
- status_ids(num).collect do |id|
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(num = -1)
67
- redis.zrevrange(set_key, 0, num) || []
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
@@ -8,7 +8,10 @@ module Resque
8
8
  def self.registered(app)
9
9
 
10
10
  app.get '/statuses' do
11
- @statuses = Resque::Status.statuses
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
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{resque-status}
8
- s.version = "0.1.5"
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-08-23}
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 = [
@@ -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
- - 1
8
- - 5
9
- version: 0.1.5
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-08-23 00:00:00 -07:00
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"