lib/lev/background_job.rb in lev-6.0.0 vs lib/lev/background_job.rb in lev-7.0.0
- old
+ new
@@ -2,75 +2,93 @@
module Lev
class BackgroundJob
attr_reader :id, :status, :progress, :errors
+ STATE_UNQUEUED = 'unqueued'
STATE_QUEUED = 'queued'
STATE_WORKING = 'working'
- STATE_COMPLETED = 'completed'
+ STATE_SUCCEEDED = 'succeeded'
STATE_FAILED = 'failed'
STATE_KILLED = 'killed'
STATE_UNKNOWN = 'unknown'
STATES = [
+ STATE_UNQUEUED,
STATE_QUEUED,
STATE_WORKING,
- STATE_COMPLETED,
+ STATE_SUCCEEDED,
STATE_FAILED,
STATE_KILLED,
STATE_UNKNOWN
].freeze
- def initialize(attrs = {})
- @id = attrs[:id] || attrs['id'] || SecureRandom.uuid
- @status = attrs[:status] || attrs['status'] || STATE_UNKNOWN
- @progress = attrs[:progress] || attrs['progress'] || 0
- @errors = attrs[:errors] || attrs['errors'] || []
+ def self.create
+ new(status: STATE_UNQUEUED).tap do |job|
+ job.save_standard_values
+ end
+ end
- set({ id: id,
- status: status,
- progress: progress,
- errors: errors })
+ # Finds the job with the specified ID and returns it. If no such ID
+ # exists in the store, returns a job with 'unknown' status and sets it
+ # in the store
+ def self.find!(id)
+ find(id) || new({id: id}).tap do |job|
+ job.save_standard_values
+ end
end
+ # Finds the job with the specified ID and returns it. If no such ID
+ # exists in the store, returns nil.
def self.find(id)
+ raise(ArgumentError, "`id` cannot be nil") if id.nil?
+
attrs = { id: id }
- if job = fetch_and_parse(job_key(id))
- attrs.merge!(job)
+ existing_job_attrs = fetch_and_parse(job_key(id))
+
+ if existing_job_attrs.present?
+ attrs.merge!(existing_job_attrs)
+ new(attrs)
else
- attrs.merge!(status: STATE_UNKNOWN)
+ nil
end
-
- new(attrs)
end
def self.all
- job_ids.map { |id| find(id) }
+ job_ids.map { |id| find!(id) }
end
def set_progress(at, out_of = nil)
progress = compute_fractional_progress(at, out_of)
-
- data_to_set = { progress: progress }
- data_to_set[:status] = STATE_COMPLETED if 1.0 == progress
-
- set(data_to_set)
-
- progress
+ set(progress: progress)
end
- (STATES - [STATE_COMPLETED]).each do |state|
+ STATES.each do |state|
define_method("#{state}!") do
set(status: state)
end
+
+ define_method("#{state}?") do
+ status == state
+ end
end
- def completed!
- set({status: STATE_COMPLETED, progress: 1.0})
+ (STATES + %w(completed incomplete)).each do |state|
+ define_singleton_method("#{state}") do
+ all.select{|job| job.send("#{state}?")}
+ end
end
+ def completed?
+ failed? || succeeded?
+ end
+
+ def incomplete?
+ !completed?
+ end
+
def add_error(error, options = { })
options = { is_fatal: false }.merge(options)
@errors << { is_fatal: options[:is_fatal],
code: error.code,
message: error.message,
@@ -90,33 +108,68 @@
else
set(incoming_hash)
end
end
+ def save_standard_values
+ set({
+ id: id,
+ status: status,
+ progress: progress,
+ errors: errors
+ })
+ end
+
def method_missing(method_name, *args)
- instance_variable_get("@#{method_name}") || super
+ get_dynamic_variable(method_name) || super
end
def respond_to?(method_name)
- if method_name.match /\?$/
- super
- else
- instance_variable_get("@#{method_name}").present? || super
- end
+ has_dynamic_variable?(method_name) || super
end
protected
+
RESERVED_KEYS = [:id, :status, :progress, :errors]
+ def initialize(attrs = {})
+ attrs = attrs.stringify_keys
+
+ @id = attrs['id'] || SecureRandom.uuid
+ @status = attrs['status'] || STATE_UNKNOWN
+ @progress = attrs['progress'] || 0
+ @errors = attrs['errors'] || []
+
+ attrs.each do |attr, value|
+ if !instance_variable_defined?("@#{attr}")
+ instance_variable_set("@#{attr}", attrs[attr])
+ end
+ end
+ end
+
def set(incoming_hash)
- incoming_hash = incoming_hash.stringify_keys
- incoming_hash = stored.merge(incoming_hash)
- incoming_hash.each { |k, v| instance_variable_set("@#{k}", v) }
- self.class.store.write(job_key, incoming_hash.to_json)
+ apply_consistency_rules!(incoming_hash)
+ new_hash = stored.merge(incoming_hash)
+ new_hash.each { |k, v| instance_variable_set("@#{k}", v) }
+ self.class.store.write(job_key, new_hash.to_json)
track_job_id
end
+ def apply_consistency_rules!(hash)
+ hash.stringify_keys!
+ hash['progress'] = 1.0 if hash['status'] == 'succeeded'
+ end
+
+ def get_dynamic_variable(name)
+ return nil if !has_dynamic_variable?(name)
+ instance_variable_get("@#{name}")
+ end
+
+ def has_dynamic_variable?(name)
+ !name.match(/\?|\!/) && instance_variable_defined?("@#{name}")
+ end
+
def self.store
Lev.configuration.job_store
end
def self.fetch_and_parse(job_key)
@@ -153,15 +206,9 @@
end
def push(key, new_item)
new_value = (send(key) || []).push(new_item)
set(key => new_value)
- end
-
- STATES.each do |state|
- define_method("#{state}?") do
- status == state
- end
end
def compute_fractional_progress(at, out_of)
if at.nil?
raise IllegalArgument, "Must specify at least `at` argument to `progress` call"