lev 4.3.0 → 4.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZGMxZTI5ZTEzM2M2Yzc0OWFhMzRmMDg0MDdjZDI3ZmRlNDhmZjM0Mg==
4
+ NTU2OGIyM2MxZDQzZmYwMGVkNmZkZWRiZmE2YjMzZmE2NDQ2MDM3Zg==
5
5
  data.tar.gz: !binary |-
6
- NjkwYjg3YTJjOWEyMThlMjk1YjdiY2RmMDI1YmJhNmQ4ZjUyOGQ2Ng==
6
+ Mjk0OTU3NDBhM2E4MjNmMWRhNmZlMzFhYWU4YjMxNTdiMjEwOWE0MQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YmM3ZDY1MzdlMWQxMjhjMjFjNDhiYTBjOWY0ZjA1YjVjYTlhMzVjODZjNjg3
10
- NGYxOWIwNGQ2OWVmNWE0MjViYTgwYTYxMTZiY2QxYzk4Njg3NWUwZDNkYTk3
11
- MmZhMWY1YWEwMjM1ZWFhZGIwZjU5MTY4ZDAyMDFmNWY5MTk2ZGQ=
9
+ YTU4ZmQ3YjExMGNlNjI4MjNkYWRkOWNkM2YxOTkwYTM4ZGEyY2VlMjM4Y2Ew
10
+ MmFiOTg4YTE3YTU5OTdlZWY3NmQzNWEyNGQyMmFiMzNjZmRjMDA3ODZmYzFk
11
+ OWE0NjU2ZDgyOTljZTg2MGU1MmJjMWEyODIxYjI4ZTI2NzQ5ZTE=
12
12
  data.tar.gz: !binary |-
13
- ZWJhZTc3NjFlNDc2MTI2MWZiNjg2YjAyNGFkYmE3ZjRhY2QzZWEyZWE1MDEw
14
- NDg4ZjMwNGI3NDQ2ZGE0NGI3Mjg0YjFkZDgxZGFlNzA2ZDMxM2NiNjAxNWJk
15
- NzMxM2Y2ODJiYzVlNDMxNzQ1ZDk0OWVkZWQyMzYyNTFhZmRiODc=
13
+ ZjM2ZjFmMTBkMGVkMDgxMWMwOGQ2ZDBmYjY1YWI5ZmFkMTc5ODdlYWFkMDFj
14
+ YmEwMWE0NzU5ODg4MDY0ODA2NTE2ZjdlNWU0NDk0MjI0NDgxOGZkNTA2MGNj
15
+ YThlY2E2NDMxNzEyZjNiNjExMzFmN2IzMzI0NTdjOTI5ZjY3MzY=
data/README.md CHANGED
@@ -433,14 +433,49 @@ class MyRoutine
433
433
  end
434
434
  ```
435
435
 
436
- By default also, jobs use ActiveJob::Base as the class, but you can configure this:
436
+ Routines run as ActiveJobs can also publish their status somewhere it can be listened to (e.g. to Redis).
437
+
438
+ Routines have a `status` object and can call the following methods:
439
+
440
+ * `set_progress(at, out_of = nil)` sets the current progress; can either pass a float between 0.0 and 1.0 or
441
+ a counter towards a total, e.g. `set_progress(67,212)`.
442
+ * `queued!` Sets the status to 'queued'
443
+ * `working!` Sets the status to 'working'
444
+ * `completed!` Sets the status to 'completed'
445
+ * `failed!` Sets the status to 'failed'
446
+ * `killed!` Sets the status to 'killed'
447
+ * `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)
448
+ * `add_error(is_fatal, error)` takes a boolean and a Lev `Error` object and adds its data to an array of `errors` in the status hash.
449
+
450
+ All routines have such a status object. 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 actually records the statuses to a store of your choice. The store is configured in the Lev configuration block, e.g.:
437
451
 
438
452
  ```ruby
439
453
  Lev.configure do |config|
440
- config.active_job_class = TrackableJob
454
+ config.status_store = whatever
441
455
  end
442
456
  ```
443
457
 
458
+ The store needs to respond to the following methods:
459
+
460
+ 1. fetch(key)
461
+ 2. write(key, value)
462
+
463
+ The default store is essentially a hash (implemented in `Lev::MemoryStore`). Any `ActiveSupport::Cache::Store` will work.
464
+
465
+ A routine's status can be retrieved with `Lev::Status.get(uuid_here)`. This just returns a simple hash. Notable keys are
466
+
467
+ * `'progress'` which returns the progress as a number between 0.0 and 1.0
468
+ * `'status'` which returns one of the status strings shown above
469
+ * `'errors'` which (if present) is an error of error hashes
470
+ * `'uuid'` the UUID of the routine / status
471
+
472
+ Other routine-specific keys (set with a `save` call) are also present.
473
+
474
+ **Notes:**
475
+
476
+ 1. Don't try to write a status store that uses the ActiveRecord database, as the database changes would only be seen when the routine completes and its transaction is committed.
477
+ 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).
478
+
444
479
  ## Handlers
445
480
 
446
481
  Handlers are specialized routines that take user input (e.g. form data) and then take an action based on that input. Because all Handlers are Routines, everything discussed above applies to them.
data/lib/lev.rb CHANGED
@@ -24,6 +24,10 @@ require "lev/form_builder"
24
24
  require "lev/delegate_to_routine"
25
25
  require "lev/transaction_isolation"
26
26
 
27
+ require 'lev/active_job'
28
+ require 'lev/memory_store'
29
+ require 'lev/status'
30
+ require 'lev/black_hole_status'
27
31
 
28
32
  module Lev
29
33
  class << self
@@ -55,14 +59,16 @@ module Lev
55
59
  attr_accessor :security_transgression_error
56
60
  attr_accessor :illegal_argument_error
57
61
  attr_accessor :raise_fatal_errors
58
- attr_accessor :active_job_class
62
+ attr_accessor :status_store
63
+ attr_accessor :status_store_namespace
59
64
 
60
65
  def initialize
61
66
  @form_error_class = 'error'
62
67
  @security_transgression_error = Lev::SecurityTransgression
63
68
  @illegal_argument_error = Lev::IllegalArgument
64
69
  @raise_fatal_errors = false
65
- @active_job_class = defined?(ActiveJob) ? ActiveJob::Base : nil
70
+ @status_store = Lev::MemoryStore.new
71
+ @status_store_namespace = "lev_status"
66
72
  super
67
73
  end
68
74
  end
@@ -0,0 +1,33 @@
1
+ if defined?(::ActiveJob)
2
+ module Lev
3
+ module ActiveJob
4
+ class Base < ::ActiveJob::Base
5
+ def self.perform_later(routine_class, *args, &block)
6
+ queue_as routine_class.active_job_queue
7
+ args.push(routine_class.to_s)
8
+
9
+ # To enable tracking of this job's status, create a new Status object
10
+ # and push it on to the arguments so that in `perform` it can be peeled
11
+ # off and handed to the routine instance. The Status UUID is returned
12
+ # so that callers can track the status.
13
+ status = Lev::Status.new
14
+ status.queued!
15
+ args.push(status.uuid)
16
+
17
+ super(*args, &block)
18
+
19
+ status.uuid
20
+ end
21
+
22
+ def perform(*args, &block)
23
+ # Pop arguments added by perform_later
24
+ uuid = args.pop
25
+ routine_class = Kernel.const_get(args.pop)
26
+
27
+ routine_instance = routine_class.new(Lev::Status.new(uuid))
28
+ routine_instance.call(*args, &block)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ module Lev
2
+ class BlackHoleStatus
3
+
4
+ # Provide null object pattern methods for status setters; routines should
5
+ # not be checking their own status (they should know it), and outside callers
6
+ # should not be checking status unless the status object is a real one.
7
+
8
+ def set_progress(*); end
9
+ def save(*); end
10
+ def add_error(*); end
11
+
12
+ Lev::Status::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::Status.new.respond_to?(method_sym)
18
+ raise NameError,
19
+ "'#{method_sym}' is Lev::Status query method, and those cannot be called on BlackHoleStatus"
20
+ else
21
+ super
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -4,12 +4,29 @@ module Lev
4
4
  #
5
5
  class Errors < Array
6
6
 
7
+ def initialize(routine_status = nil, raise_fatal_errors = false)
8
+ @routine_status = routine_status || BlackHoleStatus.new
9
+ @raise_fatal_errors = raise_fatal_errors
10
+ end
11
+
7
12
  def add(fail, args={})
8
13
  args[:kind] ||= :lev
9
14
  error = Error.new(args)
15
+
10
16
  return if ignored_error_procs.any?{|proc| proc.call(error)}
11
17
  self.push(error)
12
- throw :fatal_errors_encountered if fail
18
+
19
+ routine_status.add_error(error, is_fatal: fail)
20
+
21
+ if fail
22
+ routine_status.failed!
23
+
24
+ if raise_fatal_errors
25
+ raise StandardError, args.to_a.map { |i| i.join(' ') }.join(' - ')
26
+ else
27
+ throw :fatal_errors_encountered
28
+ end
29
+ end
13
30
  end
14
31
 
15
32
  def ignore(arg)
@@ -37,6 +54,9 @@ module Lev
37
54
 
38
55
  protected
39
56
 
57
+ attr_reader :routine_status
58
+ attr_reader :raise_fatal_errors
59
+
40
60
  def ignored_error_procs
41
61
  @ignored_error_procs ||= []
42
62
  end
@@ -0,0 +1,17 @@
1
+ module Lev
2
+ class MemoryStore
3
+
4
+ def initialize
5
+ @store = {}
6
+ end
7
+
8
+ def fetch(key)
9
+ @store[key]
10
+ end
11
+
12
+ def write(key, value)
13
+ @store[key] = value
14
+ end
15
+
16
+ end
17
+ end
@@ -185,12 +185,14 @@ module Lev
185
185
  attr_reader :outputs
186
186
  attr_reader :errors
187
187
 
188
- def initialize
189
- @outputs = Outputs.new
190
- @errors = Errors.new
188
+ def initialize(outputs, errors)
189
+ @outputs = outputs
190
+ @errors = errors
191
191
  end
192
192
  end
193
193
 
194
+ attr_reader :uuid
195
+
194
196
  def self.included(base)
195
197
  base.extend(ClassMethods)
196
198
  end
@@ -206,27 +208,10 @@ module Lev
206
208
  result.outputs.send(@express_output)
207
209
  end
208
210
 
209
- if defined?(ActiveJob)
210
- def active_job_class
211
- @active_job_class ||= const_set("ActiveJob",
212
- Class.new(Lev.configuration.active_job_class) do
213
- queue_as do
214
- parent_routine.active_job_queue
215
- end
216
-
217
- def parent_routine
218
- self.class.parent
219
- end
220
-
221
- def perform(*args, &block)
222
- parent_routine.call(*args, &block)
223
- end
224
- end
225
- )
226
- end
227
-
211
+ if defined?(::ActiveJob)
228
212
  def perform_later(*args, &block)
229
- active_job_class.perform_later(*args, &block)
213
+ # Delegate to a subclass of Lev::Routine::ActiveJob::Base
214
+ Lev::ActiveJob::Base.perform_later(self, *args, &block)
230
215
  end
231
216
 
232
217
  def active_job_queue
@@ -282,6 +267,8 @@ module Lev
282
267
  def call(*args, &block)
283
268
  @after_transaction_blocks = []
284
269
 
270
+ status.working!
271
+
285
272
  in_transaction do
286
273
  catch :fatal_errors_encountered do
287
274
  begin
@@ -298,6 +285,8 @@ module Lev
298
285
  block.call
299
286
  end
300
287
 
288
+ status.completed! if !errors?
289
+
301
290
  result
302
291
  end
303
292
 
@@ -402,11 +391,7 @@ module Lev
402
391
  end
403
392
 
404
393
  def fatal_error(args={})
405
- if topmost_runner.class.raise_fatal_errors?
406
- raise StandardError, args.to_a.map { |i| i.join(' ') }.join(' - ')
407
- else
408
- errors.add(true, args)
409
- end
394
+ errors.add(true, args)
410
395
  end
411
396
 
412
397
  def nonfatal_error(args={})
@@ -431,13 +416,29 @@ module Lev
431
416
  @after_transaction_blocks.push(block)
432
417
  end
433
418
 
419
+ # Note that the parent may neglect to call super, leading to this method never being called.
420
+ # Do not perform any initialization here that cannot be safely skipped
421
+ def initialize(status = nil)
422
+ # If someone cares about the status, they'll pass it in; otherwise all
423
+ # status updates go into the bit bucket.
424
+ @status = status
425
+ end
426
+
434
427
  protected
435
428
 
436
- def result
437
- @result ||= Result.new
429
+ attr_writer :runner
430
+
431
+ def status
432
+ @status ||= Lev::BlackHoleStatus.new
438
433
  end
439
434
 
440
- attr_writer :runner
435
+ def result
436
+ @result ||= Result.new(
437
+ Outputs.new,
438
+ Errors.new(status,
439
+ topmost_runner.class.raise_fatal_errors?)
440
+ )
441
+ end
441
442
 
442
443
  def outputs
443
444
  result.outputs
@@ -0,0 +1,125 @@
1
+ require 'json'
2
+
3
+ module Lev
4
+ class Status
5
+ STATE_QUEUED = 'queued'
6
+ STATE_WORKING = 'working'
7
+ STATE_COMPLETED = 'completed'
8
+ STATE_FAILED = 'failed'
9
+ STATE_KILLED = 'killed'
10
+
11
+ STATES = [
12
+ STATE_QUEUED,
13
+ STATE_WORKING,
14
+ STATE_COMPLETED,
15
+ STATE_FAILED,
16
+ STATE_KILLED
17
+ ].freeze
18
+
19
+ attr_reader :uuid
20
+
21
+ def initialize(uuid = nil)
22
+ @uuid = uuid || SecureRandom.uuid
23
+ save
24
+ end
25
+
26
+ def self.find(uuid)
27
+ if status = store.fetch(status_key(uuid))
28
+ JSON.parse(status)
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def set_progress(at, out_of = nil)
35
+ progress = compute_fractional_progress(at, out_of)
36
+
37
+ data_to_set = { progress: progress }
38
+ data_to_set[:state] = STATE_COMPLETED if 1.0 == progress
39
+
40
+ set(data_to_set)
41
+ end
42
+
43
+ STATES.each do |state|
44
+ define_method("#{state}!") do
45
+ set(state: state)
46
+ end
47
+ end
48
+
49
+ def save(hash = {})
50
+ if has_reserved_keys?(hash)
51
+ raise IllegalArgument,
52
+ "Caller cannot specify any reserved keys (#{RESERVED_KEYS})"
53
+ else
54
+ set(hash)
55
+ end
56
+ end
57
+
58
+ def add_error(error, options = { })
59
+ options = { is_fatal: false }.merge(options)
60
+ push('errors', { is_fatal: options[:is_fatal],
61
+ code: error.code,
62
+ message: error.message })
63
+ end
64
+
65
+ def get(key)
66
+ self.class.find(uuid)[key]
67
+ end
68
+
69
+ protected
70
+ RESERVED_KEYS = [:progress, :uuid, :state, :errors]
71
+
72
+ def self.store
73
+ # Nice to get the store from lev config each time so it isn't serialized
74
+ # when activejobs are sent off to places like redis
75
+ Lev.configuration.status_store
76
+ end
77
+
78
+ def set(incoming_hash)
79
+ if existing_settings = self.class.find(uuid)
80
+ incoming_hash = existing_settings.merge(incoming_hash)
81
+ end
82
+
83
+ self.class.store.write(status_key, incoming_hash.to_json)
84
+ end
85
+
86
+ def status_key
87
+ self.class.status_key(uuid)
88
+ end
89
+
90
+ def self.status_key(uuid)
91
+ "#{Lev.configuration.status_store_namespace}:#{uuid}"
92
+ end
93
+
94
+ def has_reserved_keys?(hash)
95
+ (hash.keys.collect(&:to_sym) & RESERVED_KEYS).any?
96
+ end
97
+
98
+ def push(key, new_item)
99
+ new_value = (get(key) || []).push(new_item)
100
+ set(key => new_value)
101
+ end
102
+
103
+ STATES.each do |state|
104
+ define_method("#{state}?") do
105
+ get('state') == state
106
+ end
107
+ end
108
+
109
+ def compute_fractional_progress(at, out_of)
110
+ if at.nil?
111
+ raise IllegalArgument, "Must specify at least `at` argument to `progress` call"
112
+ elsif at < 0
113
+ raise IllegalArgument, "progress cannot be negative (at=#{at})"
114
+ elsif out_of && out_of < at
115
+ raise IllegalArgument, "`out_of` must be greater than `at` in `progress` calls"
116
+ elsif out_of.nil? && (at < 0 || at > 1)
117
+ raise IllegalArgument, "If `out_of` not specified, `at` must be in the range [0.0, 1.0]"
118
+ end
119
+
120
+ at.to_f / (out_of || 1).to_f
121
+ end
122
+
123
+ end
124
+ end
125
+
@@ -1,3 +1,3 @@
1
1
  module Lev
2
- VERSION = "4.3.0"
2
+ VERSION = "4.3.1"
3
3
  end
@@ -1,15 +1,7 @@
1
- require 'active_job'
2
1
  require 'spec_helper'
3
2
 
4
- ActiveJob::Base.queue_adapter = :test
5
- ActiveJob::Base.logger = ::Logger.new(nil)
6
-
7
3
  RSpec.describe 'ActiveJob routines' do
8
4
  context 'default configuration' do
9
- before do
10
- Lev.configure { |c| c.active_job_class = ActiveJob::Base } # default
11
- end
12
-
13
5
  class LaterRoutine
14
6
  lev_routine active_job_queue: :something_else
15
7
 
@@ -29,29 +21,4 @@ RSpec.describe 'ActiveJob routines' do
29
21
  expect(queue_name).to eq('something_else')
30
22
  end
31
23
  end
32
-
33
- context 'specialized configuration' do
34
- before do
35
- Lev.configure { |c| c.active_job_class = SomeOtherJobBase }
36
- end
37
-
38
- class SomeOtherJobBase
39
- def self.queue_as(*args); end
40
- def self.perform_later; end
41
- end
42
-
43
- class NewLaterRoutine
44
- lev_routine
45
- protected
46
- def exec; end
47
- end
48
-
49
- it 'allows configuration of the class' do
50
- allow(SomeOtherJobBase).to receive(:perform_later)
51
-
52
- expect(Lev.configuration.active_job_class).to eq(SomeOtherJobBase)
53
- NewLaterRoutine.perform_later
54
- expect(SomeOtherJobBase).to have_received(:perform_later)
55
- end
56
- end
57
24
  end
@@ -111,7 +111,7 @@ describe Lev::Routine do
111
111
  begin
112
112
  RaiseFatalError.call
113
113
  rescue => e
114
- expect(e.message).to eq('code broken - such disaster')
114
+ expect(e.message).to eq('code broken - such disaster - kind lev')
115
115
  end
116
116
  end
117
117
  end
@@ -4,6 +4,12 @@
4
4
  # loaded once.
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'active_job'
9
+
10
+ ActiveJob::Base.queue_adapter = :test
11
+ ActiveJob::Base.logger = ::Logger.new(nil)
12
+
7
13
  RSpec.configure do |config|
8
14
  config.run_all_when_everything_filtered = true
9
15
  config.filter_run :focus
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+
3
+ class StatusedRoutine
4
+ lev_routine
5
+
6
+ protected
7
+ def exec
8
+ status.set_progress(9, 10)
9
+ end
10
+ end
11
+
12
+ RSpec.describe 'Statused Routines' do
13
+ subject(:status) { Lev::Status.new }
14
+
15
+ context 'in a routine' do
16
+ it 'queues the status object on queue' do
17
+ uuid = StatusedRoutine.perform_later
18
+ status = Lev::Status.find(uuid)
19
+
20
+ expect(status['state']).to eq(Lev::Status::STATE_QUEUED)
21
+ end
22
+
23
+ context 'inline activejob mode' do
24
+ before { ::ActiveJob::Base.queue_adapter = :inline }
25
+ after { ::ActiveJob::Base.queue_adapter = :test }
26
+
27
+ it 'sets status to working when called' do
28
+ expect_any_instance_of(Lev::Status).to receive(:working!)
29
+ StatusedRoutine.perform_later
30
+ end
31
+
32
+ it 'completes the status object on completion, returning other data' do
33
+ uuid = StatusedRoutine.perform_later
34
+ status = Lev::Status.find(uuid)
35
+ expect(status['state']).to eq(Lev::Status::STATE_COMPLETED)
36
+ expect(status['progress']).to eq(0.9)
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#save' do
42
+ it 'prevents the use of reserved keys' do
43
+ expect {
44
+ status.save(progress: 'blocked')
45
+ }.to raise_error(Lev::IllegalArgument)
46
+
47
+ expect {
48
+ status.save(uuid: 'blocked')
49
+ }.to raise_error(Lev::IllegalArgument)
50
+
51
+ expect {
52
+ status.save(state: 'blocked')
53
+ }.to raise_error(Lev::IllegalArgument)
54
+
55
+ expect {
56
+ status.save(errors: 'blocked')
57
+ }.to raise_error(Lev::IllegalArgument)
58
+ end
59
+
60
+ it 'saves the hash given and writes them to the status' do
61
+ status.save(something: 'else')
62
+ expect(status.get('something')).to eq('else')
63
+ end
64
+ end
65
+
66
+ describe '#add_error' do
67
+ it 'adds the error object data to the status object' do
68
+ errors = Lev::Error.new(code: 'bad', message: 'awful')
69
+ status.add_error(errors)
70
+ expect(status.get('errors')).to eq([{ 'is_fatal' => false,
71
+ 'code' => 'bad',
72
+ 'message' => 'awful' }])
73
+ end
74
+ end
75
+
76
+ describe 'dynamic status setters/getters' do
77
+ it 'is queued' do
78
+ expect(status).not_to be_queued
79
+ status.queued!
80
+ expect(status).to be_queued
81
+ end
82
+
83
+ it 'is working' do
84
+ expect(status).not_to be_working
85
+ status.working!
86
+ expect(status).to be_working
87
+ end
88
+
89
+ it 'is completed' do
90
+ expect(status).not_to be_completed
91
+ status.completed!
92
+ expect(status).to be_completed
93
+ end
94
+
95
+ it 'is failed' do
96
+ expect(status).not_to be_failed
97
+ status.failed!
98
+ expect(status).to be_failed
99
+ end
100
+
101
+ it 'is killed' do
102
+ expect(status).not_to be_killed
103
+ status.killed!
104
+ expect(status).to be_killed
105
+ end
106
+ end
107
+
108
+ describe '#set_progress' do
109
+ it 'sets the progress key on the status object' do
110
+ status.set_progress(8, 10)
111
+ progress = status.get('progress')
112
+ expect(progress).to eq(0.8)
113
+ end
114
+
115
+ context 'when `out_of` is supplied' do
116
+ it 'requires a positive `at` float or integer' do
117
+ expect {
118
+ status.set_progress(nil, 1)
119
+ }.to raise_error(Lev::IllegalArgument)
120
+
121
+ expect {
122
+ status.set_progress(-1, 1)
123
+ }.to raise_error(Lev::IllegalArgument)
124
+
125
+ expect {
126
+ status.set_progress(2, 5)
127
+ }.not_to raise_error
128
+ end
129
+
130
+ it 'requires `out_of` to be greater than `at`' do
131
+ expect {
132
+ status.set_progress(15, 8)
133
+ }.to raise_error(Lev::IllegalArgument)
134
+
135
+ expect {
136
+ status.set_progress(5, 10)
137
+ }.not_to raise_error
138
+ end
139
+ end
140
+
141
+ context 'without out_of specified' do
142
+ it 'requires `at` to be a float between 0.0 and 1.0' do
143
+ expect {
144
+ status.set_progress(1.1)
145
+ }.to raise_error(Lev::IllegalArgument)
146
+
147
+ expect {
148
+ status.set_progress(-1)
149
+ }.to raise_error(Lev::IllegalArgument)
150
+
151
+ expect {
152
+ status.set_progress(0.78)
153
+ }.not_to raise_error
154
+ end
155
+ end
156
+
157
+ end
158
+ end
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.3.0
4
+ version: 4.3.1
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-06-12 00:00:00.000000000 Z
11
+ date: 2015-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -217,7 +217,9 @@ files:
217
217
  - README.md
218
218
  - Rakefile
219
219
  - lib/lev.rb
220
+ - lib/lev/active_job.rb
220
221
  - lib/lev/better_active_model_errors.rb
222
+ - lib/lev/black_hole_status.rb
221
223
  - lib/lev/delegate_to_routine.rb
222
224
  - lib/lev/error.rb
223
225
  - lib/lev/error_transferer.rb
@@ -228,9 +230,11 @@ files:
228
230
  - lib/lev/handle_with.rb
229
231
  - lib/lev/handler.rb
230
232
  - lib/lev/handler_helper.rb
233
+ - lib/lev/memory_store.rb
231
234
  - lib/lev/object.rb
232
235
  - lib/lev/outputs.rb
233
236
  - lib/lev/routine.rb
237
+ - lib/lev/status.rb
234
238
  - lib/lev/term_mapper.rb
235
239
  - lib/lev/transaction_isolation.rb
236
240
  - lib/lev/utilities.rb
@@ -246,6 +250,7 @@ files:
246
250
  - spec/spec_helper.rb
247
251
  - spec/sprocket_handler_spec.rb
248
252
  - spec/sprocket_spec.rb
253
+ - spec/statused_routines_spec.rb
249
254
  - spec/support/create_sprocket.rb
250
255
  - spec/support/delegated_routine.rb
251
256
  - spec/support/delegating_routine.rb
@@ -290,6 +295,7 @@ test_files:
290
295
  - spec/spec_helper.rb
291
296
  - spec/sprocket_handler_spec.rb
292
297
  - spec/sprocket_spec.rb
298
+ - spec/statused_routines_spec.rb
293
299
  - spec/support/create_sprocket.rb
294
300
  - spec/support/delegated_routine.rb
295
301
  - spec/support/delegating_routine.rb