<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Flvorful Bloggage: Deep in Rails: ActionMailer#deliver Part V</title>
    <link>http://blog.flvorful.com/articles/2009/12/14/deep-in-rails-actionmailer-deliver-part-v</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description></description>
    <item>
      <title>Deep in Rails: ActionMailer#deliver Part V</title>
      <description>&lt;p&gt;So now we have our mail object.  Let&amp;#8217;s send it. &lt;/p&gt;

&lt;p&gt;For the sending of the message, we need to go back to the beginning, &lt;code&gt;method_missing&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(line 62 action_mailer/base.rb)
def method_missing(method_symbol, *parameters) #:nodoc:
  if match = matches_dynamic_method?(method_symbol)

    case match[1]
      when 'create'  then new(match[2], *parameters).mail
      when 'deliver' then new(match[2], *parameters).deliver!
      when 'new'     then nil
      else super
    end
  else
    super
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You remember &lt;code&gt;method_missing&lt;/code&gt; from a long time ago.  We just finished the first part of the line:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;when 'deliver' then new(match[2], *parameters).deliver!
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We created the new AM object and now we call &lt;code&gt;deliver!&lt;/code&gt; on it.  That&amp;#8217;s our next stop:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(line 520 action_mailer/base.rb)
def deliver!(mail = @mail)
  raise "no mail object available for delivery!" unless mail
  unless logger.nil?
    logger.info  "Sent mail to #{Array(recipients).join(', ')}"
    logger.debug "\n#{mail.encoded}"
  end

  begin
    __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
  rescue Exception =&amp;gt; e  # Net::SMTP errors or sendmail pipe errors
    raise e if raise_delivery_errors
  end

  return mail
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The beginning of this method does some checking and logging.  The main part of this method is:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We are again using &lt;code&gt;__send__&lt;/code&gt; to bypass any possible overridden method and calling &lt;code&gt;perform_delivery_#{delivery_method}&lt;/code&gt; with the mail object.  &lt;code&gt;delivery_method&lt;/code&gt; is a configuration attribute on the AM class along with &lt;code&gt;perform_deliveries&lt;/code&gt;.  AM defaults to &amp;#8220;smtp&amp;#8221; for &lt;code&gt;delivery_method&lt;/code&gt; and &amp;#8220;true&amp;#8221; for &lt;code&gt;perform_deliveries&lt;/code&gt; so the method we end up calling in our example is &lt;code&gt;perform_delivery_smtp&lt;/code&gt;.  Let&amp;#8217;s see that code:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(line 680 action_mailer/base.rb)
def perform_delivery_smtp(mail)
  destinations = mail.destinations
  mail.ready_to_send
  sender = (mail['return-path'] &amp;amp;&amp;amp; mail['return-path'].spec) || mail.from

  smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
  smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] &amp;amp;&amp;amp; smtp.respond_to?(:enable_starttls_auto)
  smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
             smtp_settings[:authentication]) do |smtp|
    smtp.sendmail(mail.encoded, sender, destinations)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the finale.  The climax.  The whole reason we even engage in &lt;code&gt;ActionMailer&lt;/code&gt;, to actualy send an email.  Here we go.&lt;/p&gt;

&lt;p&gt;The first line pulls the &lt;code&gt;destinations&lt;/code&gt; (recipients) from the mail object and stores them in a variable.  Next, we call &lt;code&gt;ready_to_send&lt;/code&gt; on the mail object which is also the TMail object (remember that we assigned the final TMail object to the @mail attribute on our AM object).  This method is defined in &lt;code&gt;TMail::Net&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(line 62 action_mailer/tmail-1.2.3/tmail-1.2.3/net.rb)
def ready_to_send
  delete_no_send_fields
  add_message_id
  add_date
end

NOSEND_FIELDS = %w(
  received
  bcc
)

def delete_no_send_fields
  NOSEND_FIELDS.each do |nm|
    delete nm
  end
  delete_if {|n,v| v.empty? }
end

def add_message_id( fqdn = nil )
  self.message_id = ::TMail::new_message_id(fqdn)
end

def add_date
  self.date = Time.now
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, if you scan through that code you may notice that we are calling &lt;code&gt;delete&lt;/code&gt; and &lt;code&gt;delete_if&lt;/code&gt; like we were in a standard &lt;code&gt;Enumerable&lt;/code&gt; object, but we are not.  TMail also defines those methods:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def delete( key )
  @header.delete key.downcase
end

def delete_if
  @header.delete_if do |key,val|
    if Array === val
      val.delete_if {|v| yield key, v }
      val.empty?
    else
      yield key, val
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What we are doing with all this is stripping and setting some defaults.  &lt;code&gt;delete_no_send_fields&lt;/code&gt; get&amp;#8217;s rid of the &amp;#8220;received&amp;#8221; and &amp;#8220;bcc&amp;#8221; keys from our header and we are getting rid of any headers that have empty values.  After that we set a nice new hex type id for this email with &lt;code&gt;TMail.new_message_id&lt;/code&gt; which is defined in &lt;code&gt;TMail::Utils&lt;/code&gt; which gives us a string with a hex code attached to your machine&amp;#8217;s hostname.  The last line adds the date to the mail message and that&amp;#8217;s it.&lt;/p&gt;

&lt;p&gt;The next line sets the sender attribute.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sender = (mail['return-path'] &amp;amp;&amp;amp; mail['return-path'].spec) || mail.from
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we get into the actual sending of the email.  We aren&amp;#8217;t going to go into it because it is part of Ruby&amp;#8217;s core lib, but we will quickly skim over it to see what&amp;#8217;s going on:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] &amp;amp;&amp;amp; smtp.respond_to?(:enable_starttls_auto)
smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
           smtp_settings[:authentication]) do |smtp|
  smtp.sendmail(mail.encoded, sender, destinations)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;First, we create the new SMTP object and then we enable StartTLS if necessary (this allows you to send email through SMTP hosts that require SSL connections).  Next we login to the SMTP server (with the &lt;code&gt;start&lt;/code&gt; command) and call &lt;code&gt;sendmail&lt;/code&gt; inside the block to do the actual delivery.  &lt;/p&gt;

&lt;p&gt;We&amp;#8217;re done.  That&amp;#8217;s how an email gets sent in Rails.  Next week we will create a quick monkey patch for ActionMailer based on the information that we learned from the trek.&lt;/p&gt;


</description>
      <pubDate>Mon, 14 Dec 2009 12:54:00 -0600</pubDate>
      <guid isPermaLink="false">urn:uuid:a6a6693d-ab06-4635-8b2c-fa3af2a3cffb</guid>
      <author>jake</author>
      <link>http://blog.flvorful.com/articles/2009/12/14/deep-in-rails-actionmailer-deliver-part-v</link>
      <category>Rails</category>
      <category>rails</category>
      <category>action_mailer</category>
      <category>deep</category>
      <category>DIR</category>
      <category>part4</category>
    </item>
  </channel>
</rss>
