views:

49

answers:

2

I have a delayed_job designed to send an email using a mailer.

Upon completion, I need to record that the email was sent -- I do this by saving the newly created ContactEmail.

Right now, the new ContactEmail records gets saved even if the delayed_job fails.

How do I correct that so that the new ContactEmail is only saved when the mailer is successfully sent?

Here is the snippet from the cron task which calls the delayed_job:

    puts contact_email.subject

    contact_email.date_sent = Date.today
    contact_email.date_created = Date.today

    contact_email.body = email.substituted_message(contact, contact.colleagues)

    contact_email.status = "sent" 

    #Delayed::Job.enqueue OutboundMailer.deliver_campaign_email(contact,contact_email)
    Delayed::Job.enqueue SomeMailJob.new(contact,contact_email)

    contact_email.save #now save the record

Here is the some_mail_job.rb

class SomeMailJob < Struct.new(:contact, :contact_email) 
   def perform
     OutboundMailer.deliver_campaign_email(contact,contact_email)
   end
 end

And here is the outbound_mailer:

class OutboundMailer < Postage::Mailer 

  def campaign_email(contact,email)
    subject    email.subject
    recipients contact.email
    from       '<[email protected]>'
    sent_on    Date.today

    body       :email => email
  end
A: 
  1. You need synchronic delivery so stop using delayed job in this case and do standard mailer delivery.
  2. or add success column to you ContactEmail - initialy save it with false then update in job to true
gertas
so save it before it runs the delayed_job (it has to be a delayed_job I am running it in heroku) --- does the instance persistent in the delayed job?
Angela
I didn't know that you are bind to delayed_job by heroku. As you already have status column just update status in job or save whole new record it in job instead of cron task.
gertas
A: 

You could update the status in the perform of the job itself.

For example, something like:

contact_email.status = 'queued'
contact_email.save
contact_email.delay.deliver_campaign_email

And then in your ContactEmail class, something to the effect of

def deliver_campaign_email
  OutboundMailer.deliver_campaign_email(self.contact, self)
  self.status = 'sent'    # or handle failure and set it appropriately
  self.save
end

delayed_job has some magic bits that it adds to your models that will deal with the persistence.

In order to deal with your OutboundMailer throwing an exception, you can do something like so:

def deliver_campaign_email
  begin
    OutboundMailer.deliver_campaign_email(self.contact, self)
    self.status = 'sent'
  rescue
    self.status = 'failed' # or better yet grab the the message from the exception
  end
  self.save
end
Aaron Hinni
I see...when i do contact_email.save -- that creates the "record" which means it was successfully saved...so I can't really put it before delivering the email.....if I deleted that line from the perform method (which is what I assume you mean?) would that work upon successful completion? What happens if the OutboundMailer fails?
Angela
OutboundMailer failure raises exception thus stops method invocation without processing further statements
gertas
You need to rescue the exception.
Aaron Hinni
I see -- so I put the deliver campaign email as a method in the ContactEmail class. How can I make sure it only get status set to "sent" when it is successfullyd elivered?
Angela
Put the setting of the sent status in the same block that might throw the exception. It won't get called if the deliver_campaign_email raises an exception.
Aaron Hinni