From 2f364fa00b5d0e65ad9abc193fd14cc30b20e4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sun, 14 Jan 2024 01:40:01 +0100 Subject: [PATCH] Implement job batches --- README.md | 18 ++++++++ lib/activejob-status.rb | 1 + lib/activejob-status/batch.rb | 23 ++++++++++ spec/specs/active_job/status/batch_spec.rb | 52 ++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 lib/activejob-status/batch.rb create mode 100644 spec/specs/active_job/status/batch_spec.rb diff --git a/README.md b/README.md index 0c0e76b..0f78d46 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,24 @@ class MyJob < ActiveJob::Base end ``` +### Grouping jobs into batches + +```ruby +job = MyJob.perform_later +other_job = OtherJob.perform_later + +batch = ActiveJob::Status::Batch.new(job, other_job) +batch.status +# "queued" +``` + +The batch status can be `queued`, `failed`, `completed` or `working`. + +1. The batch is considered `queued` if **all** of the jobs are `queued` +2. The batch is considered `failed` if **one** of the jobs is `failed` +3. The batch is considered `completed` if **all** of the jobs are `completed` +4. The batch is considered `working` in all other circumstances + ## ActiveJob::Status and exceptions Internally, ActiveJob::Status uses `ActiveSupport#rescue_from` to catch every `Exception` to apply the `failed` status diff --git a/lib/activejob-status.rb b/lib/activejob-status.rb index 16ca5c5..c263ff7 100644 --- a/lib/activejob-status.rb +++ b/lib/activejob-status.rb @@ -8,6 +8,7 @@ require "activejob-status/status" require "activejob-status/progress" require "activejob-status/throttle" +require "activejob-status/batch" module ActiveJob module Status diff --git a/lib/activejob-status/batch.rb b/lib/activejob-status/batch.rb new file mode 100644 index 0000000..df1eb9a --- /dev/null +++ b/lib/activejob-status/batch.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module ActiveJob + module Status + class Batch + def initialize(*jobs) + @statuses = jobs.map { |job| ActiveJob::Status.get(job) } + end + + def status + if @statuses.all? { |status| status[:status] == "queued" } + "queued" + elsif @statuses.any? { |status| status[:status] == "failed" } + "failed" + elsif @statuses.all? { |status| status[:status] == "completed" } + "completed" + else + "working" + end + end + end + end +end diff --git a/spec/specs/active_job/status/batch_spec.rb b/spec/specs/active_job/status/batch_spec.rb new file mode 100644 index 0000000..7a83c62 --- /dev/null +++ b/spec/specs/active_job/status/batch_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require_relative "../../../spec_helper" +require_relative "../../../jobs/test_jobs" + +RSpec.describe ActiveJob::Status::Batch do + describe "#status" do + it "returns queued when all jobs are queued" do + first_job = BaseJob.perform_later + second_job = BaseJob.perform_later + batch = described_class.new(first_job, second_job) + + ActiveJob::Status.get(first_job).update(status: "queued") + ActiveJob::Status.get(second_job).update(status: "queued") + + expect(batch.status).to eq("queued") + end + + it "returns failed when one job is failed" do + first_job = BaseJob.perform_later + second_job = BaseJob.perform_later + batch = described_class.new(first_job, second_job) + + ActiveJob::Status.get(first_job).update(status: "failed") + ActiveJob::Status.get(second_job).update(status: "completed") + + expect(batch.status).to eq("failed") + end + + it "returns completed when all jobs are completed" do + first_job = BaseJob.perform_later + second_job = BaseJob.perform_later + batch = described_class.new(first_job, second_job) + + ActiveJob::Status.get(first_job).update(status: "completed") + ActiveJob::Status.get(second_job).update(status: "completed") + + expect(batch.status).to eq("completed") + end + + it "returns working in other cases" do + first_job = BaseJob.perform_later + second_job = BaseJob.perform_later + batch = described_class.new(first_job, second_job) + + ActiveJob::Status.get(first_job).update(status: "queued") + ActiveJob::Status.get(second_job).update(status: "working") + + expect(batch.status).to eq("working") + end + end +end