timpwbaker
timpwbaker

Reputation: 126

Wicked PDF save_to_file does not create file on heroku production instance

I have a Ruby on Rails application which generates PDFs and saves them into /pdf/.. then runs a delayed job to email the pdf to someone. Production is hosted on Heroku.

The process works fine locally, and has in the past worked in the (same) production environment. However, the files are now not generating/saving on the production environment and I have been unable to work out why. Despite going back over all the old commits, I can't see what I have changed to stop the files from generating.

The folder /pdf exists in the root of the application. I have confirmed using Heroku Bash.

I believe the problem to be somewhere around the save_to_file: instruction.

The delayed job is working fine, if I point the attachment to a file that already exists it will execute and send the email.

I am aware that heroku's filesystem does not persist assets. However seeing as I don't want the assets to persist I don't believe this is a problem (I could be wrong), I just want to generate and then use and discard.

My Route:

  get '/matrices/:matrix_id/submissions/:id/email' => 'submissions#emailpdf', as: :submissions_emailpdf

My controller:

def emailpdf
  get_submission_details
  get_topline_stats
  get_brand
  render  javascript_delay: 2000,
        pdf:       'submission',
        layout:    'pdf', 
        template:  'submissions/showpdf.html.haml',
        show_as_html: params.key?('debug'),
        save_to_file: Rails.root.join('pdf', "submission#{@user.id}.pdf"),
        save_only: true
  SendpdfJob.delay.perform_later(@user.name, @user.id, @user.email, @matrix.id, @submission.id)
  redirect_to matrix_submission_path(@matrix,@submission), notice: "We have emailed you your PDF"
end

My delayed job

 class SendpdfJob < ActiveJob::Base
  queue_as :default
  def perform(username, userid, useremail, matrixid, submissionid)
    Pony.delay.mail(
      :to => useremail, 
      :from => '[email protected]', 
      :subject => 'Your Matrix', 
      :html_body => '<h2>Hello '+ username+'.</h2>
        <p> Your Maturity Matrix is attached. We hope you find this useful.
        <p >All the best 
      :attachments => {
        "matrix.pdf" => File.read("pdf/submission#{userid}.pdf")
      }
    );
  end
end

My Log errror:

Job SendpdfJob.perform_later (id=45) FAILED (8 prior attempts) with Errno::ENOENT: No such file or directory @ rb_sysopen - pdf/submission26.pdf

Heroku bash confirms there are no files in /pdf

Thanks all very much in advance.

TB

Upvotes: 1

Views: 331

Answers (1)

John Athayde
John Athayde

Reputation: 600

As @timpwbaker notes, Heroku doesn't persist files. You can build the file in the tmp directory and do something with it immediately, or you can upload to S3 if it needs to persist. In my worker, I do something along these lines (Heroku/S3 with carrierwave uploader) "issueable" [sic] is my app object.

# Wicked PDF generation code here

# Give it a name and tell it where to write (tmp)
save_location = Rails.root.join("tmp", "#{issueable.name.parameterize}_checklist.pdf")

# Write the PDF to the temp file
File.open(save_location, "wb") do |file|
  file << pdf
end

# Create a new uploader to put the file on S3, most stuff defined in uploader
issueable.checklist = ChecklistUploader.new
File.open(save_location, "r") do |file|
  issueable.checklist.store!(file)
end
issueable.save!

And here's the checklist uploader for reference.

class ChecklistUploader < CarrierWave::Uploader::Base
  storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}_checklists/#{mounted_as}/#{model.combined}"
  end

  def extension_whitelist
    %w[pdf]
  end
end

Upvotes: 2

Related Questions