Active Job

From Expertiza_Wiki
Revision as of 02:41, 6 February 2016 by Ychen71 (talk | contribs) (→‎Examples)
Jump to navigation Jump to search

Introduction

Active Job is a framework that helps developers writing codes and run them on the background automatically under different scenarios. It’s an interface that adapts different queueing backends like Backburner[1], Delayed Job[2], Qu[3] and so on. Jobs can vary from schedule newsletter, follow-up emails to database housekeeping. <ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” </ref> Overall, Active Job is a interface which you can work with common queues.

Ruby On Rails versions support Active Job

Version history
Version Date
4.2 2014/12/19
4.2.5 2015/11/13







Active Job adapters<ref name="Active-Job-Adapter">Active Job Adapter"Active Job Adapter" </ref>

List of queueing backends Active Job support:

How to use <ref>http://guides.rubyonrails.org/active_job_basics.html</ref>

We will introduce how to creating a job and and how to add the job into a queue. <ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>

Download

With RubyGems you can install Active Job:

$ gem install activejob

Souce code of Active Job available on GitHub, as part of Rails.<ref> GitHub “Active Job -- Make work happen later” 2015 </ref>

Create a Job

In Active Job, a process inserted in a queue and waiting for carry out is called “Job”. It’s possible to generate a Job using the Generator provided by Rails. You can create a Job in app/jobs. By doing the following, we created a Job called “update_wiki”.

$ bin/rails generate job update_wiki
  invoke  test_unit
  create  test/jobs/update_wiki_job_test.rb
  create  app/jobs/update_wiki_job.rb

Active Job provides the ability to run your Job on a specific queue by creating a job:

$ bin/rails generate job update_wiki --queue urgent

Files inside of app/jobs can be created manually, instead of using a generator. In Rails 4.2 an ActiveJob class inherits from ActiveJob::Base. In Rails 5.0, it has changed to now inherit from ApplicationJob. When upgrading from Rails 4.2 to Rails 5.0, an application_job.rb file is needed to be created in app/jobs/ and add the following content:<ref> Rails Guides “A Guide for Upgrading Ruby on Rails” 2015 </ref>

class ApplicationJob < ActiveJob::Base
end

In Rails 4.2 a Job class defined a perform method and set a “queue_as” value:

class UpdateWikiJob < ActiveJob::Base
  queue_as :default
 
  def perform(*wiki)
   # Do something later
  wiki.update_contents
  end
end

There is a perform method to be called when the Job was first enqueued.

Adding a Job to the queue

If you wish your Job be processed as soon as the queuing system is free, you can enqueue a Job like:

UpdateWikiJob.perform_later wiki

Or you can add a Job be performed tomorrow at noon:

UpdateWikiJob.set(wait_until: Date.tomorrow.noon).perform_later(wiki)

If you want your Job be performed a week from now, most of the queueing backends ( Sidekiq , Delayed Job, etc. ) allow you to set a delay time.

UpdateWikiJob.set(wait: 1.week).perform_later(wiki)

Execution of Job

Active Job provides adapters for multiple queueing backends (Sidekiq, Resque, Delayed Job and others).<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref> Without setting any adapter, the job would be performed immediately.
Queueing backend can be set at: /config/application.rb, in this example we use the Sidekiq.

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

Play with Queues

Active Job allows to schedule the job to be processed on a specific queue, this became helpful as common adapters support multiple queues.

class UpdateWikiJob < ActiveJob::Base
  queue_as :low_priority
  #....
end

Queue name can be prefixed for all jobs using config.active_job.queue_name_prefixin application.rb:

# config/application.rb
module YourApp
  class Application < Rails::Application
    config.active_job.queue_name_prefix = Rails.env
  end
end
 
# app/jobs/update_wiki_job.rb
class UpdateWikiJob < 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


You can control a job to run on a queue you like, by passing a :queue option to #set :

MyJob.set(queue: :another_queue).perform_later(record)

All the code snippets are referred from the source<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>

Examples

In this section, a example is provided to demonstrate how to use ActiveJob in practice.

Background Mail Sender

You can send emails asynchronously with Action Mailer which Active Job is already integrated in<ref name= "Action-Mailer-Basics">Rails Guides “Action Mailer Basics” 2014</ref>. Here in this example, we try to use Active Job with Action Mailer.

1. Getting started with Rails<ref name= "Welcome_to_Rails">GitHub “Welcome to Rails” 2015</ref>

# Install Rails if you haven’t done it yet:
$ gem install rails

# Create a new Rails application, “myapp_activejob”
# is the name of the application:
$ rails new myapp_activejob

# Change your directory to myapp_activejob
$ cd myapp_activejob

Edit the Gemfile in the folder, add following into it:

gem 'responders'

Run “ $ bundle update “ and make sure “$ rails server” works.

2. Resque setup<ref name= "Download_Redis">Redis “Download Redis” 2016</ref>

We will use Resque as the enqueuing backend. You need to install Redis before you can run Resque. You can get Redis using Homebrew:

$ brew install redis

Or you can download, extract and compile Redis using:<ref name= "Download_Redis">Redis “Download Redis” 2016</ref>

$ wget http://download.redis.io/releases/redis-3.0.7.tar.gz
$ tar xzf redis-3.0.7.tar.gz
$ cd redis-3.0.7
$ make

Redis does not officially support Microsoft Windows[14] platform, but you can find Redis on Windows by Microsoft Open Tech group.
Next get Resque be installed. To use resque with Active Job, we also need resque-scheduler. Add the following into Gemfile, and run “$ bundle install”.

gem 'resque'
gem 'resque-scheduler'

After the installation, create a Resque configuration file resque.rb in config/initializers/

#config/initializers/resque.rb

Resque.redis = Redis.new(host: 'localhost', post: 6379)
Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }

As the Resque tasks and Resque Scheduler rake tasks are needed in this example, we need to create a resque.rake file in lib/tasks/:

#lib/tasks/resque.rake

require 'resque/tasks'
require 'resque/scheduler/tasks'

namespace :resque do
  task setup: :environment do
    ENV['TERM_CHILD'] ||= '1'
    ENV['QUEUE'] ||= '*'
    require 'resque'
    require 'resque-scheduler'
  end
end

3. Creating a Mailer<ref name= "Action-Mailer-Basics">Rails Guides “Action Mailer Basics” 2014</ref>

By doing the things above, we already have our Rails project and the Resque for queuing get set. Next we will create a Mailer.

$ bin/rails generate mailer user_mailer

Create a method and a view for sending the email.
In app/mailer/user_mailer.rb:

#app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base
  default from: 'from@example.com'

  def test_email(email)
    mail(
      to: email,
      subject: 'We are testing Active Job!'
    )
  end
end

In app/views/user_mailer/test_email.text

#app/views/user_mailer/test_email.text

Hey, we are testing Active Job!

4. Active Job<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>

Create a configuration file active_job.rb in config/initializers/ , to set Resque as queue_adapter.

#config/initializers/active_job.rb

ActiveJob::Base.queue_adapter = :resque

Create a Job with generator.

$ bin/rails generate job test_email

In the generated file app/jobs/test_email_job.rb, define the perform method and set it’s queue_as.

#app/jobs/test_email_job.rb

class TestEmailJob < ActiveJob::Base
  queue_as :email

  def perform(email)
    UserMailer.test_email(email).deliver_now
  end
end

Now we can use a UserController to put the job into the queue for later execution. Here we set the email to be sent one minute later for test.

$ bin/rails generate job test_email
#app/controllers/users_controller.rb

class UsersController < ApplicationController
  def new
    @user = User.new
  end

  def create
    @user = User.create(user_params)
    TestEmailJob.new(@user.email).deliver_later!(wait: 1.minute)
    # redirect somewhere
  end
end

Routes and view need to set to make it work.

#config/routes.rb

Rails.application.routes.draw do
  resources :users, only: [:new, :create]
end
#app/views/users/new.html.erb

<%= form_for @user do |f| %>
  <%= f.email_field :email %>
  <%= f.submit %>
<% end %>

5. Try execution

Before we try the Mailer, we need MailCatcher for the test. Add following into Gemfile and “$ bundle install”

gem 'mailcatcher'

To set the environment, add following things into config/environments/development.rb.

#config/environments/development.rb

Rails.application.configure do
  ...
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = { address: "localhost", port: 1025 }
end

Run it!

# Start mailcatcher
$ mailcatcher

# Start redis-server
$ redis-server

# Start resque:work
$ bundle exec rake resque:work

# Start resque:scheduler
$ rake environment resque:scheduler

# Finally start rails server
$ rails server

Open the browser, visit localhost:3000/users/new and sign up as a new user. One minute later you can see following in Resque scheduler:

resque-scheduler: [INFO] 2016-02-05T22:53:15+09:00: Processing Delayed Items

And in MailCatcher:

==> SMTP: Received message from '<from@example.com>' (315 bytes)

References

<references/>