New Rails Shiny: ActiveJob

Kyle Szives


One highly anticipated feature of Rails 4.2 is the introduction of the ActiveJob library. Like many components in Rails, ActiveJob serves as the adapter layer on a few of the most popular queuing libraries in the Ruby ecosystem.

With the new ActiveJob library, choosing a queuing library with an unique API will not be something to worry about anymore. Rails now provides a unique queuing interface which allows you to swap out queueing gems to your heart’s desire, without changing your application code. Want to switch from Delayed Job to Backburner? Not a problem. ActiveJob let’s you do that with minimal pain.

Another great feature is complete integration with ActionMailer. Email is always one of those tasks that can be done without the user needing to know the email was sent. ActiveJob also provides the same level of abstraction with the methods deliver_later! and deliver_now!, with the obvious functionality.

Lastly, with the addition of ActiveJob, Rails 4.2 will also include the Global ID library, which provides a unique identifier of a model instance: gid://Application/Model/id. This is particularly useful in job scheduling, since we need to reference a model object rather than the serialized object itself. Instead of the scheduler being involved with the particular model and it’s ID, the scheduler just needs to use the global ID to find the exact model instance.

To fully understand the ActiveJob feature set, we need to take a look at it’s core ActiveJob functionality, the ActionMailer functionality, and Global ID.

Using ActiveJob

For this example, we will be utilizing a Rails 4.2 application (4.2.0.beta2 to be exact) that deals with geolocation. In the application, there is an Account model with fields for zipcode, city, state, latitude, and longitude. Since zipcode yields the results for the other items in the list, this is the only one our application will request from the user.

Below is an example of our account class:

    "id": null,
    "name":  null,
    "city": null,
    "state": null,
    "zipcode": null,
    "latitude": null,
    "longitude": null,
    "created_at": null,
    "updated_at": null

Without a job based system, the simple task of finding this additional data might be too slow of a transaction for the application to do in real time. Typically, the application must go out to a third party service to gather the data. This is the perfect scenario for a background job.

Let’s create our first job for this action!

First, be sure to add gem 'delayed_job_active_record' to your gemfile, run bundle install in the terminal, and follow the directions on the delayed_job github page to fully install Delayed Job.

Once Delayed Job (or your queuing system of choice) is installed, you’ll need to generate the job class from the terminal. By using the following command, a new file will appear in the app/jobs folder:

$ rails g job geolocate_account
create  app/jobs/geolocate_account_job.rb

The above command generates the following class:

class GeolocateAccountJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later

In the above class, the perform action is where all the logic for the task will live. For our application, we want to find the geolocation from the zipcode. We will utilize the geocoder gem, which gives us an API to connect to a third parties geolocation service. Here’s the code:

def perform(account)
  result =

  if result
    account.latitude = result.latitude
    account.longitude = result.longitude =
    account.state = result.state!

Now that we have our job’s perform method complete. Let’s open the console and call this job to get our first account queued up. Since our application is fresh, let’s add a new account first:

Account.create(name: 'Dunder Mifflin', zipcode: '18505')

And then queue up the account with our GeolocateAccountJob:

GeolocateAccountJob.perform_later Account.first

You can see that the job has successfully been enqueued by running Delayed::Job.count. You can also review your logs and see that the job has successfully committed to the database.

Queueing for the Future

Sometimes it is important to be able to queue a job in the future and ActiveJob let’s you do just that. For example, the vanilla way of queueing a job is as follows:

GeolocateAccountJob.perform_later Account.first

However, you also have the ability to queue jobs for the future:

GeolocateAccountJob.set(wait: Date.tomorrow.noon).perform_later Account.first


GeolocateAccountJob.set(wait_until: Account.first

Both of these code snippets will yield the about same results.

You can also set a priority to your queue jobs:

GeolocateAccountJob.set(queue: :low_priority).perform_later Account.first

Rescuing from Failure

Another great feature of ActiveJob is the ability to catch exceptions that happen in your perform method. Using the same GeolocateAccountJob class, let’s modify our perform method to throw a ServiceDown exception:

class GeolocateAccountJob < ActiveJob::Base
  queue_as :default

  def perform(account)
     raise Geolocation::ServiceDown

We can then add a rescue block to handle our exception:

class GeolocateAccountJob < ActiveJob::Base
  queue_as :default

  rescue_from(Geolocation::ServiceDown) do |exception|
    # Handle failed exception

  def perform(account)
     raise Geolocation::ServiceDown

Integration with ActionMailer

Another beneficial feature that has been included in ActiveJob is integration with ActionMailer. Sending email is definitely a function that should be performed via a queue. Instead of using your selected library’s Mailer methods, use the two that ActiveJob natively supports deliver_now! and deliver_later!.

Like its name suggests, deliver_now! executes the send right away. Use this instead of the deprecated deliver method. The method which implements queues is deliver_later!.


Need functionality like set provides? Use these optional parameters!

AccountMailer.welcome(Account.first).deliver_later!(wait: 1.hour)
AccountMailer.welcome(Account.first).deliver_later!(wait_until: 10.hours.from_now)

Global ID

The last feature in the ActiveJob library is Global ID, which creates a URI to represent a model instance. This is helpful in situations relating to jobs. Instead of serializing a whole object or an id/class pair, just save the Global ID of the instance to the job.

At the time of job execution, the Global ID is used to locate the appropriate instance.

Let’s see this in action:

account = Account.first

=> "gid://sample/Account/1"

GlobalID::Locator.locate account.global_id
=> Account:0x007f9746c8a1b0
=> "sample"

=> "Account"

=> Account(id: integer, name: string, city: string, state: string, zipcode: string, latitude: integer, longitude: integer, created_at: datetime, updated_at: datetime)

=> "1"

Notice that a lot of data is stored in relation to the Global ID, allowing you to get a lot of meta data about the object instance.

The not so distant relative of Global ID is Signed Global ID, which is similar to Global ID except it is a signed object. The signed version can be accessed using the sgid method on the object.

=> SignedGlobalID:0x007f97470cac98

=> "BAhJIhtnaWQ6Ly9zYW1wbGUvQWNjb3VudC8xBjoGRVQ=--4b86065bf01ecf72de68ea3d34d69f5241178ea1"

The SignedGlobalID can be used to verify and retrieve a GlobalID and includes the ability to set an expiration date for the signed global ID.

ActiveJob Compatible Libraries

As of the time this article was written, ActiveJob can support the following queuing libraries:

Wrapping Up

ActiveJob is a powerful and much needed addition to the Rails framework. Now that you understand the ActiveJob library, the ActionMailer counterpart, and Global ID, you can use ActiveJob to the fullest. I’d love to hear more about how you utilized ActiveJob for your application and the things you have learned so far. Please feel free to leave a comment. Let’s have a conversation!

CSS Master, 3rd Edition