How to add images (i.e. logo) to email messages while using Pony gem.
Public
27 Dec 11:23

Pony gem allows to easily send good looking and customizable emails, but it doesn’t provide a way to include image in a way that it will be displayed in gmail.
You may think that embedding email by encoding it to base64 is good idea. but no. Gmail is very restrictive and will remove it. This means we have to embed it using CID Embedded Images (Inline Images). It’s the same way standard Mailer ( and so device) embeds images. It’s made by using
attachments.inline['logo.png'] = File.read( 'logo')
, and in view
= image_tag attachments['logo.png'].url
We can’t do that in our case. Body of our email is created before mail object so attachments object doesn’t exist yet. However we can bypass it by overriding Pony gem a little bit. First we have to pass images:

    Pony.mail(
      ...
      images: images,
    )

where images is a hash, key is filename (with extension!) and value is file:

    # Remember that name must have extension (i.e. 'image.png')
    {
        'logo.png': File.read( 'logo.png')
    }

Then we override Pony:
In build_mail method we need to add images as inline attachments

      # Add images as inline attachments to mail
      images = (options[:images] || {}).map do |name, image|
        mail.attachments.inline[name.to_s] = image
        [name, mail.attachments.last.url]
      end

and pass it to build_html_part, where placeholders will be replaced with cid of images

    def build_html_part(options, images)
      # Replace placeholders with real images attached as inline attachments
      opts = options[:html_body]
      images.each do |image_name, cid|
        opts.gsub!("/images/#{image_name}", cid)
      end
      Mail::Part.new(:content_type => 'text/html;charset=UTF-8') do
        content_transfer_encoding 'quoted-printable'
        body Mail::Encodings::QuotedPrintable.encode(opts)
        if options[:html_body_part_header] && options[:html_body_part_header].is_a?(Hash)
          options[:html_body_part_header].each do |k,v|
            header[k] = v
          end
        end
      end
    end

and add :images as non standard option:

    def non_standard_options
      # Add new non standard option :images
      [
       ...,
        :images
      ]
    end

After that we need to create placeholder for image in view:

<%= image_tag 'logo.png' %>

And that’s all.
You have to remember that adding images this way will increase overall size of email, and it still may not be displayed properly in some email clients.

Comments

Joe
aleksander
Thanks @szymon. Good job but please fix styling a little