resque-state 1.0.4 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: def5514d96c48b47f9f04cb7c6531c475fdacc71
4
- data.tar.gz: 55d4f6107656c9bf627c9a863cf767df92db2cad
3
+ metadata.gz: 780ed4ffab1f5d88c532ed4e505cf9bf80efa584
4
+ data.tar.gz: 55857ef7b3c22c689e496dd7e2758bd74cd048a6
5
5
  SHA512:
6
- metadata.gz: 98191a7d19376ed31345aebee07d699743a24ab64c32e0ed872eae7a97fc02dd0e51cf5a952362d908871232960ec6c1f4e73c0803df25a74070002b273ba4ba
7
- data.tar.gz: 69795a7d0a60b5a9ac19e8894c36b2d384848ad1f424e6b3a1a42fc1a90a503c37e6254a99c783dc6b28fd83877aa589b20ff6d39a5ea44b1fc794b497a39ca6
6
+ metadata.gz: '0309cc6512943aed87f89aa0f49a209753609c49a1f27f569edb105c012088adf460e0ecb8709a6f73cf0a67c0fa706b39600a4b38181c814314c53c934208c8'
7
+ data.tar.gz: 49fcf5105f608210123c50e6beada0d0ed0f32e012c51758da30a484f98f45c6fa72c35bea16d1adf1f9a2049d92b3f9f0f4d717fa941cac5806f11646c95e51
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
3
+ - 2.2.2
4
4
  - 2.3.4
5
5
  - 2.4.1
6
6
  - ruby-head
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
- addressable (2.5.1)
5
- public_suffix (~> 2.0, >= 2.0.2)
4
+ addressable (2.4.0)
6
5
  builder (3.2.3)
7
6
  codeclimate-test-reporter (1.0.7)
8
7
  simplecov
@@ -14,22 +13,21 @@ GEM
14
13
  faraday (0.9.2)
15
14
  multipart-post (>= 1.2, < 3)
16
15
  git (1.3.0)
17
- github_api (0.11.3)
18
- addressable (~> 2.3)
19
- descendants_tracker (~> 0.0.1)
16
+ github_api (0.16.0)
17
+ addressable (~> 2.4.0)
18
+ descendants_tracker (~> 0.0.4)
20
19
  faraday (~> 0.8, < 0.10)
21
- hashie (>= 1.2)
22
- multi_json (>= 1.7.5, < 2.0)
23
- nokogiri (~> 1.6.0)
24
- oauth2
20
+ hashie (>= 3.4)
21
+ mime-types (>= 1.16, < 3.0)
22
+ oauth2 (~> 1.0)
25
23
  hashie (3.5.5)
26
24
  highline (1.7.8)
27
25
  jar-dependencies (0.3.11)
28
- jeweler (2.3.3)
26
+ jeweler (2.3.6)
29
27
  builder
30
- bundler (>= 1.0)
28
+ bundler (>= 1)
31
29
  git (>= 1.2.5)
32
- github_api (~> 0.11.0)
30
+ github_api (~> 0.16.0)
33
31
  highline (>= 1.6.15)
34
32
  nokogiri (>= 1.5.10)
35
33
  psych (~> 2.2)
@@ -40,19 +38,21 @@ GEM
40
38
  json (2.1.0-java)
41
39
  jwt (1.5.6)
42
40
  metaclass (0.0.4)
43
- mini_portile2 (2.1.0)
44
- minitest (5.10.1)
41
+ mime-types (2.99.3)
42
+ mini_portile2 (2.2.0)
43
+ minitest (5.10.2)
45
44
  mocha (1.2.1)
46
45
  metaclass (~> 0.0.1)
47
46
  mono_logger (1.1.0)
48
47
  multi_json (1.12.1)
49
48
  multi_xml (0.6.0)
50
49
  multipart-post (2.0.0)
51
- nokogiri (1.6.8.1)
52
- mini_portile2 (~> 2.1.0)
53
- nokogiri (1.6.8.1-java)
54
- oauth2 (1.3.1)
55
- faraday (>= 0.8, < 0.12)
50
+ mustermann (1.0.0)
51
+ nokogiri (1.8.0)
52
+ mini_portile2 (~> 2.2.0)
53
+ nokogiri (1.8.0-java)
54
+ oauth2 (1.4.0)
55
+ faraday (>= 0.8, < 0.13)
56
56
  jwt (~> 1.0)
57
57
  multi_json (~> 1.3)
58
58
  multi_xml (~> 0.5)
@@ -60,9 +60,8 @@ GEM
60
60
  psych (2.2.4)
61
61
  psych (2.2.4-java)
62
62
  jar-dependencies (>= 0.1.7)
63
- public_suffix (2.0.5)
64
- rack (1.6.5)
65
- rack-protection (1.5.3)
63
+ rack (2.0.3)
64
+ rack-protection (2.0.0)
66
65
  rack
67
66
  rake (12.0.0)
68
67
  rdoc (5.1.0)
@@ -80,11 +79,12 @@ GEM
80
79
  docile (~> 1.1.0)
81
80
  json (>= 1.8, < 3)
82
81
  simplecov-html (~> 0.10.0)
83
- simplecov-html (0.10.0)
84
- sinatra (1.4.8)
85
- rack (~> 1.5)
86
- rack-protection (~> 1.4)
87
- tilt (>= 1.3, < 3)
82
+ simplecov-html (0.10.1)
83
+ sinatra (2.0.0)
84
+ mustermann (~> 1.0)
85
+ rack (~> 2.0)
86
+ rack-protection (= 2.0.0)
87
+ tilt (~> 2.0)
88
88
  thread_safe (0.3.6)
89
89
  thread_safe (0.3.6-java)
90
90
  tilt (2.0.7)
@@ -105,4 +105,4 @@ DEPENDENCIES
105
105
  simplecov (~> 0.14)
106
106
 
107
107
  BUNDLED WITH
108
- 1.14.3
108
+ 1.14.6
data/README.rdoc CHANGED
@@ -22,7 +22,7 @@ and allowing the job instances to report their state from within their iteration
22
22
 
23
23
  == Installation
24
24
 
25
- Ruby 2.0+ and JRuby 9.0+ are supported.
25
+ Ruby 2.2.2+ and JRuby 9.1+ are supported.
26
26
 
27
27
  resque-state <b>requires Redis >= 1.1</b> (though I recommend getting the latest stable version).
28
28
  You can download Redis here: http://redis.io/ or install it
@@ -139,6 +139,25 @@ again.
139
139
  The next time the job at job_id calls <tt>at</tt> or <tt>tick</tt>, it will start a while
140
140
  loop with a 10 second sleep until Resque::Plugins::State::Hash.unpause is called.
141
141
 
142
+ As of 1.1 pause! can be called from the job itself with an optional string argument
143
+ that allows you to set a specific status along with the paused state if you'd like.
144
+
145
+ === Back it up
146
+
147
+ Perhaps you want a job to be able to undo what it did. Well; that's what revert is
148
+ all about. Jobs that support on_revert can be told to revert and will run insructions
149
+ in that method to undo whatever they might need to. While you could do similar things
150
+ with on_failure on_revert was added to provide a separate status for reverting and
151
+ reverted cases so you can see later which jobs failed (probably due to an error) or
152
+ which ones might have been intentionally reverted by a user or automation.
153
+
154
+ Resque::Plugins::State::Hash.revert(job_id)
155
+
156
+ This is all you need to tell that job to go back and undo it's work. For jobs that
157
+ do not support on_revert those jobs will pause themeslves and leave a note to the user
158
+ that the job doesn't allow that functionality. This lets the user decide if the job
159
+ should be killed or let continue.
160
+
142
161
  === Percent Complete and setting the message
143
162
 
144
163
  Use <tt>at</tt> or <tt>tick</tt> to show progress in your job's <tt>perform</tt> function
@@ -30,7 +30,7 @@ module Resque
30
30
  # end we update the status telling anyone listening to this job that its
31
31
  # complete.
32
32
  module State
33
- VERSION = '1.0.4'.freeze
33
+ VERSION = '1.1.0'.freeze
34
34
 
35
35
  STATUS_QUEUED = 'queued'.freeze
36
36
  STATUS_WORKING = 'working'.freeze
@@ -39,6 +39,8 @@ module Resque
39
39
  STATUS_KILLED = 'killed'.freeze
40
40
  STATUS_PAUSED = 'paused'.freeze
41
41
  STATUS_WAITING = 'waiting'.freeze
42
+ STATUS_REVERTING = 'reverting'.freeze
43
+ STATUS_REVERTED = 'reverted'.freeze
42
44
  STATUSES = [
43
45
  STATUS_QUEUED,
44
46
  STATUS_WORKING,
@@ -46,13 +48,16 @@ module Resque
46
48
  STATUS_FAILED,
47
49
  STATUS_KILLED,
48
50
  STATUS_PAUSED,
49
- STATUS_WAITING
51
+ STATUS_WAITING,
52
+ STATUS_REVERTING,
53
+ STATUS_REVERTED
50
54
  ].freeze
51
55
 
52
56
  autoload :Hash, 'resque/plugins/state/hash'
53
57
 
54
58
  # The error class raised when a job is killed
55
59
  class Killed < RuntimeError; end
60
+ class Revert < RuntimeError; end
56
61
  class NotANumber < RuntimeError; end
57
62
 
58
63
  attr_reader :uuid, :options
@@ -157,9 +162,10 @@ module Resque
157
162
 
158
163
  # Create a new instance with <tt>uuid</tt> and <tt>options</tt>
159
164
  def initialize(uuid, options = {})
160
- @uuid = uuid
161
- @options = options
162
- @logger = Resque.logger
165
+ @reverting = false
166
+ @uuid = uuid
167
+ @options = options
168
+ @logger = Resque.logger
163
169
  end
164
170
 
165
171
  # Run by the Resque::Worker when processing this job. It wraps the
@@ -181,7 +187,21 @@ module Resque
181
187
  rescue Killed
182
188
  Resque::Plugins::State::Hash.killed(uuid)
183
189
  on_killed if respond_to?(:on_killed)
190
+ rescue Revert
191
+ Resque::Plugins::State::Hash.revert(uuid)
192
+ if respond_to?(:on_revert)
193
+ on_revert
194
+ messages = ["Reverted at #{Time.now}"]
195
+ job_status('status' => STATUS_REVERTED,
196
+ 'message' => messages[0])
197
+ else
198
+ @logger.error("Job #{@uuid}: Attempted revert on job with no revert"\
199
+ " support")
200
+ pause!('This job does not support revert functionality')
201
+ end
184
202
  rescue => e
203
+ messages = ["Failed at #{Time.now}: #{e.message}"]
204
+ @logger.error("Job #{@uuid}: #{messages.join(' ')}")
185
205
  failed("The task failed because of an error: #{e}")
186
206
  raise e unless respond_to?(:on_failure)
187
207
  on_failure(e)
@@ -214,6 +234,12 @@ module Resque
214
234
  Resque::Plugins::State::Hash.should_pause?(uuid)
215
235
  end
216
236
 
237
+ # Checks against the revert list if this specific job instance should be
238
+ # paused on the next iteration
239
+ def should_revert?
240
+ Resque::Plugins::State::Hash.should_revert?(uuid)
241
+ end
242
+
217
243
  # Checks against the lock list if this specific job instance should wait
218
244
  # before starting
219
245
  def locked?(key)
@@ -244,6 +270,10 @@ module Resque
244
270
  kill! if should_kill?
245
271
  if should_pause?
246
272
  pause!
273
+ elsif should_revert?
274
+ return revert! unless @reverting
275
+ job_status({ 'status' => STATUS_REVERTING }, *messages)
276
+ @logger.info("Job #{@uuid}: #{messages.join(' ')}")
247
277
  else
248
278
  job_status({ 'status' => STATUS_WORKING }, *messages)
249
279
  @logger.info("Job #{@uuid}: #{messages.join(' ')}")
@@ -256,6 +286,12 @@ module Resque
256
286
  @logger.error("Job #{@uuid}: #{messages.join(' ')}")
257
287
  end
258
288
 
289
+ # set the status to 'reverted' passing along any additional messages
290
+ def reverted(*messages)
291
+ job_status({ 'status' => STATUS_REVERTED }, *messages)
292
+ @logger.error("Job #{@uuid}: #{messages.join(' ')}")
293
+ end
294
+
259
295
  # set the status to 'completed' passing along any addional messages
260
296
  def completed(*messages)
261
297
  job_status({
@@ -275,17 +311,30 @@ module Resque
275
311
  raise Killed
276
312
  end
277
313
 
314
+ # revert the current job, setting the status to 'reverting' and raising
315
+ # <tt>Revert</tt>
316
+ def revert!
317
+ messages = ["Reverting at #{Time.now}"]
318
+ Resque::Plugins::State::Hash.unpause(uuid) if should_pause?
319
+ @reverting = true
320
+ job_status('status' => STATUS_REVERTING,
321
+ 'message' => messages[0])
322
+ @logger.info("Job #{@uuid}: #{messages.join(' ')}")
323
+ raise Revert
324
+ end
325
+
278
326
  # pause the current job, setting the status to 'paused' and sleeping 10
279
327
  # seconds
280
- def pause!
328
+ def pause!(pause_text = nil)
281
329
  Resque::Plugins::State::Hash.pause(uuid)
282
- messages = ["Paused at #{Time.now}"]
330
+ messages = ["Paused at #{Time.now} #{pause_text}"]
283
331
  job_status('status' => STATUS_PAUSED,
284
332
  'message' => messages[0])
285
333
  raise Killed if @testing # Don't loop or complete during testing
286
334
  @logger.info("Job #{@uuid}: #{messages.join(' ')}")
287
335
  while should_pause?
288
336
  kill! if should_kill?
337
+ revert! if should_revert?
289
338
  sleep 10
290
339
  end
291
340
  end
@@ -187,6 +187,24 @@ module Resque
187
187
  redis.sismember(pause_key, uuid)
188
188
  end
189
189
 
190
+ # revert the job at UUID on its next iteration this works by adding the UUID to a
191
+ # revert list (a.k.a. a list of jobs to be reverted. Each iteration the job checks
192
+ # if it _should_ be reverted by calling <tt>tick</tt> or <tt>at</tt>. If so, it sleeps
193
+ # for 10 seconds before checking again if it should continue sleeping
194
+ def self.revert(uuid)
195
+ redis.sadd(revert_key, uuid)
196
+ end
197
+
198
+ # Return the UUIDs of the jobs on the revert list
199
+ def self.revert_ids
200
+ redis.smembers(revert_key)
201
+ end
202
+
203
+ # Check whether a job with UUID is on the revert list
204
+ def self.should_revert?(uuid)
205
+ redis.sismember(revert_key, uuid)
206
+ end
207
+
190
208
  # Set a lock key to allow jobs to run as singletons. Optional timeout in
191
209
  # seconds
192
210
  def self.lock(key, timeout = 3600)
@@ -231,6 +249,10 @@ module Resque
231
249
  '_pause'
232
250
  end
233
251
 
252
+ def self.revert_key
253
+ '_revert'
254
+ end
255
+
234
256
  def self.generate_uuid
235
257
  SecureRandom.hex.to_s
236
258
  end
@@ -320,16 +342,22 @@ module Resque
320
342
  end
321
343
  end
322
344
 
323
- # Can the job be killed? failed, completed, and killed jobs can't be
324
- # killed, for obvious reasons
345
+ # Can the job be killed? failed, completed, reverted, and killed jobs
346
+ # can't be killed, for obvious reasons
325
347
  def killable?
326
- !failed? && !completed? && !killed?
348
+ !failed? && !completed? && !killed? && !reverted?
349
+ end
350
+
351
+ # Can the job be paused? failed, completed, paused, reverted, and killed
352
+ # jobs can't be paused, for obvious reasons
353
+ def pausable?
354
+ !failed? && !completed? && !killed? && !paused? && !reverted?
327
355
  end
328
356
 
329
- # Can the job be paused? failed, completed, paused, and killed jobs
357
+ # Can the job be reverted? failed, completed, reverted, and killed jobs
330
358
  # can't be paused, for obvious reasons
331
359
  def pausable?
332
- !failed? && !completed? && !killed? && !paused?
360
+ !failed? && !completed? && !killed? && !reverted?
333
361
  end
334
362
 
335
363
  unless method_defined?(:to_json)
data/resque-state.gemspec CHANGED
@@ -2,18 +2,18 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: resque-state 1.0.4 ruby lib
5
+ # stub: resque-state 1.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
- s.name = "resque-state".freeze
9
- s.version = "1.0.4"
8
+ s.name = "resque-state"
9
+ s.version = "1.1.0"
10
10
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib".freeze]
13
- s.authors = ["Aaron Quint".freeze, "Nathan V".freeze]
14
- s.date = "2017-05-04"
15
- s.description = "resque-state is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Plugins::State::Hash class which can set/get the statuses of jobs and a Resque::Plugins::State class that, when included, provides easily trackable/killable/pausable jobs.".freeze
16
- s.email = "nathan.v@gmail.com".freeze
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Aaron Quint", "Nathan V"]
14
+ s.date = "2017-06-17"
15
+ s.description = "resque-state is an extension to the resque queue system that provides simple trackable jobs. It provides a Resque::Plugins::State::Hash class which can set/get the statuses of jobs and a Resque::Plugins::State class that, when included, provides easily trackable/killable/pausable jobs."
16
+ s.email = "nathan.v@gmail.com"
17
17
  s.extra_rdoc_files = [
18
18
  "LICENSE",
19
19
  "README.rdoc"
@@ -41,25 +41,25 @@ Gem::Specification.new do |s|
41
41
  "test/test_resque_plugins_state.rb",
42
42
  "test/test_resque_plugins_state_hash.rb"
43
43
  ]
44
- s.homepage = "http://github.com/nathan-v/resque-state".freeze
45
- s.licenses = ["MIT".freeze]
46
- s.rubyforge_project = "nathan-v".freeze
47
- s.rubygems_version = "2.6.11".freeze
48
- s.summary = "resque-state is an extension to the resque queue system that provides simple trackable jobs.".freeze
44
+ s.homepage = "http://github.com/nathan-v/resque-state"
45
+ s.licenses = ["MIT"]
46
+ s.rubyforge_project = "nathan-v"
47
+ s.rubygems_version = "2.4.5"
48
+ s.summary = "resque-state is an extension to the resque queue system that provides simple trackable jobs."
49
49
 
50
50
  if s.respond_to? :specification_version then
51
51
  s.specification_version = 4
52
52
 
53
53
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
- s.add_runtime_dependency(%q<resque>.freeze, ["~> 1.27"])
55
- s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.3"])
54
+ s.add_runtime_dependency(%q<resque>, ["~> 1.27"])
55
+ s.add_development_dependency(%q<jeweler>, ["~> 2.3"])
56
56
  else
57
- s.add_dependency(%q<resque>.freeze, ["~> 1.27"])
58
- s.add_dependency(%q<jeweler>.freeze, ["~> 2.3"])
57
+ s.add_dependency(%q<resque>, ["~> 1.27"])
58
+ s.add_dependency(%q<jeweler>, ["~> 2.3"])
59
59
  end
60
60
  else
61
- s.add_dependency(%q<resque>.freeze, ["~> 1.27"])
62
- s.add_dependency(%q<jeweler>.freeze, ["~> 2.3"])
61
+ s.add_dependency(%q<resque>, ["~> 1.27"])
62
+ s.add_dependency(%q<jeweler>, ["~> 2.3"])
63
63
  end
64
64
  end
65
65
 
data/test/test_helper.rb CHANGED
@@ -72,6 +72,20 @@ class SleeperJob
72
72
  end
73
73
  end
74
74
 
75
+ class RevertJob
76
+ include Resque::Plugins::State
77
+
78
+ def perform
79
+ Resque.redis.set("#{uuid}:iterations", 0)
80
+ 100.times do |num|
81
+ Resque.redis.incr("#{uuid}:iterations")
82
+ at(num, 100, "At #{num} of 100")
83
+ end
84
+ end
85
+
86
+ def on_revert; end
87
+ end
88
+
75
89
  class BasicJob
76
90
  include Resque::Plugins::State
77
91
  end
@@ -290,6 +290,29 @@ class TestResquePluginsStatus < Minitest::Test
290
290
  end
291
291
  end
292
292
 
293
+ describe 'reverting a job' do
294
+ before do
295
+ @uuid = RevertJob.create(num: 100)
296
+ @payload = Resque.pop(:statused)
297
+ Resque::Plugins::State::Hash.revert(@uuid)
298
+ @performed = RevertJob.perform(*@payload['args'])
299
+ @status = Resque::Plugins::State::Hash.get(@uuid)
300
+ end
301
+
302
+ after do
303
+ Resque::Plugins::State::Hash.kill(@uuid)
304
+ end
305
+
306
+ it 'set the status to reverted' do
307
+ assert @status.reverted?
308
+ assert !@status.completed?
309
+ end
310
+
311
+ it 'persist the revert key' do
312
+ assert_includes Resque::Plugins::State::Hash.revert_ids, @uuid
313
+ end
314
+ end
315
+
293
316
  describe 'pausing a job' do
294
317
  before do
295
318
  @uuid = SleeperJob.create(num: 100)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-state
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Quint
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-04 00:00:00.000000000 Z
12
+ date: 2017-06-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: resque
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  version: '0'
92
92
  requirements: []
93
93
  rubyforge_project: nathan-v
94
- rubygems_version: 2.6.11
94
+ rubygems_version: 2.4.5
95
95
  signing_key:
96
96
  specification_version: 4
97
97
  summary: resque-state is an extension to the resque queue system that provides simple