4

I've got a controller "tech" that has an action to email and invoice, from there we use Delayed::Job.enqueue to shove the actual email action into a background process which will be handled via a worker dyno on Heroku.

This is all working fine.

The trouble that I found is that my generated PDF invoice lives over on the Heroku Web Dyno file system and the Worker has no idea where this is.

I will do upload the PDF during the generation process, it takes too damn long.

So I need to create the invoice over on the worker dyno when it goes to execute the mailer action to send the message.

I found this blog with some detailed instructions on creating the pdf from a string: http://viget.com/extend/how-to-create-pdfs-in-rails

But it's not working at all for me, here is the code:

  html = render_to_string(:action =>":show", :layout => "invoice.html")
  @pdf = WickedPdf.new.pdf_from_string(html)

And the error:

"last_error"=>"undefined method `response_body=' for #<MailSenderJob:0x007fdf7e70a638>

I know this is from the docs:

WickedPdf.new.pdf_from_string(
  render_to_string('templates/pdf.html.erb', :layout => 'pdfs/layout_pdf'),
  :footer => {
    :content => render_to_string(:layout => 'pdfs/layout_pdf')
  }
)

And that code has never worked for me at all.

What I'm getting over and over is the response_body= error. It's like it's not getting a response at all.

At the top of my file I'm doing:

  include ActionController::Rendering

Because this is the module that has the render_to_string method inside it.

Any help at all - please keep in mind in your response that I'm running this code on a Heroku WORKER dyno - so if there's any dependency that I need to manually include that is naturally included on the web server, please let me know.

notaceo
  • 1,093
  • 10
  • 28

2 Answers2

3

I ended up having to do some weird stuff with this to finally get it working.

  html = File.read(Rails.root.join('app','views','technician','invoice.html.erb'))
  html = ERB.new(html).result(binding)
  html = html.gsub!(/\0/,'')  # There is a null byte in the rendered html, so we'll strip it out (this is kind of a hack)

  #  Render the PDF - we're on a worker dyno and have no access to the pdf we rendered already on the web dyno

  @pdf = WickedPdf.new.pdf_from_string(
            html,
            :handlers => [:erb],
            :footer => {
              :center => "Copyright 2014"                   
            },
            :disable_smart_shrinking => true,
          )
@pdf = @pdf.gsub!(/\0/,'')  # Again with the null bytes!
notaceo
  • 1,093
  • 10
  • 28
3

Using Partials.

I know what you mean, it gets a little funky when you're rendering PDFs in the background job as opposed to a Controller action.

I thought I would share my implementation as a comparison and for others to get another example from.

notification_mailer.rb

def export

  header_html = render_to_string( partial: 'exports/header.pdf.erb', 
                                  locals:  { report_title: 'Emissions Export' } )

  body_html   = render_to_string( partial: "exports/show.pdf.erb" )

  footer_html = render_to_string( partial: 'exports/footer.pdf.erb' )

  @pdf = WickedPdf.new.pdf_from_string(
           body_html,
           orientation: 'Landscape',
           margin: { bottom: 20, top: 30 },
           header: { content: header_html },
           footer: { content: footer_html } )

  # Attach to email as attachment.
  attachments["Emissions Export.pdf"] = @pdf

  # Send email. Attachment assigned above will automatically be included.
  mail( { subject: 'Emissions Export PDF', to: 'elon@musk.com' } )

end
Joshua Pinter
  • 45,245
  • 23
  • 243
  • 245