lev 5.0.0 → 6.0.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 +8 -8
- data/README.md +1 -4
- data/lib/lev/background_job.rb +20 -11
- data/lib/lev/error_transferer.rb +3 -3
- data/lib/lev/errors.rb +2 -1
- data/lib/lev/exceptions.rb +1 -0
- data/lib/lev/routine.rb +18 -6
- data/lib/lev/version.rb +1 -1
- data/spec/active_job_routines_spec.rb +40 -0
- data/spec/background_job_spec.rb +54 -0
- data/spec/statused_routines_spec.rb +3 -2
- metadata +4 -6
- data/spec/errors_spec.rb +0 -5
- data/spec/lev/status_spec.rb +0 -44
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YmVhODFiNzc4NGRkZTAxYTg5MmVjZjg5OWRlMDVlNDUwMmVlNDIyYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZDU5YjNkMDRjYmE1N2VkYzQ1Njg5OWZjMDJlNzgyMjlmZDFiMzU1Yg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDRlMDY4Y2NjMTg2OWJlNjVjZDVjNWVmYzRlNDRiOGYyZGEwNzFmYWUwNWRm
|
10
|
+
YWEwODRjZjcxNTU3ZjExNTYxYWZkYzI4NDRmMjUzZDk3ZWMyN2UzMjE4MDA1
|
11
|
+
NDU0YmI1MDljNGNkZDlhMTI5YWUzN2ZkMGZiMjYzMjVhMzhhMzk=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NzJjOGNjZmEyZDJmYWQxY2U1Y2I5Yjc5Y2I3NzIxYzE3YjAxNDQ1ZjMzMjdj
|
14
|
+
MTJlNzRkN2U1MGU4MDM2NWRlMGYzNTlkZWI5N2UyZjc4M2RlODAxYmZmZjFj
|
15
|
+
YTc5MjExNjdhMzM2NWMyYTliYjNhZDA5N2RlM2Q3MDk1MjFhZTc=
|
data/README.md
CHANGED
@@ -100,10 +100,7 @@ end
|
|
100
100
|
|
101
101
|
Additionally, see below for a discussion on how to transfer errors from ActiveRecord models.
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
result = MyRoutine.call(42)
|
106
|
-
result.errors.reraise_exception! # does nothing if there were no exception errors
|
103
|
+
If an exception is raised in a routine, it will bubble out. It will also fail the job status and add information about the exception to the job error list.
|
107
104
|
|
108
105
|
Relatedly, a convenience method is provided if the caller wants to raise an exception if there were any errors returned (whether or not they themselves were caused by an exception)
|
109
106
|
|
data/lib/lev/background_job.rb
CHANGED
@@ -23,7 +23,7 @@ module Lev
|
|
23
23
|
def initialize(attrs = {})
|
24
24
|
@id = attrs[:id] || attrs['id'] || SecureRandom.uuid
|
25
25
|
@status = attrs[:status] || attrs['status'] || STATE_UNKNOWN
|
26
|
-
@progress = attrs[:progress] || attrs['progress'] ||
|
26
|
+
@progress = attrs[:progress] || attrs['progress'] || 0
|
27
27
|
@errors = attrs[:errors] || attrs['errors'] || []
|
28
28
|
|
29
29
|
set({ id: id,
|
@@ -35,8 +35,8 @@ module Lev
|
|
35
35
|
def self.find(id)
|
36
36
|
attrs = { id: id }
|
37
37
|
|
38
|
-
if job =
|
39
|
-
attrs.merge!(
|
38
|
+
if job = fetch_and_parse(job_key(id))
|
39
|
+
attrs.merge!(job)
|
40
40
|
else
|
41
41
|
attrs.merge!(status: STATE_UNKNOWN)
|
42
42
|
end
|
@@ -59,17 +59,22 @@ module Lev
|
|
59
59
|
progress
|
60
60
|
end
|
61
61
|
|
62
|
-
STATES.each do |state|
|
62
|
+
(STATES - [STATE_COMPLETED]).each do |state|
|
63
63
|
define_method("#{state}!") do
|
64
64
|
set(status: state)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
+
def completed!
|
69
|
+
set({status: STATE_COMPLETED, progress: 1.0})
|
70
|
+
end
|
71
|
+
|
68
72
|
def add_error(error, options = { })
|
69
73
|
options = { is_fatal: false }.merge(options)
|
70
74
|
@errors << { is_fatal: options[:is_fatal],
|
71
75
|
code: error.code,
|
72
|
-
message: error.message
|
76
|
+
message: error.message,
|
77
|
+
data: error.data }
|
73
78
|
set(errors: @errors)
|
74
79
|
end
|
75
80
|
|
@@ -103,6 +108,7 @@ module Lev
|
|
103
108
|
RESERVED_KEYS = [:id, :status, :progress, :errors]
|
104
109
|
|
105
110
|
def set(incoming_hash)
|
111
|
+
incoming_hash = incoming_hash.stringify_keys
|
106
112
|
incoming_hash = stored.merge(incoming_hash)
|
107
113
|
incoming_hash.each { |k, v| instance_variable_set("@#{k}", v) }
|
108
114
|
self.class.store.write(job_key, incoming_hash.to_json)
|
@@ -113,22 +119,25 @@ module Lev
|
|
113
119
|
Lev.configuration.job_store
|
114
120
|
end
|
115
121
|
|
122
|
+
def self.fetch_and_parse(job_key)
|
123
|
+
fetched = store.fetch(job_key)
|
124
|
+
return nil if fetched.nil?
|
125
|
+
JSON.parse(fetched).stringify_keys!
|
126
|
+
end
|
127
|
+
|
116
128
|
def self.job_ids
|
117
129
|
store.fetch(job_key('lev_job_ids')) || []
|
118
130
|
end
|
119
131
|
|
120
132
|
def stored
|
121
|
-
|
122
|
-
JSON.parse(found)
|
123
|
-
else
|
124
|
-
{}
|
125
|
-
end
|
133
|
+
self.class.fetch_and_parse(job_key) || {}
|
126
134
|
end
|
127
135
|
|
128
136
|
def track_job_id
|
129
137
|
ids = self.class.job_ids
|
138
|
+
return if ids.include?(@id)
|
130
139
|
ids << @id
|
131
|
-
self.class.store.write(self.class.job_key('lev_job_ids'), ids
|
140
|
+
self.class.store.write(self.class.job_key('lev_job_ids'), ids)
|
132
141
|
end
|
133
142
|
|
134
143
|
def job_key
|
data/lib/lev/error_transferer.rb
CHANGED
@@ -7,11 +7,11 @@ module Lev
|
|
7
7
|
when ActiveRecord::Base, Lev::Paramifier
|
8
8
|
source.errors.each_with_type_and_message do |attribute, type, message|
|
9
9
|
target_routine.nonfatal_error(
|
10
|
-
code: type,
|
10
|
+
code: type,
|
11
11
|
data: {
|
12
12
|
model: source,
|
13
13
|
attribute: attribute
|
14
|
-
},
|
14
|
+
},
|
15
15
|
kind: :activerecord,
|
16
16
|
message: message,
|
17
17
|
offending_inputs: input_mapper.map(attribute)
|
@@ -38,4 +38,4 @@ module Lev
|
|
38
38
|
|
39
39
|
end
|
40
40
|
|
41
|
-
end
|
41
|
+
end
|
data/lib/lev/errors.rb
CHANGED
@@ -22,7 +22,8 @@ module Lev
|
|
22
22
|
routine_job.failed!
|
23
23
|
|
24
24
|
if raise_fatal_errors
|
25
|
-
|
25
|
+
# Use special FatalError type so Routine doesn't re-add job errors
|
26
|
+
raise Lev::FatalError, args.to_a.map { |i| i.join(' ') }.join(' - ')
|
26
27
|
else
|
27
28
|
throw :fatal_errors_encountered
|
28
29
|
end
|
data/lib/lev/exceptions.rb
CHANGED
data/lib/lev/routine.rb
CHANGED
@@ -269,9 +269,9 @@ module Lev
|
|
269
269
|
|
270
270
|
job.working!
|
271
271
|
|
272
|
-
|
273
|
-
|
274
|
-
|
272
|
+
begin
|
273
|
+
in_transaction do
|
274
|
+
catch :fatal_errors_encountered do
|
275
275
|
if self.class.delegates_to
|
276
276
|
run(self.class.delegates_to, *args, &block)
|
277
277
|
else
|
@@ -279,10 +279,22 @@ module Lev
|
|
279
279
|
end
|
280
280
|
end
|
281
281
|
end
|
282
|
-
end
|
283
282
|
|
284
|
-
|
285
|
-
|
283
|
+
@after_transaction_blocks.each do |block|
|
284
|
+
block.call
|
285
|
+
end
|
286
|
+
rescue Exception => e
|
287
|
+
# Let exceptions escape but make sure to note the error in the job
|
288
|
+
# if not already done
|
289
|
+
if !e.is_a?(Lev::FatalError)
|
290
|
+
error = Error.new(code: :exception,
|
291
|
+
message: e.message,
|
292
|
+
data: e.backtrace.first)
|
293
|
+
job.add_error(error, is_fatal: true)
|
294
|
+
job.failed!
|
295
|
+
end
|
296
|
+
|
297
|
+
raise e
|
286
298
|
end
|
287
299
|
|
288
300
|
job.completed! if !errors?
|
data/lib/lev/version.rb
CHANGED
@@ -33,4 +33,44 @@ RSpec.describe 'ActiveJob routines' do
|
|
33
33
|
expect(Lev::BackgroundJob.send(:job_ids)).to eq([job_id1])
|
34
34
|
end
|
35
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
|
+
end
|
44
|
+
|
45
|
+
context 'exception raised' do
|
46
|
+
before { ::ActiveJob::Base.queue_adapter = :inline }
|
47
|
+
after { ::ActiveJob::Base.queue_adapter = :test }
|
48
|
+
|
49
|
+
class ExceptionalRoutine
|
50
|
+
lev_routine
|
51
|
+
|
52
|
+
protected
|
53
|
+
def exec
|
54
|
+
raise TypeError, 'howdy there'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'lets exception escape, job is failed and has error details' do
|
59
|
+
Lev.configuration.job_store.clear
|
60
|
+
|
61
|
+
expect{
|
62
|
+
ExceptionalRoutine.perform_later
|
63
|
+
}.to raise_error(TypeError)
|
64
|
+
|
65
|
+
job = Lev::BackgroundJob.all.first
|
66
|
+
|
67
|
+
expect(job.status).to eq Lev::BackgroundJob::STATE_FAILED
|
68
|
+
|
69
|
+
error = job.errors.first
|
70
|
+
|
71
|
+
expect(error["code"]).to eq "exception"
|
72
|
+
expect(error["message"]).to eq "howdy there"
|
73
|
+
expect(error["data"]).to be_a String
|
74
|
+
end
|
75
|
+
end
|
36
76
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lev::BackgroundJob do
|
4
|
+
|
5
|
+
context 'delayed routine' do
|
6
|
+
class DelayedRoutine
|
7
|
+
lev_routine
|
8
|
+
protected
|
9
|
+
def exec; end
|
10
|
+
end
|
11
|
+
|
12
|
+
subject(:job) { described_class.all.last }
|
13
|
+
|
14
|
+
before do
|
15
|
+
Lev.configuration.job_store.clear
|
16
|
+
allow(SecureRandom).to receive(:uuid) { '123abc' }
|
17
|
+
DelayedRoutine.perform_later
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'behaves as a nice ruby object' do
|
21
|
+
expect(job.id).to eq('123abc')
|
22
|
+
expect(job.status).to eq(Lev::BackgroundJob::STATE_QUEUED)
|
23
|
+
expect(job.progress).to eq(0.0)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'is unknown when not found' do
|
27
|
+
foo = described_class.find('noooooo')
|
28
|
+
expect(foo.status).to eq(Lev::BackgroundJob::STATE_UNKNOWN)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'uses as_json' do
|
32
|
+
json = job.as_json
|
33
|
+
|
34
|
+
expect(json).to eq({
|
35
|
+
'id' => '123abc',
|
36
|
+
'status' => Lev::BackgroundJob::STATE_QUEUED,
|
37
|
+
'progress' => 0.0,
|
38
|
+
'errors' => []
|
39
|
+
})
|
40
|
+
|
41
|
+
job.save(foo: :bar)
|
42
|
+
json = job.as_json
|
43
|
+
|
44
|
+
expect(json['foo']).to eq('bar')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'sets progress to 100% when completed' do
|
49
|
+
job = Lev::BackgroundJob.new
|
50
|
+
job.completed!
|
51
|
+
expect(job.progress).to eq 1
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -33,7 +33,7 @@ RSpec.describe 'Statused Routines' do
|
|
33
33
|
id = StatusedRoutine.perform_later
|
34
34
|
job = Lev::BackgroundJob.find(id)
|
35
35
|
expect(job.status).to eq(Lev::BackgroundJob::STATE_COMPLETED)
|
36
|
-
expect(job.progress).to eq(0
|
36
|
+
expect(job.progress).to eq(1.0)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -51,7 +51,8 @@ RSpec.describe 'Statused Routines' do
|
|
51
51
|
job.add_error(errors)
|
52
52
|
expect(job.errors).to eq([{ is_fatal: false,
|
53
53
|
code: 'bad',
|
54
|
-
message: 'awful'
|
54
|
+
message: 'awful',
|
55
|
+
data: nil }])
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lev
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Slavinsky
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -240,11 +240,10 @@ files:
|
|
240
240
|
- lib/lev/utilities.rb
|
241
241
|
- lib/lev/version.rb
|
242
242
|
- spec/active_job_routines_spec.rb
|
243
|
+
- spec/background_job_spec.rb
|
243
244
|
- spec/create_sprocket_spec.rb
|
244
245
|
- spec/deep_merge_spec.rb
|
245
246
|
- spec/delegates_to_spec.rb
|
246
|
-
- spec/errors_spec.rb
|
247
|
-
- spec/lev/status_spec.rb
|
248
247
|
- spec/outputs_spec.rb
|
249
248
|
- spec/paramify_handler_spec.rb
|
250
249
|
- spec/routine_spec.rb
|
@@ -286,11 +285,10 @@ specification_version: 4
|
|
286
285
|
summary: Ride the rails but don't touch them.
|
287
286
|
test_files:
|
288
287
|
- spec/active_job_routines_spec.rb
|
288
|
+
- spec/background_job_spec.rb
|
289
289
|
- spec/create_sprocket_spec.rb
|
290
290
|
- spec/deep_merge_spec.rb
|
291
291
|
- spec/delegates_to_spec.rb
|
292
|
-
- spec/errors_spec.rb
|
293
|
-
- spec/lev/status_spec.rb
|
294
292
|
- spec/outputs_spec.rb
|
295
293
|
- spec/paramify_handler_spec.rb
|
296
294
|
- spec/routine_spec.rb
|
data/spec/errors_spec.rb
DELETED
data/spec/lev/status_spec.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe Lev::BackgroundJob do
|
4
|
-
class DelayedRoutine
|
5
|
-
lev_routine
|
6
|
-
protected
|
7
|
-
def exec; end
|
8
|
-
end
|
9
|
-
|
10
|
-
subject(:job) { described_class.all.last }
|
11
|
-
|
12
|
-
before do
|
13
|
-
Lev.configuration.job_store.clear
|
14
|
-
allow(SecureRandom).to receive(:uuid) { '123abc' }
|
15
|
-
DelayedRoutine.perform_later
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'behaves as a nice ruby object' do
|
19
|
-
expect(job.id).to eq('123abc')
|
20
|
-
expect(job.status).to eq(Lev::BackgroundJob::STATE_QUEUED)
|
21
|
-
expect(job.progress).to eq(0.0)
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'is unknown when not found' do
|
25
|
-
foo = described_class.find('noooooo')
|
26
|
-
expect(foo.status).to eq(Lev::BackgroundJob::STATE_UNKNOWN)
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'uses as_json' do
|
30
|
-
json = job.as_json
|
31
|
-
|
32
|
-
expect(json).to eq({
|
33
|
-
'id' => '123abc',
|
34
|
-
'status' => Lev::BackgroundJob::STATE_QUEUED,
|
35
|
-
'progress' => 0.0,
|
36
|
-
'errors' => []
|
37
|
-
})
|
38
|
-
|
39
|
-
job.save(foo: :bar)
|
40
|
-
json = job.as_json
|
41
|
-
|
42
|
-
expect(json['foo']).to eq('bar')
|
43
|
-
end
|
44
|
-
end
|