powerhome-resque-status 0.6.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0d9c0c0ab1c7036c3336d46117db310f73503b76d19ee9117dd4944668abc1a0
4
+ data.tar.gz: e9df86dd8599410cec3c1ce09e2afdc8785b36011fa921c9f2b11052a2b2a38d
5
+ SHA512:
6
+ metadata.gz: 022f4fedf4fd1ef8ceeb413a0ded6c9e118ae67ca78f0a3c3f044c79a0e950d274a4d5db4178022eeda6b14b04beab7c6f710bb0355502e871b8fefbcf2d70db
7
+ data.tar.gz: ba037b5c574172c20bd394955ed6a9c822c0cb717d8da027b9b1d0a1330dae9dfd459f380484a9254a956f07bc85b317332575660133b5886e63585a06f7f19c
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'resque', '~>1.19'
4
+
5
+ group :test do
6
+ gem 'mocha', '~>0.9'
7
+ gem 'minitest', '~> 5.5'
8
+ end
9
+
10
+ group :development do
11
+ gem 'jeweler'
12
+ end
@@ -0,0 +1,85 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.4.0)
5
+ builder (3.2.4)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ faraday (0.9.2)
9
+ multipart-post (>= 1.2, < 3)
10
+ git (1.7.0)
11
+ rchardet (~> 1.8)
12
+ github_api (0.16.0)
13
+ addressable (~> 2.4.0)
14
+ descendants_tracker (~> 0.0.4)
15
+ faraday (~> 0.8, < 0.10)
16
+ hashie (>= 3.4)
17
+ mime-types (>= 1.16, < 3.0)
18
+ oauth2 (~> 1.0)
19
+ hashie (4.1.0)
20
+ highline (2.0.3)
21
+ jeweler (2.3.9)
22
+ builder
23
+ bundler
24
+ git (>= 1.2.5)
25
+ github_api (~> 0.16.0)
26
+ highline (>= 1.6.15)
27
+ nokogiri (>= 1.5.10)
28
+ psych
29
+ rake
30
+ rdoc
31
+ semver2
32
+ jwt (2.2.2)
33
+ metaclass (0.0.2)
34
+ mime-types (2.99.3)
35
+ mini_portile2 (2.4.0)
36
+ minitest (5.5.1)
37
+ mocha (0.14.0)
38
+ metaclass (~> 0.0.1)
39
+ multi_json (1.15.0)
40
+ multi_xml (0.6.0)
41
+ multipart-post (2.1.1)
42
+ nokogiri (1.10.10)
43
+ mini_portile2 (~> 2.4.0)
44
+ oauth2 (1.4.4)
45
+ faraday (>= 0.8, < 2.0)
46
+ jwt (>= 1.0, < 3.0)
47
+ multi_json (~> 1.3)
48
+ multi_xml (~> 0.5)
49
+ rack (>= 1.2, < 3)
50
+ psych (3.2.0)
51
+ rack (1.6.13)
52
+ rack-protection (1.2.0)
53
+ rack
54
+ rake (13.0.1)
55
+ rchardet (1.8.0)
56
+ rdoc (6.2.1)
57
+ redis (3.0.2)
58
+ redis-namespace (1.2.1)
59
+ redis (~> 3.0.0)
60
+ resque (1.23.0)
61
+ multi_json (~> 1.0)
62
+ redis-namespace (~> 1.0)
63
+ sinatra (>= 0.9.2)
64
+ vegas (~> 0.1.2)
65
+ semver2 (3.4.2)
66
+ sinatra (1.3.3)
67
+ rack (~> 1.3, >= 1.3.6)
68
+ rack-protection (~> 1.2)
69
+ tilt (~> 1.3, >= 1.3.3)
70
+ thread_safe (0.3.6)
71
+ tilt (1.3.3)
72
+ vegas (0.1.11)
73
+ rack (>= 1.0.0)
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ jeweler
80
+ minitest (~> 5.5)
81
+ mocha (~> 0.9)
82
+ resque (~> 1.19)
83
+
84
+ BUNDLED WITH
85
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Aaron Quint
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,185 @@
1
+ = resque-status
2
+
3
+ resque-status is an extension to the resque queue system that provides simple trackable jobs.
4
+
5
+ == About
6
+
7
+ resque-status provides a set of simple classes that extend resque's default
8
+ functionality (with 0% monkey patching) to give apps a way to track specific
9
+ job instances and their status. It achieves this by giving job instances UUID's
10
+ and allowing the job instances to report their status from within their iterations.
11
+
12
+ == Installation
13
+
14
+ resque-status *requires Redis >= 1.1* (though I recommend getting the latest stable version).
15
+ You can download Redis here: http://code.google.com/p/redis/ or install it
16
+ using homebrew (brew install redis).
17
+
18
+ Install the resque-status gem (which will pull in the dependencies).
19
+
20
+ gem install resque-status
21
+
22
+ With newer Rails add this to your Gemfile:
23
+
24
+ # Gemfile
25
+ gem 'resque-status'
26
+
27
+ Then in an initializer:
28
+
29
+ # config/initializers/resque.rb
30
+ Resque.redis = "your/redis/socket" # default localhost:6379
31
+ Resque::Plugins::Status::Hash.expire_in = (24 * 60 * 60) # 24hrs in seconds
32
+
33
+ == Usage
34
+
35
+ The most direct way to use resque-status is to create your jobs using the
36
+ Resque::Plugins::Status module. An example job would look something like:
37
+
38
+ class SleepJob
39
+ include Resque::Plugins::Status
40
+
41
+ def perform
42
+ total = (options['length'] || 1000).to_i
43
+ total.times do |i|
44
+ num = i+1
45
+ at(num, total, "At #{num} of #{total}")
46
+ sleep(1)
47
+ end
48
+ end
49
+ end
50
+
51
+ One major difference is that instead of implementing <tt>perform</tt> as a
52
+ class method, we do our job implementation within instances of the job class.
53
+
54
+ In order to queue a SleepJob up, we also won't use <tt>Resque.enqueue</tt>, instead
55
+ we'll use the <tt>create</tt> class method which will wrap <tt>enqueue</tt> and
56
+ creating a unique id (UUID) for us to track the job with.
57
+
58
+ job_id = SleepJob.create(length: 100)
59
+
60
+ This will create a UUID enqueue the job and pass the :length option on the SleepJob
61
+ instance as options['length'] (as you can see above).
62
+
63
+ Now that we have a UUID its really easy to get the status:
64
+
65
+ status = Resque::Plugins::Status::Hash.get(job_id)
66
+
67
+ This returns a Resque::Plugins::Status::Hash object, which is a Hash (with benefits).
68
+
69
+ status.pct_complete #=> 0
70
+ status.status #=> 'queued'
71
+ status.queued? #=> true
72
+ status.working? #=> false
73
+ status.time #=> Time object
74
+ status.message #=> "Created at ..."
75
+
76
+ Once the worker reserves the job, the instance of SleepJob updates the status at
77
+ each iteration using <tt>at()</tt>
78
+
79
+ status = Resque::Plugins::Status::Hash.get(job_id)
80
+ status.working? #=> true
81
+ status.num #=> 5
82
+ status.total #=> 100
83
+ status.pct_complete #=> 5
84
+
85
+ If an error occurs within the job instance, the status is set to 'failed' and then
86
+ the error is re-raised so that Resque can capture it.
87
+
88
+ Its also possible to get a list of current/recent job statuses:
89
+
90
+ Resque::Plugins::Status::Hash.statuses #=> [#<Resque::Plugins::Status::Hash>, ...]
91
+
92
+ === Passing back data from the job
93
+
94
+ You may want to save data from inside the job to access it from outside the job.
95
+
96
+ A common use-case is web-triggered jobs that create files, later available for
97
+ download by the user.
98
+
99
+ A Status is actually just a hash, so inside a job you can do:
100
+
101
+ set_status(filename: "myfilename")
102
+
103
+ Also, all the status setting methods take any number of hash arguments. So you could do:
104
+
105
+ completed('filename' => '/myfilename')
106
+
107
+ === Kill! Kill! Kill!
108
+
109
+ Because we're tracking UUIDs per instance, and we're checking in/updating the status
110
+ on each iteration (using <tt>at</tt> or <tt>tick</tt>) we can kill specific jobs
111
+ by UUID.
112
+
113
+ Resque::Plugins::Status::Hash.kill(job_id)
114
+
115
+ The next time the job at job_id calls <tt>at</tt> or <tt>tick</tt>, it will raise a <tt>Killed</tt>
116
+ error and set the status to killed.
117
+
118
+ === Percent Complete and setting the message
119
+
120
+ Use <tt>at</tt> or <tt>tick</tt> to show progress in your job's <tt>perform</tt> function
121
+ (which is displayed on the resque-web status tab). This will also be where <tt>Killed</tt>
122
+ is raised if the job is killed.
123
+
124
+ at(steps_completed, total_steps, "${steps_completed} of #{total_steps} steps completed!")
125
+
126
+ === Expiration
127
+
128
+ Since Redis is RAM based, we probably don't want to keep these statuses around forever
129
+ (at least until @antirez releases the VM feature). By setting expire_in, all statuses
130
+ and their related keys will expire in expire_in seconds from the last time theyre updated:
131
+
132
+ Resque::Plugins::Status::Hash.expire_in = (60 * 60) # 1 hour
133
+ === Testing
134
+
135
+ Recent versions of Resque introduced `Resque.inline` which changes the behavior to
136
+ instead of enqueueing and performing jobs to just executing them inline. In Resque
137
+ itself this removes the dependency on a Redis, however, `Resque::Status` uses Redis
138
+ to store information about jobs, so though `inline` "works", you will still need
139
+ to use or mock a redis connection. You should be able to use a library like
140
+ https://github.com/causes/mock_redis alongside `inline` if you really want to
141
+ avoid Redis connections in your test.
142
+
143
+ === resque-web
144
+
145
+ Though the main purpose of these trackable jobs is to allow you to surface the status
146
+ of user created jobs through your apps' own UI, I've added a simple example UI
147
+ as a plugin to resque-web.
148
+
149
+ To use, you need to setup a resque-web config file:
150
+
151
+ # ~/resque_conf.rb
152
+ require 'resque/status_server'
153
+
154
+ Then start resque-web with your config:
155
+
156
+ resque-web ~/resque_conf.rb
157
+
158
+ This should launch resque-web in your browser and you should see a 'Statuses' tab.
159
+
160
+ http://img.skitch.com/20100119-k166xyijcjpkk6xtwnw3854a8g.jpg
161
+
162
+ == More
163
+
164
+ Source: http://github.com/quirkey/resque-status
165
+ API Docs: http://rdoc.info/projects/quirkey/resque-status
166
+ Examples: http://github.com/quirkey/resque-status/tree/master/examples
167
+ Resque: http://github.com/defunkt/resque
168
+
169
+ == Thanks
170
+
171
+ Resque is awesome, @defunkt needs a shout-out.
172
+
173
+ == Note on Patches/Pull Requests
174
+
175
+ * Fork the project.
176
+ * Make your feature addition or bug fix.
177
+ * Add tests for it. This is important so I don't break it in a
178
+ future version unintentionally.
179
+ * Commit, do not mess with rakefile, version, or history.
180
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
181
+ * Send me a pull request. Bonus points for topic branches.
182
+
183
+ == Copyright
184
+
185
+ Copyright (c) 2010 Aaron Quint. See LICENSE for details.
@@ -0,0 +1,48 @@
1
+ $LOAD_PATH.unshift './lib'
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'resque-status'
6
+ require 'resque/tasks'
7
+
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gem|
11
+ gem.name = "powerhome-resque-status"
12
+ gem.version = Resque::Plugins::Status::VERSION
13
+ gem.summary = %Q{resque-status is an extension to the resque queue system that provides simple trackable jobs.}
14
+ gem.description = %Q{resque-status is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Plugins::Status::Hash class which can set/get the statuses of jobs and a Resque::Plugins::Status class that when included provides easily trackable/killable jobs.}
15
+ gem.email = "aaron@quirkey.com"
16
+ gem.homepage = "http://github.com/quirkey/resque-status"
17
+ gem.rubyforge_project = "quirkey"
18
+ gem.authors = ["Aaron Quint"]
19
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
20
+ end
21
+ Jeweler::RubygemsDotOrgTasks.new
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/test_*.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/test_*.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test
47
+
48
+ task :default => :test
@@ -0,0 +1,36 @@
1
+ require 'resque/job_with_status' # in rails you would probably do this in an initializer
2
+
3
+ # sleeps for _length_ seconds updating the status every second
4
+
5
+ class SleepJob
6
+ include Resque::Plugins::Status
7
+
8
+ def perform
9
+ total = options.has_key?('length') ? options['length'].to_i : 1000
10
+ num = 0
11
+ while num < total
12
+ at(num, total, "At #{num} of #{total}")
13
+ sleep(1)
14
+ num += 1
15
+ end
16
+ completed
17
+ end
18
+
19
+ end
20
+
21
+
22
+ if __FILE__ == $0
23
+ # Make sure you have a worker running
24
+ # rake -rexamples/sleep_job.rb resque:work QUEUE=statused
25
+
26
+ # running the job
27
+ puts "Creating the SleepJob"
28
+ job_id = SleepJob.create :length => 100
29
+ puts "Got back #{job_id}"
30
+
31
+ # check the status until its complete
32
+ while status = Resque::Plugins::Status::Hash.get(job_id) and !status.completed? && !status.failed?
33
+ sleep 1
34
+ puts status.inspect
35
+ end
36
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'resque-status'
@@ -0,0 +1 @@
1
+ require "#{File.dirname(__FILE__)}/resque/status"
@@ -0,0 +1,5 @@
1
+ module Resque
2
+ class JobWithStatus
3
+ include Resque::Plugins::Status
4
+ end
5
+ end
@@ -0,0 +1,254 @@
1
+ module Resque
2
+ module Plugins
3
+
4
+ # Resque::Plugins::Status is a module your jobs will include.
5
+ # It provides helper methods for updating the status/etc from within an
6
+ # instance as well as class methods for creating and queuing the jobs.
7
+ #
8
+ # All you have to do to get this functionality is include Resque::Plugins::Status
9
+ # and then implement a <tt>perform<tt> method.
10
+ #
11
+ # For example
12
+ #
13
+ # class ExampleJob
14
+ # include Resque::Plugins::Status
15
+ #
16
+ # def perform
17
+ # num = options['num']
18
+ # i = 0
19
+ # while i < num
20
+ # i += 1
21
+ # at(i, num)
22
+ # end
23
+ # completed("Finished!")
24
+ # end
25
+ #
26
+ # end
27
+ #
28
+ # This job would iterate num times updating the status as it goes. At the end
29
+ # we update the status telling anyone listening to this job that its complete.
30
+ module Status
31
+ VERSION = '0.6.0'
32
+
33
+ STATUS_QUEUED = 'queued'
34
+ STATUS_WORKING = 'working'
35
+ STATUS_COMPLETED = 'completed'
36
+ STATUS_FAILED = 'failed'
37
+ STATUS_KILLED = 'killed'
38
+ STATUSES = [
39
+ STATUS_QUEUED,
40
+ STATUS_WORKING,
41
+ STATUS_COMPLETED,
42
+ STATUS_FAILED,
43
+ STATUS_KILLED
44
+ ].freeze
45
+
46
+ autoload :Hash, 'resque/plugins/status/hash'
47
+
48
+ # The error class raised when a job is killed
49
+ class Killed < RuntimeError; end
50
+ class NotANumber < RuntimeError; end
51
+
52
+ attr_reader :uuid, :options
53
+
54
+ def self.included(base)
55
+ base.extend(ClassMethods)
56
+ end
57
+
58
+ module ClassMethods
59
+
60
+ # The default queue is :statused, this can be ovveridden in the specific job
61
+ # class to put the jobs on a specific worker queue
62
+ def queue
63
+ :statused
64
+ end
65
+
66
+ # used when displaying the Job in the resque-web UI and identifiyng the job
67
+ # type by status. By default this is the name of the job class, but can be
68
+ # ovveridden in the specific job class to present a more user friendly job
69
+ # name
70
+ def name
71
+ self.to_s
72
+ end
73
+
74
+ # Create is the primary method for adding jobs to the queue. This would be
75
+ # called on the job class to create a job of that type. Any options passed are
76
+ # passed to the Job instance as a hash of options. It returns the UUID of the
77
+ # job.
78
+ #
79
+ # == Example:
80
+ #
81
+ # class ExampleJob
82
+ # include Resque::Plugins::Status
83
+ #
84
+ # def perform
85
+ # set_status "Hey I'm a job num #{options['num']}"
86
+ # end
87
+ #
88
+ # end
89
+ #
90
+ # job_id = ExampleJob.create(:num => 100)
91
+ #
92
+ def create(options = {})
93
+ self.enqueue(self, options)
94
+ end
95
+
96
+ # Adds a job of type <tt>klass<tt> to the queue with <tt>options<tt>.
97
+ #
98
+ # Returns the UUID of the job if the job was queued, or nil if the job was
99
+ # rejected by a before_enqueue hook.
100
+ def enqueue(klass, options = {})
101
+ self.enqueue_to(Resque.queue_from_class(klass) || queue, klass, options)
102
+ end
103
+
104
+ # Adds a job of type <tt>klass<tt> to a specified queue with <tt>options<tt>.
105
+ #
106
+ # Returns the UUID of the job if the job was queued, or nil if the job was
107
+ # rejected by a before_enqueue hook.
108
+ def enqueue_to(queue, klass, options = {})
109
+ uuid = Resque::Plugins::Status::Hash.generate_uuid
110
+ Resque::Plugins::Status::Hash.create uuid, :options => options
111
+
112
+ if Resque.enqueue_to(queue, klass, uuid, options)
113
+ uuid
114
+ else
115
+ Resque::Plugins::Status::Hash.remove(uuid)
116
+ nil
117
+ end
118
+ end
119
+
120
+ # Removes a job of type <tt>klass<tt> from the queue.
121
+ #
122
+ # The initially given options are retrieved from the status hash.
123
+ # (Resque needs the options to find the correct queue entry)
124
+ def dequeue(klass, uuid)
125
+ status = Resque::Plugins::Status::Hash.get(uuid)
126
+ Resque.dequeue(klass, uuid, status.options)
127
+ end
128
+
129
+ # This is the method called by Resque::Worker when processing jobs. It
130
+ # creates a new instance of the job class and populates it with the uuid and
131
+ # options.
132
+ #
133
+ # You should not override this method, rahter the <tt>perform</tt> instance method.
134
+ def perform(uuid=nil, options = {})
135
+ uuid ||= Resque::Plugins::Status::Hash.generate_uuid
136
+ instance = new(uuid, options)
137
+ instance.safe_perform!
138
+ instance
139
+ end
140
+
141
+ # Wrapper API to forward a Resque::Job creation API call into a Resque::Plugins::Status call.
142
+ # This is needed to be used with resque scheduler
143
+ # http://github.com/bvandenbos/resque-scheduler
144
+ def scheduled(queue, klass, *args)
145
+ self.enqueue_to(queue, self, *args)
146
+ end
147
+ end
148
+
149
+ # Create a new instance with <tt>uuid</tt> and <tt>options</tt>
150
+ def initialize(uuid, options = {})
151
+ @uuid = uuid
152
+ @options = options
153
+ end
154
+
155
+ # Run by the Resque::Worker when processing this job. It wraps the <tt>perform</tt>
156
+ # method ensuring that the final status of the job is set regardless of error.
157
+ # If an error occurs within the job's work, it will set the status as failed and
158
+ # re-raise the error.
159
+ def safe_perform!
160
+ set_status({'status' => STATUS_WORKING})
161
+ perform
162
+ if status && status.failed?
163
+ on_failure(status.message) if respond_to?(:on_failure)
164
+ return
165
+ elsif status && !status.completed?
166
+ completed
167
+ end
168
+ on_success if respond_to?(:on_success)
169
+ rescue Killed
170
+ Resque::Plugins::Status::Hash.killed(uuid)
171
+ on_killed if respond_to?(:on_killed)
172
+ rescue => e
173
+ failed("The task failed because of an error: #{e}")
174
+ if respond_to?(:on_failure)
175
+ on_failure(e)
176
+ else
177
+ raise e
178
+ end
179
+ end
180
+
181
+ # Set the jobs status. Can take an array of strings or hashes that are merged
182
+ # (in order) into a final status hash.
183
+ def status=(new_status)
184
+ Resque::Plugins::Status::Hash.set(uuid, *new_status)
185
+ end
186
+
187
+ # get the Resque::Plugins::Status::Hash object for the current uuid
188
+ def status
189
+ Resque::Plugins::Status::Hash.get(uuid)
190
+ end
191
+
192
+ def name
193
+ "#{self.class.name}(#{options.inspect unless options.empty?})"
194
+ end
195
+
196
+ # Checks against the kill list if this specific job instance should be killed
197
+ # on the next iteration
198
+ def should_kill?
199
+ Resque::Plugins::Status::Hash.should_kill?(uuid)
200
+ end
201
+
202
+ # set the status of the job for the current itteration. <tt>num</tt> and
203
+ # <tt>total</tt> are passed to the status as well as any messages.
204
+ # This will kill the job if it has been added to the kill list with
205
+ # <tt>Resque::Plugins::Status::Hash.kill()</tt>
206
+ def at(num, total, *messages)
207
+ if total.to_f <= 0.0
208
+ raise(NotANumber, "Called at() with total=#{total} which is not a number")
209
+ end
210
+ tick({
211
+ 'num' => num,
212
+ 'total' => total
213
+ }, *messages)
214
+ end
215
+
216
+ # sets the status of the job for the current itteration. You should use
217
+ # the <tt>at</tt> method if you have actual numbers to track the iteration count.
218
+ # This will kill the job if it has been added to the kill list with
219
+ # <tt>Resque::Plugins::Status::Hash.kill()</tt>
220
+ def tick(*messages)
221
+ kill! if should_kill?
222
+ set_status({'status' => STATUS_WORKING}, *messages)
223
+ end
224
+
225
+ # set the status to 'failed' passing along any additional messages
226
+ def failed(*messages)
227
+ set_status({'status' => STATUS_FAILED}, *messages)
228
+ end
229
+
230
+ # set the status to 'completed' passing along any addional messages
231
+ def completed(*messages)
232
+ set_status({
233
+ 'status' => STATUS_COMPLETED,
234
+ 'message' => "Completed at #{Time.now}"
235
+ }, *messages)
236
+ end
237
+
238
+ # kill the current job, setting the status to 'killed' and raising <tt>Killed</tt>
239
+ def kill!
240
+ set_status({
241
+ 'status' => STATUS_KILLED,
242
+ 'message' => "Killed at #{Time.now}"
243
+ })
244
+ raise Killed
245
+ end
246
+
247
+ private
248
+ def set_status(*args)
249
+ self.status = [status, {'name' => self.name}, args].flatten
250
+ end
251
+
252
+ end
253
+ end
254
+ end