More at rubyonrails.org:

1. What is Active Job?

The Active Job Rails framework allows you to declare background jobs and execute them on a queuing backend. It provides a consistent, high-level interface for common asynchronous tasks such as sending emails, processing data, or performing periodic maintenance tasks.

The goal of background jobs is to move long-running or non-critical work out of the HTTP request-response cycle and into a background queue (such as the default Solid Queue), and keep the web requests fast and responsive. This separation allows applications to perform work asynchronously, scale background processing independently, and execute multiple tasks in parallel without blocking user interactions.

2. Creating Jobs

This section provides a step-by-step guide for defining a job Ruby class and then using the perform_* method to enqueue work to be executed in the background.

2.1. Defining a Job

Active Job provides a Rails generator to create jobs. The following will create a job in the app/jobs directory (with tests under test/jobs):

$ bin/rails generate job guests_cleanup
invoke  test_unit
create    test/jobs/guests_cleanup_job_test.rb
create  app/jobs/guests_cleanup_job.rb

If you don't want to use a generator, you can create your own file inside of app/jobs and define a class that inherits from ApplicationJob.

Here's what a job looks like:

class GuestsCleanupJob < ApplicationJob
  queue_as :default

  def perform(*guests)
    # Do something later
  end
end

Note that you can define the perform method inside a job class with as many arguments as you want.

If your application uses a custom abstract job base class instead of ApplicationJob, you can use the --parent option with the generator. The parent class must itself inherit from ApplicationJob. This can be useful for grouping related functionality in one place.

For example, given a custom abstract job class using queue_as:

class PaymentJob < ApplicationJob
  queue_as :payments
end

You can generate a new job that inherits from it:

$ bin/rails generate job process_payment --parent=payment_job

The above creates a class that will use the payments queue:

class ProcessPaymentJob < PaymentJob
  def perform(*args)
    # Do something later, uses the "payments" queue
  end
end

2.2. Calling the perform_* Methods

Once you have defined a job class with a perform method, you'd typically call it using perform_later to enqueue the work to be executed on a queuing backend. Or use perform_now if you want the job to execute immediately without queueing. Both perform_later and perform_now call perform under the hood.

In the examples below, the methods can be called from anywhere in your Rails application, most commonly from controllers, models, or other jobs.

# To run a job immediately without enqueuing it
GuestsCleanupJob.perform_now(guest)

# To enqueue a job to be performed later
GuestsCleanupJob.perform_later(guest)

Use the set method to specify exactly when to perform a job.

# Enqueue a job to be performed tomorrow at noon
GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)

# Enqueue a job to be performed one week from now
GuestsCleanupJob.set(wait: 1.week).perform_later(guest)

Since both perform_now and perform_later forward their arguments to perform, you can pass as many arguments as defined in perform, including keyword arguments:

GuestsCleanupJob.perform_later(guest1, guest2, filter: "some_filter")

2.2.1. Example: Sending Email

One of the most common jobs in a modern web application is sending emails to users. Active Job can do this outside of the request-response cycle, so the user doesn't have to wait on it. Active Job is integrated with Action Mailer so you can easily send emails asynchronously:

# If you want to send the email now, use #deliver_now
UserMailer.welcome(@user).deliver_now

# If you want to send the email asynchronously, use #deliver_later
UserMailer.welcome(@user).deliver_later

The deliver_now and deliver_later methods are Action Mailer's counterparts to perform_now and perform_later. Under the hood, deliver_later works by enqueuing an ActionMailer::MailDeliveryJob — a built-in Active Job job that Rails provides — which goes through the same queuing pipeline as any job you define yourself, and will eventually call the perform method in your Job class.

2.3. Supported Argument Types for perform

ActiveJob supports the following types of arguments by default:

  • Basic types (NilClass, String, Integer, Float, BigDecimal, TrueClass, FalseClass)
  • Symbol
  • Date
  • Time
  • DateTime
  • ActiveSupport::TimeWithZone
  • ActiveSupport::Duration
  • Hash (Keys should be of String or Symbol type)
  • ActiveSupport::HashWithIndifferentAccess
  • Array
  • Range
  • Module
  • Class

Active Job supports GlobalID for arguments. This makes it possible to pass live Active Record objects to your job instead of class/id pairs, which you then have to manually deserialize.

For example, instead of having to do something like this:

class GuestsCleanupJob < ApplicationJob
  def perform(guests_class, guests_id, depth)
    guests = guests_class.constantize.find(guests_id)
    guest.cleanup(depth)
  end
end

Using GlobalID, you can simply do:

class GuestsCleanupJob < ApplicationJob
  def perform(guest, depth)
    guest.cleanup(depth)
  end
end

This works with any class that mixes in GlobalID::Identification, which is mixed into Active Record by default.

2.3.1. Add Custom Types by Defining Serializers

You can extend the list of supported argument types by defining your own serializer for your custom types. A serializer needs three methods: serialize, deserialize, and klass.

The serialize method converts an object into a simpler representation using only supported types. The recommended approach is to return a Hash with string keys, calling super to let Active Job merge in the serializer's type information:

# app/serializers/money_serializer.rb
class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
  def serialize(money)
    super(
      "amount" => money