Rails 3 ActionMailer and Wicked_PDF
Asked Answered
O

4

22

I'm trying to generate emails with rendered PDF attachements using ActionMailer and wicked_pdf.

On my site, I'm using already both wicked_pdf and actionmailer separately. I can use wicked_pdf to serve up a pdf in the web app, and can use ActionMailer to send mail, but I'm having trouble attaching rendered pdf content to an ActionMailer (edited for content):

class UserMailer < ActionMailer::Base
  default :from => "[email protected]"

  def generate_pdf(invoice)
    render :pdf => "test.pdf",
     :template => 'invoices/show.pdf.erb',
     :layout => 'pdf.html'
  end

  def email_invoice(invoice)
    @invoice = invoice
    attachments["invoice.pdf"] = {:mime_type => 'application/pdf',
                                  :encoding => 'Base64',
                                  :content => generate_pdf(@invoice)}
    mail :subject => "Your Invoice", :to => invoice.customer.email
  end
end

Using Railscasts 206 (Action Mailer in Rails 3) as a guide, I can send email with my desired rich content, only if I don't try to add my rendered attachment.

If I try to add the attachment (as shown above), I get an attachement of what looks to be the right size, only the name of the attachment doesn't come across as expected, nor is it readable as a pdf. In addition to that, the content of my email is missing...

Does anyone have any experience using ActionMailer while rendering the PDF on the fly in Rails 3.0?

Thanks in advance! --Dan

Olla answered 24/3, 2011 at 15:32 Comment(1)
FYI - I was using edgeguides.rubyonrails.org/action_mailer_basics.html to guide me as well...Olla
B
31

WickedPDF can render to a file just fine to attach to an email or save to the filesystem.

Your method above won't work for you because generate_pdf is a method on the mailer, that returns a mail object (not the PDF you wanted)

Also, there is a bug in ActionMailer that causes the message to be malformed if you try to call render in the method itself

http://chopmode.wordpress.com/2011/03/25/render_to_string-causes-subsequent-mail-rendering-to-fail/

https://rails.lighthouseapp.com/projects/8994/tickets/6623-render_to_string-in-mailer-causes-subsequent-render-to-fail

There are 2 ways you can make this work,

The first is to use the hack described in the first article above:

def email_invoice(invoice)
  @invoice = invoice
  attachments["invoice.pdf"] = WickedPdf.new.pdf_from_string(
    render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
  )
  self.instance_variable_set(:@lookup_context, nil)
  mail :subject => "Your Invoice", :to => invoice.customer.email
end

Or, you can set the attachment in a block like so:

def email_invoice(invoice)
  @invoice = invoice
  mail(:subject => 'Your Invoice', :to => invoice.customer.email) do |format|
    format.text
    format.pdf do
      attachments['invoice.pdf'] = WickedPdf.new.pdf_from_string(
        render_to_string(:pdf => "invoice",:template => 'documents/show.pdf.erb')
      )
    end
  end
end
Butterflies answered 2/4, 2011 at 16:0 Comment(2)
I had to revisit my PDF generation - and was just disappointed of the complexity/lack of elegance in prawn and thought I'd try wicked again.... and it worked! The only thing I got caught on - the view for the actual attachment needs to live at the root of the view folder, where I'd expect it to use the view from within the folder of the object I'm working with...Olla
On Rails 3.2.2, the first way works without the hacking into @lookup_context. The second way leads to duplicates or missing attachments on Mail.app, Sparrow and GMail.Fertilizer
C
4

I used of Unixmonkey's solutions above, but then when I upgraded to rails 3.1.rc4 setting the @lookup_context instance variable no longer worked. Perhaps there's another way to achieve the same clearing of the lookup context, but for now, setting the attachment in the mail block works fine like so:

  def results_email(participant, program)
    mail(:to => participant.email,
         :subject => "my subject") do |format|
      format.text
      format.html
      format.pdf do
        attachments['trust_quotient_results.pdf'] = WickedPdf.new.pdf_from_string(
          render_to_string :pdf => "results",
               :template => '/test_sessions/results.pdf.erb',
               :layout => 'pdf.html')
      end
   end
  end
Cristoforo answered 14/6, 2011 at 19:7 Comment(2)
The first solution does not work anymore since the name of the lookup context instance variable was changed with this commit: github.com/rails/rails/commit/… So it is now called @_lookup_context, and that's what I'm using.Devonna
self.instance_variable_set(:@_lookup_context, nil) (note the underscore) also ensured that Sparrow shows the attachment preview.Nicolettenicoli
O
0

Heres' how I fixed this issue:

  1. Removed wicked_pdf
  2. Installed prawn (https://github.com/sandal/prawn/wiki/Using-Prawn-in-Rails)

While Prawn is/was a bit more cumbersome in laying out a document, it can easily sling around mail attachments...

Olla answered 30/3, 2011 at 4:52 Comment(2)
wicked_pdf is "more fun" to use than prawn (IMO) - and now, using the first hack from unix_monkey, it works...Olla
have some experience using prawn and wicked_pdf. so I choose wicket_pdf - very fast, complete html support and painless.Unswear
M
0

Better to use PDFKit, for example:

class ReportMailer < ApplicationMailer

  def report(users:, amounts:)
    @users = users
    @amounts = amounts

    attachments["proveedores.pdf"] = PDFKit.new(
      render_to_string(
        pdf: 'bcra',
        template: 'reports/users.html.haml',
        layout: false,
        locals: { users: @users }
      )
    ).to_pdf

    mail subject: "Report - #{Date.today}"
  end
end
Mechellemechlin answered 25/1, 2021 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.