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 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"