views:

37

answers:

2

I have a rails code that sends emails. Following is in my controller:

def create
    @users = Users.find(:all)
    @sub = params[:sub]
    @body = params[:body]
    @index = 0
    @users.each {|i| index++; Notifier.deliver_notification(@users.email_address, @sub, @body, @users.unsubscribe_link);}
    flash[:notice] = "Mail was sent to " + @index + " people"   
end

I have the following in my Model

class Notifier < ActionMailer::Base
   def notification(email, sub, content, link)
     recipients email 
     from       "[email protected]"
     subject    sub
     body       :content => recipient, :link => link
   end
end

This all works fine. My Question is:

For example if there is an error in sending mail to one of the pople, even then my flash message will say. Mail was sent to X people

What can I do to ensure that @index gets incremented ONLY when mail is successfully sent?

+1  A: 

The deliver_notification method should always return a TMail object regardless of success or failure. There is a raise_delivery_errors setting which will allow the mailer to raise exceptions if there's trouble, but you'll have to rescue these in your block and only increment on success.

Due to the way mail is delivered by ActionMailer, it's often the case you won't know if the message is successful or not. Email is usually queued and delivered at a point in time well beyond your method call, and most errors occur at this point due to any number of difficulties in delivery. It's only wildly malformed email addresses that will be rejected up front, or if the mail delivery mechanism is non-functional.

Edit: Added Exception Tracking

count = 0
@users.each do |user|
  begin
    Notifier.deliver_notification(
      user.email_address,
      @sub,
      @body,
      user.unsubscribe_link
    )

    count += 1
  rescue => e
    # Something went wrong, should probably store these and examine them, or
    # at the very least use Rails.logger
  end
end

flash[:notice] = "Mail was sent to #{count} people"

Your example used index++ which is not supported by Ruby. What you probably want is index += 1. You were also using the @users array directly instead of the individual elements.

tadman
Thanks. I understand that. Only errors I am trying to capture are if a email address just does not exist or if the email address is wrong. In those cases I've noticed that error is thrown right away. (at least on localhost)
learn_plsql
There is no reliable way to test if an email address exists or not. Many server implementations quietly accept and drop invalid emails so as a defense against spammers that are dictionary probing for valid email addresses. Also, as you're probably delivering your email to an intermediary of some sort, the best you can hope for is to verify that there is a valid MX for the receiver's host name or domain. If you want to validate an email address, check it against the [RFC822 regex](http://tfletcher.com/lib/rfc822.rb). See my amended answer for how you can potentially alter the count.
tadman
+1  A: 

You could ask ActionMailer to throw exceptions for you, and then only count those deliveries that don't result in an exception.

ActionMailer::Base.raise_delivery_errors = true
@users.each do |i| 
  begin
    Notifier.deliver_notification(@users.email_address, @sub, @body, @users.unsubscribe_link)
    index++
  rescue Exception => e
    # Do whatever you want with the failed deliveries here
  end
end
jdl