Rails 4.2で導入されたActive Jobを使ってみよう

## Active Jobとは？

Ruby on Rails（以下Rails） 4.2からバックグラウンドでジョブを実行する[Active Job](https://github.com/rails/rails/tree/master/activejob)が利用できるようになりました。

Active Jobを利用することによって、メールの送信や、バッチ処理などをバックグラウンドで実行することが可能になります。

Railsで利用できるバックグラウンドでのジョブ実行の仕組みとしては、[Delayed Job](https://github.com/collectiveidea/delayed_job), [Resque](https://github.com/resque/resque), [Sidekiq](http://sidekiq.org/)などがありますが、Active Jobはこれらのアダプタとなって動作します。

Active Jobは、ジョブスケジューリングを抽象化するAPIを備えており、異なるジョブ実行の仕組みに移行（たとえばDelayed JobからResqueへ移行）しても、コードをほとんど変えることなく実行することができます。

## Active Jobを使うための準備

### Resqueのセットアップ

今回はActive JobをResqueのアダプタとして使用するので、Resqueをセットアップします。

Resqueは、Redisが必要なのでインストールを済ませておきます。

MacでHomebrewを利用している場合は、以下のようにしてインストールします。

```
$ brew install redis
```

次にResqueをセットアップします。
以下のようにGemfileに指定して、`bundle install`を実行します。

``` ruby:Gemfile
gem 'resque'
gem 'resque-scheduler'
```

インストールできたら、初期設定ファイルとrakeタスクの追加を行います。

``` ruby:config/initializers/resque.rb
Resque.redis = Redis.new(host: 'localhost', post: 6379)
Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }
```

Reqeustの設定ファイルを以下の内容で作成します。

``` ruby: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
```

### Active Jobのセットアップ

以下のような初期設定ファイルを作成し、`queue_adapter`としてResqueを指定します。

``` ruby:config/initializers/active_job.rb
ActiveJob::Base.queue_adapter = :resque
```

## Active Jobを実際に使ってみる

例として、Active Jobを使ってバックグラウンドでメールを送信してみます。

Webサービスで、ユーザーが登録した際にWelcomeメールを送信することを考えます。(今回は、[Rdaily](https://github.com/ryohashimoto/rdaily)というブログ用のアプリケーションを使っています）

### メーラの作成

以下のコマンドで`UserMailer`を作成します。

```
$ rails g mailer user_mailer
```

送信用のメソッドとビューを追加します。

``` ruby:app/mailers/user_mailer.rb
class UserMailer < ActionMailer::Base
  default from: "from@example.com"

  def registered(user)
    mail(to: user.email, subject: 'Welcome to Rdaily!')
  end
end
```

``` erb:app/views/user_mailer/registered.text.erb
Dear. <%= @user.name %>

Welcome to Rdaily!
```

### ジョブの作成

メーラを作成したので、次はActive Jobのジョブを作成します。
Rails 4.2ではActive Jobの導入に伴い、以下のようなジェネレータが利用できるようになっています。

```
$ rails g job user_registered_mailer
      ...
      create  app/jobs/user_registered_mailer_job.rb
```

作成されたファイルを以下のように編集します。

``` ruby:app/jobs/user_registered_mailer_job.rb
class UserRegisteredMailerJob < ActiveJob::Base
  queue_as :email

  def perform(user)
    UserMailer.registered(user).deliver_now
  end
end
```

### アプリケーション内でのジョブの使用

作成したジョブを利用するには、コントローラなどで以下のようにジョブのインスタンスを作成し、待ち時間を指定して`#perform_later`メソッドを呼び出します。
今回のアプリケーションの例では、ユーザーを作成後に以下のように呼び出しています。

``` ruby:app/controllers/account/users_controller.rb
class Account::UsersController < ApplicationController
  ...

  def create
    @user = User.new(user_params)
    if @user.save
      UserRegisteredMailerJob.perform_later(@user)
      flash.notice = "User is successfully created."
      redirect_to account_path
  ...
end
```

（追記）ActiveJob::Baseのサブクラスを定義することなく、

``` ruby
UserRegisteredMailerJob.perform_later(@user)
```

の部分を

``` ruby
UserMailer.registered(@user).deliver_later!(wait: 1.minute)
```

と直接メーラから[`#deliver_later!`](http://edgeapi.rubyonrails.org/classes/ActionMailer/MessageDelivery.html#method-i-deliver_later)メソッドで遅延して送信するジョブを登録することもできるようです。

### ジョブの実行

メールの送受信をテストする前に、[mailcatcher](http://mailcatcher.me/)と呼ばれるgemをセットアップします。

`Gemfile`に以下の記述を追加し、`bundle install`を実行します。

``` ruby:Gemfile
gem 'mailcatcher'
```

環境設定ファイルに以下のように記述します。

``` ruby:config/environments/development.rb
Rails.application.configure do
  ...
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 }
  ...
end
```
以下のコマンドでmailcatcherを起動します。

```
$ mailcatcher
```

各種サーバーを起動します。(ログを参照するためにそれぞれターミナルの別タブで開きます）

Redisサーバーを以下のコマンドで起動します。

```
$ redis-server
```

Resqueのワーカーを以下のコマンドで起動します。

```
$ bundle exec rake resque:work
```

Resqueスケジューラを起動します。

```
$ rake environment resque:scheduler
```

最後にRailsサーバーを起動します。

```
$ rails server
```

ここでブラウザでユーザー登録のアプリケーションの操作を行います。

1分後に、Resqueスケジューラのログに

```
resque-scheduler: [INFO] 2014-10-09T22:53:15+09:00: Processing Delayed Items
```

mailcatcherのログに

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

と表示され、確かにジョブが実行されています。

## まとめ

Rails 4.2に標準で含まれるActive Jobで簡単にジョブを登録して、バックグラウンドでスケジューリングしたジョブを実行することができるようになりました。

## 参考サイト

* [rails/activejob at master](https://github.com/rails/rails/tree/master/activejob)
* [Getting Started With Active Job](https://blog.engineyard.com/2014/getting-started-with-active-job)
* [Ruby on Rails 4.2 Release Notes](http://edgeguides.rubyonrails.org/4_2_release_notes.html)
* [Riding Rails: [ANN] Rails 4.2.0.beta1 has been released!](http://weblog.rubyonrails.org/2014/9/29/Rails-4-2-0-beta2-has-been-released/)
* [Riding Rails: [ANN] Rails 4.2.0.beta2 has been released!](http://weblog.rubyonrails.org/2014/9/29/Rails-4-2-0-beta2-has-been-released/)