gucki
gucki

Reputation: 4762

Delayed job with custom attributes

I'm using delayed job 3.0.2 with ActiveRecord and Rails 3.2.3. I have a User model which uses the has_secure_password mixin, so the password is only stored encrypted. Now I want to use delayed job to send the welcome email, which should contain a copy of the unencrypted password.

When creating the record, the plain-text password is in User#password. But delayed job seems to serialize/ deserialize the id of the record only and create a new instance of the model by doing User.find(X). This way my plain-text password is lost and the user gets an empty password in his email.

How can I tell delayed-job to serialize/ deserialize custom "virtual" attributes too, which are not stored in the database otherwise?

This is my monkey patch for delayed job 2.x, which worked fine.

class ActiveRecord::Base
  def self.yaml_new(klass, tag, val)
    klass.find(val['attributes']['id']).tap do |m|
      val.except("attributes").each_pair{ |k, v| m.send("#{k}=", v) }
    end
  rescue ActiveRecord::RecordNotFound
    raise Delayed::DeserializationError
  end
end

It doesn't work with delayed job 3.x. I'm also not really interested in fixing my monkey patch as I hope there's a proper solution to this.

Upvotes: 3

Views: 1378

Answers (1)

munchbit
munchbit

Reputation: 539

In delayed job 3.x, the best way to do this is to override a few methods on your ActiveRecord class, and then to force the Psych YAML deserializer to load the ActiveRecord object from the serialized data. By default, delayed job uses just the deserialized id, and then loads the ActiveRecord object from the DB. So, say I have an ActiveRecord class called ShipmentImport, and I want an attr_accessor named 'user_id' to work with delayed job serialization/deserialization. Here is what I would do.

In the ShipmentImport ActiveRecord class, add this:

def encode_with(coder)
  super
  coder['user_id'] = @user_id
end

def init_with(coder)
 super
  @user_id = coder['user_id']
  self
end

In an initializer for your application, add this for your ActiveRecord class:

Psych.load_tags[['!ruby/ActiveRecord', ShipmentImport.name].join(':')] = ShipmentImport

Upvotes: 2

Related Questions