aboutsummaryrefslogtreecommitdiffstats
path: root/guides/source/active_job_basics.md
diff options
context:
space:
mode:
Diffstat (limited to 'guides/source/active_job_basics.md')
-rw-r--r--guides/source/active_job_basics.md179
1 files changed, 132 insertions, 47 deletions
diff --git a/guides/source/active_job_basics.md b/guides/source/active_job_basics.md
index 2fd242cb2c..e36c0f899f 100644
--- a/guides/source/active_job_basics.md
+++ b/guides/source/active_job_basics.md
@@ -1,15 +1,17 @@
+**DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.**
+
Active Job Basics
=================
This guide provides you with all you need to get started in creating,
-enqueueing and executing background jobs.
+enqueuing and executing background jobs.
After reading this guide, you will know:
* How to create jobs.
* How to enqueue jobs.
* How to run jobs in the background.
-* How to send emails from your application async.
+* How to send emails from your application asynchronously.
--------------------------------------------------------------------------------
@@ -18,19 +20,22 @@ Introduction
------------
Active Job is a framework for declaring jobs and making them run on a variety
-of queueing backends. These jobs can be everything from regularly scheduled
+of queuing backends. These jobs can be everything from regularly scheduled
clean-ups, to billing charges, to mailings. Anything that can be chopped up
into small units of work and run in parallel, really.
-The Purpose of the Active Job
+The Purpose of Active Job
-----------------------------
The main point is to ensure that all Rails apps will have a job infrastructure
-in place, even if it's in the form of an "immediate runner". We can then have
-framework features and other gems build on top of that, without having to
-worry about API differences between various job runners such as Delayed Job
-and Resque. Picking your queuing backend becomes more of an operational concern,
-then. And you'll be able to switch between them without having to rewrite your jobs.
+in place. We can then have framework features and other gems build on top of that,
+without having to worry about API differences between various job runners such as
+Delayed Job and Resque. Picking your queuing backend becomes more of an operational
+concern, then. And you'll be able to switch between them without having to rewrite
+your jobs.
+
+NOTE: Rails by default comes with an "immediate runner" queuing implementation.
+That means that each job that has been enqueued will run immediately.
Creating a Job
@@ -41,10 +46,12 @@ This section will provide a step-by-step guide to creating a job and enqueuing i
### Create the Job
Active Job provides a Rails generator to create jobs. The following will create a
-job in `app/jobs`:
+job in `app/jobs` (with an attached test case under `test/jobs`):
```bash
$ bin/rails generate job guests_cleanup
+invoke test_unit
+create test/jobs/guests_cleanup_job_test.rb
create app/jobs/guests_cleanup_job.rb
```
@@ -52,12 +59,8 @@ You can also create a job that will run on a specific queue:
```bash
$ bin/rails generate job guests_cleanup --queue urgent
-create app/jobs/guests_cleanup_job.rb
```
-As you can see, you can generate jobs just like you use other generators with
-Rails.
-
If you don't want to use a generator, you could create your own file inside of
`app/jobs`, just make sure that it inherits from `ActiveJob::Base`.
@@ -67,52 +70,85 @@ Here's what a job looks like:
class GuestsCleanupJob < ActiveJob::Base
queue_as :default
- def perform(*args)
+ def perform(*guests)
# Do something later
end
end
```
+Note that you can define `perform` with as many arguments as you want.
+
### Enqueue the Job
Enqueue a job like so:
```ruby
-MyJob.perform_later record # Enqueue a job to be performed as soon the queueing system is free.
+# Enqueue a job to be performed as soon as the queuing system is
+# free.
+GuestsCleanupJob.perform_later guest
```
```ruby
-MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record) # Enqueue a job to be performed tomorrow at noon.
+# Enqueue a job to be performed tomorrow at noon.
+GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
```
```ruby
-MyJob.set(wait: 1.week).perform_later(record) # Enqueue a job to be performed 1 week from now.
+# Enqueue a job to be performed 1 week from now.
+GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
```
-That's it!
+```ruby
+# `perform_now` and `perform_later` will call `perform` under the hood so
+# you can pass as many arguments as defined in the latter.
+GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')
+```
+That's it!
Job Execution
-------------
-If no adapter is set, the job is immediately executed.
+For enqueuing and executing jobs you need to set up a queuing backend, that is to
+say you need to decide for a 3rd-party queuing library that Rails should use.
+Rails itself does not provide a sophisticated queuing system and just executes the
+job immediately if no adapter is set.
### Backends
-Active Job has built-in adapters for multiple queueing backends (Sidekiq,
+Active Job has built-in adapters for multiple queuing backends (Sidekiq,
Resque, Delayed Job and others). To get an up-to-date list of the adapters
see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
-### Changing the Backend
+### Setting the Backend
-You can easily change your queueing backend:
+You can easily set your queuing backend:
```ruby
-# be sure to have the adapter gem in your Gemfile and follow the adapter specific
-# installation and deployment instructions
-Rails.application.config.active_job.queue_adapter = :sidekiq
+# config/application.rb
+module YourApp
+ class Application < Rails::Application
+ # Be sure to have the adapter's gem in your Gemfile
+ # and follow the adapter's specific installation
+ # and deployment instructions.
+ config.active_job.queue_adapter = :sidekiq
+ end
+end
```
+### Starting the Backend
+
+Since jobs run in parallel to your Rails application, most queuing libraries
+require that you start a library-specific queuing service (in addition to
+starting your Rails app) for the job processing to work. Refer to library
+documentation for instructions on starting your queue backend.
+
+Here is a noncomprehensive list of documentation:
+
+- [Sidekiq](https://github.com/mperham/sidekiq/wiki/Active-Job)
+- [Resque](https://github.com/resque/resque/wiki/ActiveJob)
+- [Sucker Punch](https://github.com/brandonhilkert/sucker_punch#active-job)
+- [Queue Classic](https://github.com/QueueClassic/queue_classic#active-job)
Queues
------
@@ -145,19 +181,42 @@ class GuestsCleanupJob < ActiveJob::Base
end
# Now your job will run on queue production_low_priority on your
-# production environment and on beta_low_priority on your beta
-# environment
+# production environment and on staging_low_priority
+# on your staging environment
+```
+
+The default queue name prefix delimiter is '\_'. This can be changed by setting
+`config.active_job.queue_name_delimiter` in `application.rb`:
+
+```ruby
+# config/application.rb
+module YourApp
+ class Application < Rails::Application
+ config.active_job.queue_name_prefix = Rails.env
+ config.active_job.queue_name_delimiter = '.'
+ end
+end
+
+# app/jobs/guests_cleanup.rb
+class GuestsCleanupJob < ActiveJob::Base
+ queue_as :low_priority
+ #....
+end
+
+# Now your job will run on queue production.low_priority on your
+# production environment and on staging.low_priority
+# on your staging environment
```
-If you want more control on what queue a job will be run you can pass a :queue
-option to #set:
+If you want more control on what queue a job will be run you can pass a `:queue`
+option to `#set`:
```ruby
MyJob.set(queue: :another_queue).perform_later(record)
```
-To control the queue from the job level you can pass a block to queue_as. The
-block will be executed in the job context (so you can access self.arguments)
+To control the queue from the job level you can pass a block to `#queue_as`. The
+block will be executed in the job context (so you can access `self.arguments`)
and you must return the queue name:
```ruby
@@ -172,23 +231,22 @@ class ProcessVideoJob < ActiveJob::Base
end
def perform(video)
- # do process video
+ # Do process video
end
end
ProcessVideoJob.perform_later(Video.last)
```
-
-NOTE: Make sure your queueing backend "listens" on your queue name. For some
+NOTE: Make sure your queuing backend "listens" on your queue name. For some
backends you need to specify the queues to listen to.
Callbacks
---------
-Active Job provides hooks during the lifecycle of a job. Callbacks allow you to
-trigger logic during the lifecycle of a job.
+Active Job provides hooks during the life cycle of a job. Callbacks allow you to
+trigger logic during the life cycle of a job.
### Available callbacks
@@ -206,13 +264,13 @@ class GuestsCleanupJob < ActiveJob::Base
queue_as :default
before_enqueue do |job|
- # do something with the job instance
+ # Do something with the job instance
end
around_perform do |job, block|
- # do something before perform
+ # Do something before perform
block.call
- # do something after perform
+ # Do something after perform
end
def perform
@@ -222,7 +280,7 @@ end
```
-ActionMailer
+Action Mailer
------------
One of the most common jobs in a modern web application is sending emails outside
@@ -238,14 +296,28 @@ UserMailer.welcome(@user).deliver_later
```
+Internationalization
+--------------------
+
+Each job uses the `I18n.locale` set when the job was created. Useful if you send
+emails asynchronously:
+
+```ruby
+I18n.locale = :eo
+
+UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.
+```
+
+
GlobalID
--------
+
Active Job supports GlobalID for parameters. 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. Before, jobs would look like this:
```ruby
-class TrashableCleanupJob
+class TrashableCleanupJob < ActiveJob::Base
def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
@@ -256,15 +328,15 @@ end
Now you can simply do:
```ruby
-class TrashableCleanupJob
+class TrashableCleanupJob < ActiveJob::Base
def perform(trashable, depth)
trashable.cleanup(depth)
end
end
```
-This works with any class that mixes in `ActiveModel::GlobalIdentification`, which
-by default has been mixed into Active Model classes.
+This works with any class that mixes in `GlobalID::Identification`, which
+by default has been mixed into Active Record classes.
Exceptions
@@ -274,12 +346,11 @@ Active Job provides a way to catch exceptions raised during the execution of the
job:
```ruby
-
class GuestsCleanupJob < ActiveJob::Base
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
- # do something with the exception
+ # Do something with the exception
end
def perform
@@ -287,3 +358,17 @@ class GuestsCleanupJob < ActiveJob::Base
end
end
```
+
+### Deserialization
+
+GlobalID allows serializing full Active Record objects passed to `#perform`.
+
+If a passed record is deleted after the job is enqueued but before the `#perform`
+method is called Active Job will raise an `ActiveJob::DeserializationError`
+exception.
+
+Job Testing
+--------------
+
+You can find detailed instructions on how to test your jobs in the
+[testing guide](testing.html#testing-jobs).