Rails 6 - Custom Serializers for ActiveJob arguments


Rails 6 introduces custom serializers for objects which are passed as arguments to ActiveJob.

Before

When building large Rails application we require various tasks that need to run in the background.

We generally make use of ActiveJob to run these tasks in background. We need to pass various objects as arguments to these tasks, which rely on serialization of these objects.

Let’s say we have a background job to send a verification code to a user. We have a VerificationCode object with attributes code and email.

class VerificationCode
  attr_accessor :code, :email
  
  def initialize(code, email)
    @code = code
    @email = email
  end
end

We have below EmailVerificationCode job which expects a verification code object, as an argument to the perform method.

class EmailVerificationCode < ActiveJob::Base
  
  def perform(verification_code)
    # send verification code to user email
  end
end

When we actually try to pass EmailVerificationCode as an object to EmailVerificationCode it ends up in an active job serialization error.

verification_code = VerificationCode.new("code", "sam@example.com")
EmailVerificationCode.perform_later(verification_code)

ActiveJob::SerializationError: Unsupported argument type: VerificationCode

This is because ActiveJob doesn’t really know how to serialize/deserialize and make this available in background jobs.

After

Since ActiveJob supports custom serialization and deserialization for any object, we can create a custom VerificationCodeSerializer:

class VerificationCodeSerializer < ActiveJob::Serializers::ObjectSerializer
  def serialize?(argument)
    argument.kind_of?(VerificationCode)
  end

  def serialize(verification_code)
    super(
      "code" => verification_code.code,
      "email" => verification_code.email
    )
  end

  def deserialize(hash)
    VerificationCode.new(hash["code"], hash["email"])
  end
end

For any custom serializer we need to implement 3 methods which are listed below:

  • serialize? - Checks if the argument passed can be serialized or not.

  • serialize - Returns hash with the keys of basic types only.

  • deserialize - Converts hash back to original object.

Since this is a custom serializer, we need to let Rails know that we intend to use it.

This is done by adding the serializers to active_job.custom_serializers config:

Rails.application.config.active_job.custom_serializers << VerificationCodeSerializer

Now when we pass the verification_code object to the perform_later method it won’t raise any error, and handle the object appropriately.

verification_code = VerificationCode.new("code", "sam@example.com")
EmailVerificationCode.perform_later(verification_code)