Давайте начнем с API.Я хотел бы иметь что-то вроде следующего.
@available.working? # => true or false, so we know it's running
@available.finished? # => true or false, so we know it's finished (already ran)
Теперь давайте напишем работу.
class AwesomeJob < Struct.new(:options)
def perform
do_something_with(options[:var])
end
end
Пока все хорошо.У нас есть работа.Теперь давайте напишем логику, которая ставит ее в очередь.Поскольку Доступен - модель, ответственная за эту работу, давайте научим ее, как начать эту работу.
class Available < ActiveRecord::Base
def start_working!
Delayed::Job.enqueue(AwesomeJob.new(options))
end
def working?
# not sure what to put here yet
end
def finished?
# not sure what to put here yet
end
end
Итак, как мы узнаем, работает эта работа или нет?Есть несколько способов, но в рельсах кажется правильным, что когда моя модель что-то создает, она обычно ассоциируется с этим.Как мы общаемся?Использование идентификаторов в базе данных.Давайте добавим job_id
к доступной модели.
Пока мы на ней, как мы узнаем, что задание не работает, потому что оно уже закончено или потому что оно еще не началось?Одним из способов является проверка того, что на самом деле сделала работа.Если он создал файл, проверьте, существует ли файл.Если он вычислил значение, проверьте, что результат записан.Однако некоторые задания проверить не так просто, так как может не быть четкого поддающегося проверке результата их работы.Для такого случая вы можете использовать флаг или метку времени в вашей модели.Предполагая, что это наш случай, давайте добавим job_finished_at
отметку времени, чтобы отличить еще не выполненное задание от уже выполненного .
class AddJobIdToAvailable < ActiveRecord::Migration
def self.up
add_column :available, :job_id, :integer
add_column :available, :job_finished_at, :datetime
end
def self.down
remove_column :available, :job_id
remove_column :available, :job_finished_at
end
end
Хорошо.Так что теперь давайте на самом деле свяжем Available
с его работой, как только мы поставим задачу в очередь, изменив метод start_working!
.
def start_working!
job = Delayed::Job.enqueue(AwesomeJob.new(options))
update_attribute(:job_id, job.id)
end
Отлично.К этому моменту я мог написать belongs_to :job
, но нам это на самом деле не нужно.
Так что теперь мы знаем, как написать метод working?
, так просто.
def working?
job_id.present?
end
Но как мы помечаем работу как выполненную?Никто не знает, что работа закончена лучше, чем сама работа.Итак, давайте передадим available_id
на работу (как один из вариантов) и используем его на работе.Для этого нам нужно изменить метод start_working!
для передачи идентификатора.
def start_working!
job = Delayed::Job.enqueue(AwesomeJob.new(options.merge(:available_id => id))
update_attribute(:job_id, job.id)
end
И мы должны добавить логику в работу, чтобы обновить нашу job_finished_at
метку времени, когда она будет завершена.
class AwesomeJob < Struct.new(:options)
def perform
available = Available.find(options[:available_id])
do_something_with(options[:var])
# Depending on whether you consider an error'ed job to be finished
# you may want to put this under an ensure. This way the job
# will be deemed finished even if it error'ed out.
available.update_attribute(:job_finished_at, Time.current)
end
end
С этим кодом мы знаем, как написать наш finished?
метод.
def finished?
job_finished_at.present?
end
И все готово.Теперь мы можем просто опросить @available.working?
и @available.finished?
. Кроме того, вы получаете удобство в определении того, какое именно задание было создано для вашего Доступного, отметив @available.job_id
.Вы можете легко превратить это в реальную ассоциацию, сказав belongs_to :job
.