lev 7.0.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/README.md +13 -31
- data/lib/lev.rb +13 -6
- data/lib/lev/active_job.rb +8 -9
- data/lib/lev/error.rb +1 -5
- data/lib/lev/errors.rb +6 -6
- data/lib/lev/null_status.rb +34 -0
- data/lib/lev/routine.rb +11 -14
- data/lib/lev/version.rb +1 -1
- data/spec/active_job_routines_spec.rb +7 -20
- data/spec/spec_helper.rb +1 -2
- data/spec/statused_routines_spec.rb +17 -139
- data/spec/support/jobba.rb +13 -0
- metadata +50 -37
- data/lib/lev/background_job.rb +0 -229
- data/lib/lev/no_background_job.rb +0 -26
- data/spec/background_job_spec.rb +0 -129
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
OGVmOWM3NzViMDdlY2UwNTY0MDdmZTM1MGE1MWMyMTQ5NDc1OGRlOQ==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ef0f435c1b9d31f93f019ec4ed9a39a7f5168fd2
|
4
|
+
data.tar.gz: 7b20edd8fc30967cae1a66e409d42d5ec45a54dd
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
M2NmOWI5YWQyZTc3ZGU3MmFkNTk3YWE2YjZiZDgzZTJmMjI3M2Y5YThjMGI1
|
11
|
-
OTRjMjcyNWEyNTdlZmI2MmY5NDg5MzI4YWJmNGMwZmU2MTA3MDY=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZDY5MGZiZGMwNzgxZDExZGE0Njk1ZTRiMDU0YzExYjNjZDRjNWI4NzJkZTdl
|
14
|
-
M2RjODNkNzUyMDYyNzUxNjYzZjhiYWNkZGEyOTJjY2RiMzFjOTY0MjFjNmEx
|
15
|
-
MGYzYzY5YzUzYjRkMDRkNDUyZmRhYjRjYTY4YjA0ZjNjMmVmZjE=
|
6
|
+
metadata.gz: c423328fc9d60a3583776a51d778c0b13252da9f3f93594a3765ed445893e12207b1fd11ac97394a64e8ca6443c478f8d2789596bf01bef40726a1644e2030de
|
7
|
+
data.tar.gz: 0fbf4737caa577039695222f8bf272d7c9f99aaeca9ac57116ce9be8fb139ce5ece6280c1f8fe4b2448b22d88c16a3400baf4efbf761a7011b8fb98e2f21dec9
|
data/README.md
CHANGED
@@ -432,48 +432,30 @@ end
|
|
432
432
|
|
433
433
|
Routines run as ActiveJobs can also publish their status somewhere it can be listened to (e.g. to Redis).
|
434
434
|
|
435
|
-
Routines have a `
|
435
|
+
Routines have a `status` object and can call the following methods:
|
436
436
|
|
437
437
|
* `set_progress(at, out_of = nil)` sets the current progress; can either pass a float between 0.0 and 1.0 or
|
438
438
|
a counter towards a total, e.g. `set_progress(67,212)`.
|
439
|
-
* `queued!` Sets the
|
440
|
-
* `
|
441
|
-
* `succeeded!` Sets the
|
442
|
-
* `failed!` Sets the
|
443
|
-
* `killed!` Sets the
|
444
|
-
* `save(hash)` Takes a hash of key value pairs and writes those keys and values to the
|
445
|
-
* `add_error(
|
439
|
+
* `queued!` Sets the state to 'queued'
|
440
|
+
* `started!` Sets the state to 'working'
|
441
|
+
* `succeeded!` Sets the state to 'succeeded'
|
442
|
+
* `failed!` Sets the state to 'failed'
|
443
|
+
* `killed!` Sets the state to 'killed'
|
444
|
+
* `save(hash)` Takes a hash of key value pairs and writes those keys and values to the status; there are several reserved keys which cannot be used (and which will blow up if you try to use them)
|
445
|
+
* `add_error(error)` takes a Lev `Error` object and adds its data to an array of `errors` in the job status hash.
|
446
446
|
|
447
|
-
Routine
|
447
|
+
Routine status objects also have query methods to check if a status is in a given state, e.g. `queued?`. `completed?` and `incomplete` convenience methods are provided as well. A status is complete if it is failed or succeeded; incomplete if neither. All job routines start in an `unqueued` state and will only stay there if queueing had a problem. Scope-like class methods are provided to return all statuses in a given state.
|
448
448
|
|
449
|
-
For plain vanilla routines not run as an active job, the
|
449
|
+
For plain vanilla routines not run as an active job, the status calls are no-ops. When a routine is invoked with `perform_later`, the status object is created/found using two configuration options that must be set (if you care about the status):
|
450
450
|
|
451
451
|
```ruby
|
452
452
|
Lev.configure do |config|
|
453
|
-
config.
|
453
|
+
config.create_status_proc = ->(*) { Jobba::Status.create! }
|
454
|
+
config.find_status_proc = ->(id) { Jobba::Status.find!(id) }
|
454
455
|
end
|
455
456
|
```
|
456
457
|
|
457
|
-
|
458
|
-
|
459
|
-
1. fetch(key)
|
460
|
-
2. write(key, value)
|
461
|
-
|
462
|
-
The default store is essentially a hash (implemented in `Lev::MemoryStore`). Any `ActiveSupport::Cache::Store` will work.
|
463
|
-
|
464
|
-
A routine's job can be retrieved with `Lev::BackgroundJob.get(uuid_here)`. This just returns a simple hash. Notable keys are
|
465
|
-
|
466
|
-
* `'progress'` which returns the progress as a number between 0.0 and 1.0
|
467
|
-
* `'status'` which returns one of the status strings shown above
|
468
|
-
* `'errors'` which (if present) is an error of error hashes
|
469
|
-
* `'id'` the UUID of the routine / job
|
470
|
-
|
471
|
-
Other routine-specific keys (set with a `save` call) are also present.
|
472
|
-
|
473
|
-
**Notes:**
|
474
|
-
|
475
|
-
1. Don't try to write a job store that uses the ActiveRecord database, as the database changes would only be seen when the routine completes and its transaction is committed.
|
476
|
-
2. Job killing hasn't been implemented yet, but shouldn't be too bad. For routines run in a transaction (which are frankly the only ones you'd want to kill), we can likely kill them by raising an exception or similar to cause a rollback (will need to have good tests to prove that).
|
458
|
+
See the [Jobba README](https://github.com/openstax/jobba) for full details on the status objects.
|
477
459
|
|
478
460
|
## Handlers
|
479
461
|
|
data/lib/lev.rb
CHANGED
@@ -26,12 +26,19 @@ require "lev/delegate_to_routine"
|
|
26
26
|
require "lev/transaction_isolation"
|
27
27
|
|
28
28
|
require 'lev/memory_store'
|
29
|
-
require 'lev/
|
30
|
-
require 'lev/no_background_job'
|
29
|
+
require 'lev/null_status'
|
31
30
|
|
32
31
|
module Lev
|
33
32
|
class << self
|
34
33
|
|
34
|
+
def create_status
|
35
|
+
configuration.create_status_proc.call
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_status(id)
|
39
|
+
configuration.find_status_proc.call(id)
|
40
|
+
end
|
41
|
+
|
35
42
|
###########################################################################
|
36
43
|
#
|
37
44
|
# Configuration machinery.
|
@@ -64,8 +71,8 @@ module Lev
|
|
64
71
|
attr_accessor :security_transgression_error
|
65
72
|
attr_accessor :illegal_argument_error
|
66
73
|
attr_accessor :raise_fatal_errors
|
67
|
-
attr_accessor :
|
68
|
-
attr_accessor :
|
74
|
+
attr_accessor :create_status_proc
|
75
|
+
attr_accessor :find_status_proc
|
69
76
|
attr_accessor :job_class
|
70
77
|
|
71
78
|
def initialize
|
@@ -73,8 +80,8 @@ module Lev
|
|
73
80
|
@security_transgression_error = Lev::SecurityTransgression
|
74
81
|
@illegal_argument_error = Lev::IllegalArgument
|
75
82
|
@raise_fatal_errors = false
|
76
|
-
@
|
77
|
-
@
|
83
|
+
@create_status_proc = ->(*) { NullStatus.new }
|
84
|
+
@find_status_proc = ->(*) { NullStatus.new }
|
78
85
|
@job_class = ::ActiveJob::Base
|
79
86
|
super
|
80
87
|
end
|
data/lib/lev/active_job.rb
CHANGED
@@ -5,19 +5,18 @@ module Lev
|
|
5
5
|
queue_as routine_class.active_job_queue
|
6
6
|
args.push(routine_class.to_s)
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
args.push(job.id)
|
8
|
+
# Create a new status object and push its ID on to the arguments so that
|
9
|
+
# in `perform` it can be used to retrieve the status when the routine is
|
10
|
+
# initialized.
|
11
|
+
status = Lev::create_status
|
12
|
+
args.push(status.id)
|
14
13
|
|
15
14
|
# In theory we'd mark as queued right after the call to super, but this messes
|
16
15
|
# up when the activejob adapter runs the job right away
|
17
|
-
|
16
|
+
status.queued!
|
18
17
|
super(*args, &block)
|
19
18
|
|
20
|
-
|
19
|
+
status.id
|
21
20
|
end
|
22
21
|
|
23
22
|
def perform(*args, &block)
|
@@ -25,7 +24,7 @@ module Lev
|
|
25
24
|
id = args.pop
|
26
25
|
routine_class = Kernel.const_get(args.pop)
|
27
26
|
|
28
|
-
routine_instance = routine_class.new(Lev::
|
27
|
+
routine_instance = routine_class.new(Lev::find_status(id))
|
29
28
|
|
30
29
|
routine_instance.call(*args, &block)
|
31
30
|
end
|
data/lib/lev/error.rb
CHANGED
data/lib/lev/errors.rb
CHANGED
@@ -4,8 +4,8 @@ module Lev
|
|
4
4
|
#
|
5
5
|
class Errors < Array
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(routine_status = nil, raise_fatal_errors = false)
|
8
|
+
@routine_status = routine_status || NullStatus.new
|
9
9
|
@raise_fatal_errors = raise_fatal_errors
|
10
10
|
end
|
11
11
|
|
@@ -16,13 +16,13 @@ module Lev
|
|
16
16
|
return if ignored_error_procs.any?{|proc| proc.call(error)}
|
17
17
|
self.push(error)
|
18
18
|
|
19
|
-
|
19
|
+
routine_status.add_error(error)
|
20
20
|
|
21
21
|
if fail
|
22
|
-
|
22
|
+
routine_status.failed!
|
23
23
|
|
24
24
|
if raise_fatal_errors
|
25
|
-
# Use special FatalError type so Routine doesn't re-add
|
25
|
+
# Use special FatalError type so Routine doesn't re-add status errors
|
26
26
|
raise Lev::FatalError, args.to_a.map { |i| i.join(' ') }.join(' - ')
|
27
27
|
else
|
28
28
|
throw :fatal_errors_encountered
|
@@ -55,7 +55,7 @@ module Lev
|
|
55
55
|
|
56
56
|
protected
|
57
57
|
|
58
|
-
attr_reader :
|
58
|
+
attr_reader :routine_status
|
59
59
|
attr_reader :raise_fatal_errors
|
60
60
|
|
61
61
|
def ignored_error_procs
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Lev::NullStatus
|
2
|
+
attr_reader :id
|
3
|
+
|
4
|
+
def initialize(id=nil)
|
5
|
+
@id = id || "null-status:#{SecureRandom.uuid}"
|
6
|
+
@kill_requested = false
|
7
|
+
end
|
8
|
+
|
9
|
+
def request_kill!
|
10
|
+
@kill_requested = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def kill_requested?
|
14
|
+
@kill_requested
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(*args, &block)
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Provide null object pattern methods for status setter methods called from
|
22
|
+
# within routines; routines should not be using other query methods to check
|
23
|
+
# their own status (they should know it), with the exception of `kill_requested?`
|
24
|
+
|
25
|
+
def set_progress(*); end
|
26
|
+
def save(*); end
|
27
|
+
def add_error(*); end
|
28
|
+
|
29
|
+
def queued!; end
|
30
|
+
def started!; end
|
31
|
+
def succeeded!; end
|
32
|
+
def failed!; end
|
33
|
+
def killed!; end
|
34
|
+
end
|
data/lib/lev/routine.rb
CHANGED
@@ -265,7 +265,7 @@ module Lev
|
|
265
265
|
def call(*args, &block)
|
266
266
|
@after_transaction_blocks = []
|
267
267
|
|
268
|
-
|
268
|
+
status.started!
|
269
269
|
|
270
270
|
begin
|
271
271
|
in_transaction do
|
@@ -284,20 +284,20 @@ module Lev
|
|
284
284
|
block.call
|
285
285
|
end
|
286
286
|
rescue Exception => e
|
287
|
-
# Let exceptions escape but make sure to note the error in the
|
287
|
+
# Let exceptions escape but make sure to note the error in the status
|
288
288
|
# if not already done
|
289
289
|
if !e.is_a?(Lev::FatalError)
|
290
290
|
error = Error.new(code: :exception,
|
291
291
|
message: e.message,
|
292
292
|
data: e.backtrace.first)
|
293
|
-
|
294
|
-
|
293
|
+
status.add_error(error)
|
294
|
+
status.failed!
|
295
295
|
end
|
296
296
|
|
297
297
|
raise e
|
298
298
|
end
|
299
299
|
|
300
|
-
|
300
|
+
status.succeeded! if !errors?
|
301
301
|
|
302
302
|
result
|
303
303
|
end
|
@@ -430,23 +430,20 @@ module Lev
|
|
430
430
|
|
431
431
|
# Note that the parent may neglect to call super, leading to this method never being called.
|
432
432
|
# Do not perform any initialization here that cannot be safely skipped
|
433
|
-
def initialize(
|
434
|
-
# If someone cares about the
|
435
|
-
#
|
436
|
-
@
|
433
|
+
def initialize(status = nil)
|
434
|
+
# If someone cares about the status, they'll pass it in; otherwise all
|
435
|
+
# status updates go into the bit bucket.
|
436
|
+
@status = status || Lev::NullStatus.new
|
437
437
|
end
|
438
438
|
|
439
439
|
protected
|
440
440
|
|
441
441
|
attr_writer :runner
|
442
|
-
|
443
|
-
def job
|
444
|
-
@job ||= Lev::NoBackgroundJob.new
|
445
|
-
end
|
442
|
+
attr_reader :status
|
446
443
|
|
447
444
|
def result
|
448
445
|
@result ||= Result.new(Outputs.new,
|
449
|
-
Errors.new(
|
446
|
+
Errors.new(status, topmost_runner.class.raise_fatal_errors?))
|
450
447
|
end
|
451
448
|
|
452
449
|
def reset_result!
|
data/lib/lev/version.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe 'ActiveJob routines' do
|
4
|
+
|
5
|
+
before { Lev::Jobba.use_jobba }
|
6
|
+
|
4
7
|
context 'default configuration' do
|
5
8
|
class LaterRoutine
|
6
9
|
lev_routine active_job_queue: :something_else
|
@@ -24,22 +27,6 @@ RSpec.describe 'ActiveJob routines' do
|
|
24
27
|
|
25
28
|
expect(queue_name).to eq('something_else')
|
26
29
|
end
|
27
|
-
|
28
|
-
it 'stores all the UUIDs of queued jobs' do
|
29
|
-
Lev.configuration.job_store.clear
|
30
|
-
|
31
|
-
job_id1 = LaterRoutine.perform_later
|
32
|
-
|
33
|
-
expect(Lev::BackgroundJob.send(:job_ids)).to eq([job_id1])
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'does not duplicate BackgroundJobs in `all`' do
|
38
|
-
# Previous track_job_id implementation changed string objects in job_ids
|
39
|
-
# resulting in duplicate objects in `all`
|
40
|
-
Lev.configuration.job_store.clear
|
41
|
-
LaterRoutine.perform_later
|
42
|
-
expect(Lev::BackgroundJob.all.count).to eq 1
|
43
30
|
end
|
44
31
|
|
45
32
|
context 'exception raised' do
|
@@ -56,17 +43,17 @@ RSpec.describe 'ActiveJob routines' do
|
|
56
43
|
end
|
57
44
|
|
58
45
|
it 'lets exception escape, job is failed and has error details' do
|
59
|
-
|
46
|
+
Jobba.all.delete_all!
|
60
47
|
|
61
48
|
expect{
|
62
49
|
ExceptionalRoutine.perform_later
|
63
50
|
}.to raise_error(TypeError)
|
64
51
|
|
65
|
-
|
52
|
+
status = Jobba.all.run.to_a.first
|
66
53
|
|
67
|
-
expect(
|
54
|
+
expect(status).to be_failed
|
68
55
|
|
69
|
-
error =
|
56
|
+
error = status.errors.first
|
70
57
|
|
71
58
|
expect(error["code"]).to eq "exception"
|
72
59
|
expect(error["message"]).to eq "howdy there"
|
data/spec/spec_helper.rb
CHANGED
@@ -5,166 +5,44 @@ class StatusedRoutine
|
|
5
5
|
|
6
6
|
protected
|
7
7
|
def exec
|
8
|
-
|
8
|
+
status.set_progress(9, 10)
|
9
|
+
status.save({'hi' => 'there'})
|
10
|
+
fatal_error(code: 'blah', message: 'hi')
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
14
|
RSpec.describe 'Statused Routines' do
|
13
|
-
|
15
|
+
|
16
|
+
before { Lev::Jobba.use_jobba }
|
14
17
|
|
15
18
|
context 'in a routine' do
|
16
19
|
it 'queues the job object on queue' do
|
17
20
|
id = StatusedRoutine.perform_later
|
18
|
-
|
21
|
+
status = Jobba::Status.find(id)
|
19
22
|
|
20
|
-
expect(
|
23
|
+
expect(status).to be_queued
|
21
24
|
end
|
22
25
|
|
23
26
|
context 'inline activejob mode' do
|
24
27
|
before { ::ActiveJob::Base.queue_adapter = :inline }
|
25
28
|
after { ::ActiveJob::Base.queue_adapter = :test }
|
26
29
|
|
27
|
-
it 'sets job to
|
28
|
-
expect_any_instance_of(
|
30
|
+
it 'sets job to started when called' do
|
31
|
+
expect_any_instance_of(Jobba::Status).to receive(:started!)
|
29
32
|
StatusedRoutine.perform_later
|
30
33
|
end
|
31
34
|
|
32
|
-
it 'completes the
|
35
|
+
it 'completes the status object on completion, returning other data' do
|
33
36
|
id = StatusedRoutine.perform_later
|
34
|
-
|
35
|
-
expect(
|
36
|
-
expect(
|
37
|
+
status = Jobba::Status.find(id)
|
38
|
+
expect(status).to be_failed
|
39
|
+
expect(status.progress).to eq(0.9)
|
40
|
+
expect(status.errors).to contain_exactly(
|
41
|
+
a_hash_including({'code' => 'blah', 'message' => 'hi'})
|
42
|
+
)
|
43
|
+
expect(status.data).to eq ({'hi' => 'there'})
|
37
44
|
end
|
38
45
|
end
|
39
46
|
end
|
40
47
|
|
41
|
-
describe '#save' do
|
42
|
-
it 'saves the hash given and writes them to the job' do
|
43
|
-
job.save(something: 'else')
|
44
|
-
expect(job.something).to eq('else')
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe '#add_error' do
|
49
|
-
it 'adds the error object data to the job object' do
|
50
|
-
errors = Lev::Error.new(code: 'bad', message: 'awful')
|
51
|
-
job.add_error(errors)
|
52
|
-
expect(job.errors).to eq([{ is_fatal: false,
|
53
|
-
code: 'bad',
|
54
|
-
message: 'awful',
|
55
|
-
data: nil }])
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe '#save' do
|
60
|
-
it 'prevents the use of reserved keys' do
|
61
|
-
expect {
|
62
|
-
job.save(progress: 'blocked')
|
63
|
-
}.to raise_error(Lev::IllegalArgument)
|
64
|
-
|
65
|
-
expect {
|
66
|
-
job.save(id: 'blocked')
|
67
|
-
}.to raise_error(Lev::IllegalArgument)
|
68
|
-
|
69
|
-
expect {
|
70
|
-
job.save(status: 'blocked')
|
71
|
-
}.to raise_error(Lev::IllegalArgument)
|
72
|
-
|
73
|
-
expect {
|
74
|
-
job.save(errors: 'blocked')
|
75
|
-
}.to raise_error(Lev::IllegalArgument)
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'saves the hash given and writes them to the job' do
|
79
|
-
job.save(something: 'else')
|
80
|
-
expect(job).to respond_to(:something)
|
81
|
-
expect(job.something).to eq('else')
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'dynamic job setters/getters' do
|
86
|
-
it 'is queued' do
|
87
|
-
expect(job).not_to be_queued
|
88
|
-
job.queued!
|
89
|
-
expect(job).to be_queued
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'is working' do
|
93
|
-
expect(job).not_to be_working
|
94
|
-
job.working!
|
95
|
-
expect(job).to be_working
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'is succeeded' do
|
99
|
-
expect(job).not_to be_succeeded
|
100
|
-
job.succeeded!
|
101
|
-
expect(job).to be_succeeded
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'is failed' do
|
105
|
-
expect(job).not_to be_failed
|
106
|
-
job.failed!
|
107
|
-
expect(job).to be_failed
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'is killed' do
|
111
|
-
expect(job).not_to be_killed
|
112
|
-
job.killed!
|
113
|
-
expect(job).to be_killed
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'is unknown' do
|
117
|
-
expect(job).to be_unknown
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
describe '#set_progress' do
|
122
|
-
it 'sets the progress key on the job object' do
|
123
|
-
job.set_progress(8, 10)
|
124
|
-
expect(job.progress).to eq(0.8)
|
125
|
-
end
|
126
|
-
|
127
|
-
context 'when `out_of` is supplied' do
|
128
|
-
it 'requires a positive `at` float or integer' do
|
129
|
-
expect {
|
130
|
-
job.set_progress(nil, 1)
|
131
|
-
}.to raise_error(Lev::IllegalArgument)
|
132
|
-
|
133
|
-
expect {
|
134
|
-
job.set_progress(-1, 1)
|
135
|
-
}.to raise_error(Lev::IllegalArgument)
|
136
|
-
|
137
|
-
expect {
|
138
|
-
job.set_progress(2, 5)
|
139
|
-
}.not_to raise_error
|
140
|
-
end
|
141
|
-
|
142
|
-
it 'requires `out_of` to be greater than `at`' do
|
143
|
-
expect {
|
144
|
-
job.set_progress(15, 8)
|
145
|
-
}.to raise_error(Lev::IllegalArgument)
|
146
|
-
|
147
|
-
expect {
|
148
|
-
job.set_progress(5, 10)
|
149
|
-
}.not_to raise_error
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context 'without out_of specified' do
|
154
|
-
it 'requires `at` to be a float between 0.0 and 1.0' do
|
155
|
-
expect {
|
156
|
-
job.set_progress(1.1)
|
157
|
-
}.to raise_error(Lev::IllegalArgument)
|
158
|
-
|
159
|
-
expect {
|
160
|
-
job.set_progress(-1)
|
161
|
-
}.to raise_error(Lev::IllegalArgument)
|
162
|
-
|
163
|
-
expect {
|
164
|
-
job.set_progress(0.78)
|
165
|
-
}.not_to raise_error
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
48
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lev
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Slavinsky
|
@@ -14,196 +14,210 @@ dependencies:
|
|
14
14
|
name: activemodel
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: actionpack
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '4.2'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '4.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: activejob
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: transaction_isolation
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: transaction_retry
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: active_attr
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '0'
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: hashie
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: bundler
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: rake
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
143
|
+
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
145
|
version: '0'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: rspec
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
|
-
- -
|
157
|
+
- - ">="
|
158
158
|
- !ruby/object:Gem::Version
|
159
159
|
version: '0'
|
160
160
|
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
|
-
- -
|
164
|
+
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: sqlite3
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
|
-
- -
|
171
|
+
- - ">="
|
172
172
|
- !ruby/object:Gem::Version
|
173
173
|
version: '0'
|
174
174
|
type: :development
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
|
-
- -
|
178
|
+
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
182
|
+
name: byebug
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
|
-
- -
|
185
|
+
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
187
|
version: '0'
|
188
188
|
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
|
-
- -
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: jobba
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
193
207
|
- !ruby/object:Gem::Version
|
194
208
|
version: '0'
|
195
209
|
- !ruby/object:Gem::Dependency
|
196
210
|
name: rails
|
197
211
|
requirement: !ruby/object:Gem::Requirement
|
198
212
|
requirements:
|
199
|
-
- -
|
213
|
+
- - ">="
|
200
214
|
- !ruby/object:Gem::Version
|
201
215
|
version: '0'
|
202
216
|
type: :development
|
203
217
|
prerelease: false
|
204
218
|
version_requirements: !ruby/object:Gem::Requirement
|
205
219
|
requirements:
|
206
|
-
- -
|
220
|
+
- - ">="
|
207
221
|
- !ruby/object:Gem::Version
|
208
222
|
version: '0'
|
209
223
|
description: Ride the rails but don't touch them.
|
@@ -218,7 +232,6 @@ files:
|
|
218
232
|
- Rakefile
|
219
233
|
- lib/lev.rb
|
220
234
|
- lib/lev/active_job.rb
|
221
|
-
- lib/lev/background_job.rb
|
222
235
|
- lib/lev/better_active_model_errors.rb
|
223
236
|
- lib/lev/delegate_to_routine.rb
|
224
237
|
- lib/lev/error.rb
|
@@ -231,7 +244,7 @@ files:
|
|
231
244
|
- lib/lev/handler.rb
|
232
245
|
- lib/lev/handler_helper.rb
|
233
246
|
- lib/lev/memory_store.rb
|
234
|
-
- lib/lev/
|
247
|
+
- lib/lev/null_status.rb
|
235
248
|
- lib/lev/object.rb
|
236
249
|
- lib/lev/outputs.rb
|
237
250
|
- lib/lev/routine.rb
|
@@ -240,7 +253,6 @@ files:
|
|
240
253
|
- lib/lev/utilities.rb
|
241
254
|
- lib/lev/version.rb
|
242
255
|
- spec/active_job_routines_spec.rb
|
243
|
-
- spec/background_job_spec.rb
|
244
256
|
- spec/create_sprocket_spec.rb
|
245
257
|
- spec/deep_merge_spec.rb
|
246
258
|
- spec/delegates_to_spec.rb
|
@@ -254,6 +266,7 @@ files:
|
|
254
266
|
- spec/support/create_sprocket.rb
|
255
267
|
- spec/support/delegated_routine.rb
|
256
268
|
- spec/support/delegating_routine.rb
|
269
|
+
- spec/support/jobba.rb
|
257
270
|
- spec/support/paramify_handler_a.rb
|
258
271
|
- spec/support/paramify_handler_b.rb
|
259
272
|
- spec/support/sprocket.rb
|
@@ -269,23 +282,22 @@ require_paths:
|
|
269
282
|
- lib
|
270
283
|
required_ruby_version: !ruby/object:Gem::Requirement
|
271
284
|
requirements:
|
272
|
-
- -
|
285
|
+
- - ">="
|
273
286
|
- !ruby/object:Gem::Version
|
274
287
|
version: '0'
|
275
288
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
276
289
|
requirements:
|
277
|
-
- -
|
290
|
+
- - ">="
|
278
291
|
- !ruby/object:Gem::Version
|
279
292
|
version: '0'
|
280
293
|
requirements: []
|
281
294
|
rubyforge_project:
|
282
|
-
rubygems_version: 2.4.
|
295
|
+
rubygems_version: 2.4.8
|
283
296
|
signing_key:
|
284
297
|
specification_version: 4
|
285
298
|
summary: Ride the rails but don't touch them.
|
286
299
|
test_files:
|
287
300
|
- spec/active_job_routines_spec.rb
|
288
|
-
- spec/background_job_spec.rb
|
289
301
|
- spec/create_sprocket_spec.rb
|
290
302
|
- spec/deep_merge_spec.rb
|
291
303
|
- spec/delegates_to_spec.rb
|
@@ -299,6 +311,7 @@ test_files:
|
|
299
311
|
- spec/support/create_sprocket.rb
|
300
312
|
- spec/support/delegated_routine.rb
|
301
313
|
- spec/support/delegating_routine.rb
|
314
|
+
- spec/support/jobba.rb
|
302
315
|
- spec/support/paramify_handler_a.rb
|
303
316
|
- spec/support/paramify_handler_b.rb
|
304
317
|
- spec/support/sprocket.rb
|
data/lib/lev/background_job.rb
DELETED
@@ -1,229 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
module Lev
|
4
|
-
class BackgroundJob
|
5
|
-
attr_reader :id, :status, :progress, :errors
|
6
|
-
|
7
|
-
STATE_UNQUEUED = 'unqueued'
|
8
|
-
STATE_QUEUED = 'queued'
|
9
|
-
STATE_WORKING = 'working'
|
10
|
-
STATE_SUCCEEDED = 'succeeded'
|
11
|
-
STATE_FAILED = 'failed'
|
12
|
-
STATE_KILLED = 'killed'
|
13
|
-
STATE_UNKNOWN = 'unknown'
|
14
|
-
|
15
|
-
STATES = [
|
16
|
-
STATE_UNQUEUED,
|
17
|
-
STATE_QUEUED,
|
18
|
-
STATE_WORKING,
|
19
|
-
STATE_SUCCEEDED,
|
20
|
-
STATE_FAILED,
|
21
|
-
STATE_KILLED,
|
22
|
-
STATE_UNKNOWN
|
23
|
-
].freeze
|
24
|
-
|
25
|
-
def self.create
|
26
|
-
new(status: STATE_UNQUEUED).tap do |job|
|
27
|
-
job.save_standard_values
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Finds the job with the specified ID and returns it. If no such ID
|
32
|
-
# exists in the store, returns a job with 'unknown' status and sets it
|
33
|
-
# in the store
|
34
|
-
def self.find!(id)
|
35
|
-
find(id) || new({id: id}).tap do |job|
|
36
|
-
job.save_standard_values
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# Finds the job with the specified ID and returns it. If no such ID
|
41
|
-
# exists in the store, returns nil.
|
42
|
-
def self.find(id)
|
43
|
-
raise(ArgumentError, "`id` cannot be nil") if id.nil?
|
44
|
-
|
45
|
-
attrs = { id: id }
|
46
|
-
|
47
|
-
existing_job_attrs = fetch_and_parse(job_key(id))
|
48
|
-
|
49
|
-
if existing_job_attrs.present?
|
50
|
-
attrs.merge!(existing_job_attrs)
|
51
|
-
new(attrs)
|
52
|
-
else
|
53
|
-
nil
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.all
|
58
|
-
job_ids.map { |id| find!(id) }
|
59
|
-
end
|
60
|
-
|
61
|
-
def set_progress(at, out_of = nil)
|
62
|
-
progress = compute_fractional_progress(at, out_of)
|
63
|
-
set(progress: progress)
|
64
|
-
end
|
65
|
-
|
66
|
-
STATES.each do |state|
|
67
|
-
define_method("#{state}!") do
|
68
|
-
set(status: state)
|
69
|
-
end
|
70
|
-
|
71
|
-
define_method("#{state}?") do
|
72
|
-
status == state
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
(STATES + %w(completed incomplete)).each do |state|
|
77
|
-
define_singleton_method("#{state}") do
|
78
|
-
all.select{|job| job.send("#{state}?")}
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def completed?
|
83
|
-
failed? || succeeded?
|
84
|
-
end
|
85
|
-
|
86
|
-
def incomplete?
|
87
|
-
!completed?
|
88
|
-
end
|
89
|
-
|
90
|
-
def add_error(error, options = { })
|
91
|
-
options = { is_fatal: false }.merge(options)
|
92
|
-
@errors << { is_fatal: options[:is_fatal],
|
93
|
-
code: error.code,
|
94
|
-
message: error.message,
|
95
|
-
data: error.data }
|
96
|
-
set(errors: @errors)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Rails compatibility
|
100
|
-
# returns a Hash of all key-value pairs that have been #set()
|
101
|
-
def as_json(options = {})
|
102
|
-
stored
|
103
|
-
end
|
104
|
-
|
105
|
-
def save(incoming_hash)
|
106
|
-
if reserved = incoming_hash.select { |k, _| RESERVED_KEYS.include?(k) }.first
|
107
|
-
raise IllegalArgument, "Cannot set reserved key: #{reserved[0]}"
|
108
|
-
else
|
109
|
-
set(incoming_hash)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def save_standard_values
|
114
|
-
set({
|
115
|
-
id: id,
|
116
|
-
status: status,
|
117
|
-
progress: progress,
|
118
|
-
errors: errors
|
119
|
-
})
|
120
|
-
end
|
121
|
-
|
122
|
-
def method_missing(method_name, *args)
|
123
|
-
get_dynamic_variable(method_name) || super
|
124
|
-
end
|
125
|
-
|
126
|
-
def respond_to?(method_name)
|
127
|
-
has_dynamic_variable?(method_name) || super
|
128
|
-
end
|
129
|
-
|
130
|
-
protected
|
131
|
-
|
132
|
-
RESERVED_KEYS = [:id, :status, :progress, :errors]
|
133
|
-
|
134
|
-
def initialize(attrs = {})
|
135
|
-
attrs = attrs.stringify_keys
|
136
|
-
|
137
|
-
@id = attrs['id'] || SecureRandom.uuid
|
138
|
-
@status = attrs['status'] || STATE_UNKNOWN
|
139
|
-
@progress = attrs['progress'] || 0
|
140
|
-
@errors = attrs['errors'] || []
|
141
|
-
|
142
|
-
attrs.each do |attr, value|
|
143
|
-
if !instance_variable_defined?("@#{attr}")
|
144
|
-
instance_variable_set("@#{attr}", attrs[attr])
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def set(incoming_hash)
|
150
|
-
apply_consistency_rules!(incoming_hash)
|
151
|
-
new_hash = stored.merge(incoming_hash)
|
152
|
-
new_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
153
|
-
self.class.store.write(job_key, new_hash.to_json)
|
154
|
-
track_job_id
|
155
|
-
end
|
156
|
-
|
157
|
-
def apply_consistency_rules!(hash)
|
158
|
-
hash.stringify_keys!
|
159
|
-
hash['progress'] = 1.0 if hash['status'] == 'succeeded'
|
160
|
-
end
|
161
|
-
|
162
|
-
def get_dynamic_variable(name)
|
163
|
-
return nil if !has_dynamic_variable?(name)
|
164
|
-
instance_variable_get("@#{name}")
|
165
|
-
end
|
166
|
-
|
167
|
-
def has_dynamic_variable?(name)
|
168
|
-
!name.match(/\?|\!/) && instance_variable_defined?("@#{name}")
|
169
|
-
end
|
170
|
-
|
171
|
-
def self.store
|
172
|
-
Lev.configuration.job_store
|
173
|
-
end
|
174
|
-
|
175
|
-
def self.fetch_and_parse(job_key)
|
176
|
-
fetched = store.fetch(job_key)
|
177
|
-
return nil if fetched.nil?
|
178
|
-
JSON.parse(fetched).stringify_keys!
|
179
|
-
end
|
180
|
-
|
181
|
-
def self.job_ids
|
182
|
-
store.fetch(job_key('lev_job_ids')) || []
|
183
|
-
end
|
184
|
-
|
185
|
-
def stored
|
186
|
-
self.class.fetch_and_parse(job_key) || {}
|
187
|
-
end
|
188
|
-
|
189
|
-
def track_job_id
|
190
|
-
ids = self.class.job_ids
|
191
|
-
return if ids.include?(@id)
|
192
|
-
ids << @id
|
193
|
-
self.class.store.write(self.class.job_key('lev_job_ids'), ids)
|
194
|
-
end
|
195
|
-
|
196
|
-
def job_key
|
197
|
-
self.class.job_key(@id)
|
198
|
-
end
|
199
|
-
|
200
|
-
def self.job_key(id)
|
201
|
-
"#{Lev.configuration.job_store_namespace}:#{id}"
|
202
|
-
end
|
203
|
-
|
204
|
-
def has_reserved_keys?(hash)
|
205
|
-
(hash.keys.collect(&:to_sym) & RESERVED_KEYS).any?
|
206
|
-
end
|
207
|
-
|
208
|
-
def push(key, new_item)
|
209
|
-
new_value = (send(key) || []).push(new_item)
|
210
|
-
set(key => new_value)
|
211
|
-
end
|
212
|
-
|
213
|
-
def compute_fractional_progress(at, out_of)
|
214
|
-
if at.nil?
|
215
|
-
raise IllegalArgument, "Must specify at least `at` argument to `progress` call"
|
216
|
-
elsif at < 0
|
217
|
-
raise IllegalArgument, "progress cannot be negative (at=#{at})"
|
218
|
-
elsif out_of && out_of < at
|
219
|
-
raise IllegalArgument, "`out_of` must be greater than `at` in `progress` calls"
|
220
|
-
elsif out_of.nil? && (at < 0 || at > 1)
|
221
|
-
raise IllegalArgument, "If `out_of` not specified, `at` must be in the range [0.0, 1.0]"
|
222
|
-
end
|
223
|
-
|
224
|
-
at.to_f / (out_of || 1).to_f
|
225
|
-
end
|
226
|
-
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Lev
|
2
|
-
class NoBackgroundJob
|
3
|
-
|
4
|
-
# Provide null object pattern methods for background jobs; routines should
|
5
|
-
# not be checking their own status (they should know it), and outside callers
|
6
|
-
# should not be checking status unless the background job is a real one.
|
7
|
-
|
8
|
-
def set_progress(*); end
|
9
|
-
def save(*); end
|
10
|
-
def add_error(*); end
|
11
|
-
|
12
|
-
Lev::BackgroundJob::STATES.each do |state|
|
13
|
-
define_method("#{state}!") do; end
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.method_missing(method_sym, *args, &block)
|
17
|
-
if Lev::BackgroundJob.method_defined?(method_sym)
|
18
|
-
raise NameError,
|
19
|
-
"'#{method_sym}' is Lev::BackgroundJob query method, and those cannot be called on NoBackgroundJob"
|
20
|
-
else
|
21
|
-
super
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
data/spec/background_job_spec.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'lev/active_job'
|
3
|
-
|
4
|
-
describe Lev::BackgroundJob do
|
5
|
-
|
6
|
-
context 'delayed routine' do
|
7
|
-
class DelayedRoutine
|
8
|
-
lev_routine
|
9
|
-
protected
|
10
|
-
def exec; end
|
11
|
-
end
|
12
|
-
|
13
|
-
subject(:job) { described_class.all.last }
|
14
|
-
|
15
|
-
before do
|
16
|
-
Lev.configuration.job_store.clear
|
17
|
-
allow(SecureRandom).to receive(:uuid) { '123abc' }
|
18
|
-
DelayedRoutine.perform_later
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'behaves as a nice ruby object' do
|
22
|
-
expect(job.id).to eq('123abc')
|
23
|
-
expect(job.status).to eq(described_class::STATE_QUEUED)
|
24
|
-
expect(job.progress).to eq(0.0)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'is unknown when not found' do
|
28
|
-
foo = described_class.find!('noooooo')
|
29
|
-
expect(foo.status).to eq(described_class::STATE_UNKNOWN)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'uses as_json' do
|
33
|
-
json = job.as_json
|
34
|
-
|
35
|
-
expect(json).to eq({
|
36
|
-
'id' => '123abc',
|
37
|
-
'status' => described_class::STATE_QUEUED,
|
38
|
-
'progress' => 0.0,
|
39
|
-
'errors' => []
|
40
|
-
})
|
41
|
-
|
42
|
-
job.save(foo: :bar)
|
43
|
-
json = job.as_json
|
44
|
-
|
45
|
-
expect(json['foo']).to eq('bar')
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'generates attributes for custom variables' do
|
49
|
-
job.save(foo: 'bar')
|
50
|
-
|
51
|
-
reloaded_job = Lev::BackgroundJob.find(job.id)
|
52
|
-
|
53
|
-
expect(reloaded_job.respond_to?(:foo)).to be true
|
54
|
-
expect(reloaded_job.foo).to eq('bar')
|
55
|
-
end
|
56
|
-
|
57
|
-
it 'has scopes' do
|
58
|
-
expect(described_class.incomplete.collect(&:id)).to include(job.id)
|
59
|
-
|
60
|
-
job.queued!
|
61
|
-
expect(described_class.incomplete.collect(&:id)).to include(job.id)
|
62
|
-
expect(described_class.queued.collect(&:id)).to include(job.id)
|
63
|
-
|
64
|
-
job.working!
|
65
|
-
expect(described_class.incomplete.collect(&:id)).to include(job.id)
|
66
|
-
expect(described_class.working.collect(&:id)).to include(job.id)
|
67
|
-
|
68
|
-
job.failed!
|
69
|
-
expect(described_class.incomplete.collect(&:id)).not_to include(job.id)
|
70
|
-
expect(described_class.failed.collect(&:id)).to include(job.id)
|
71
|
-
|
72
|
-
job.killed!
|
73
|
-
expect(described_class.incomplete.collect(&:id)).to include(job.id)
|
74
|
-
expect(described_class.killed.collect(&:id)).to include(job.id)
|
75
|
-
|
76
|
-
job.unknown!
|
77
|
-
expect(described_class.incomplete.collect(&:id)).to include(job.id)
|
78
|
-
expect(described_class.unknown.collect(&:id)).to include(job.id)
|
79
|
-
|
80
|
-
job.succeeded!
|
81
|
-
expect(described_class.succeeded.collect(&:id)).to include(job.id)
|
82
|
-
expect(described_class.incomplete.collect(&:id)).not_to include(job.id)
|
83
|
-
expect(described_class.succeeded.collect(&:id)).to include(job.id)
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'has the unqueued scope' do
|
87
|
-
expect(described_class.unqueued.collect(&:id)).to eq []
|
88
|
-
unqueued_job = Lev::BackgroundJob.create
|
89
|
-
expect(described_class.unqueued.collect(&:id)).to include(unqueued_job.id)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
it 'sets progress to 100% when succeeded' do
|
94
|
-
job = described_class.new
|
95
|
-
job.succeeded!
|
96
|
-
expect(job.progress).to eq 1
|
97
|
-
end
|
98
|
-
|
99
|
-
describe '.find!' do
|
100
|
-
let!(:job) { described_class.create }
|
101
|
-
|
102
|
-
it 'does not write to store when job exists' do
|
103
|
-
expect(described_class.store).to_not receive(:write)
|
104
|
-
found_job = described_class.find!(job.id)
|
105
|
-
expect(found_job.as_json).to eq(job.as_json)
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'finds jobs that are not in the store' do
|
109
|
-
found_job = described_class.find!('not-a-real-id')
|
110
|
-
expect(found_job.as_json).to include('status' => 'unknown')
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe '.find' do
|
115
|
-
let!(:job) { described_class.create }
|
116
|
-
|
117
|
-
it 'finds jobs that are in the store' do
|
118
|
-
expect(described_class.store).to_not receive(:write)
|
119
|
-
found_job = described_class.find(job.id)
|
120
|
-
expect(found_job.as_json).to eq(job.as_json)
|
121
|
-
end
|
122
|
-
|
123
|
-
it 'returns nil for jobs not in the store' do
|
124
|
-
found_job = described_class.find('not-a-real-id')
|
125
|
-
expect(found_job).to be_nil
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
end
|