I'm using Prawn to generate a PDF from the controller of a Rails app,
...
respond_to do |format|
format.pdf do
pdf = GenerateReportPdf.new(@object, view_context)
send_data pdf.render, filename: "Report", type: "application/pdf", disposition: "inline"
end
end
This works fine, but I now want to move GenerateReportPdf into a background task, and pass the resulting object to Carrierwave to upload directly to S3.
The worker looks like this
def perform
pdf = GenerateReportPdf.new(@object)
fileString = ???????
document = Document.new(
object_id: @object.id,
file: fileString )
# file is field used by Carrierwave
end
How do I handle the object returned by Prawn (?????
) to ensure it is a format that can be read by Carrierwave.
fileString = pdf.render_file 'filename'
writes the object to the root directory of the app. As I'm on Heroku this is not possible.
file = pdf.render
returns ArgumentError: string contains null byte
fileString = StringIO.new( pdf.render_file 'filename' )
returns TypeError: no implicit conversion of nil into String
fileString = StringIO.new( pdf.render )
returns ActiveRecord::RecordInvalid: Validation failed: File You are not allowed to upload nil files, allowed types: jpg, jpeg, gif, png, pdf, doc, docx, xls, xlsx
fileString = File.open( pdf.render )
returns ArgumentError: string contains null byte
....and so on.
What am I missing? StringIO.new( pdf.render )
seems like it should work, but I'm unclear why its generating this error.
You want to create a tempfile (which is fine on Heroku as long as you don't expect it to persist across requests).
This one took me couple of days, the key is to call
render_file
controlling the filepath so you can keep track of the file, something like this:in one of my Models e.g.:
Policy
i have a list of documents and this is just the method for updating the model connected with the carrierwave e.g.:PolicyDocument < ApplicationRecord mount_uploader :pdf_file, PdfDocumentUploader
in one of my Prawn PDF File Generators e.g.:
PolicyPdfDocumentX
in here please note how im rendering the file and returning the filepath so i can grab from the worker object itselfin the "global" Worker for creating files and uploading them in the s3 bucket e.g.:
PolicyDocumentGeneratorWorker
finally how to test, run
rails c
and:NOTE: im using meta-programming in case we have multiple and different file generators,
constantize.new
is just creating new prawn pdf doc generator instance so is similar toPolicyPdfDocument.new
that way we can only have one pdf doc generator worker class that can handleall
of your prawn pdf documents so for instance if you need a new document you can simplyPolicyDocumentGeneratorWorker.new.perform('report_y.pdf', 'PolicyPdfDocumentY',the_policy)
:D
hope this helps someone to save some time
Here's a way you can use StringIO like Andy Harvey mentioned, but without adding a method to the StringIO intstance's eigenclass.
It turns out
StringIO.new( pdf.render )
should indeed work.The problem I was having was that the filename was being set incorrectly and, despite following the advise below on Carrierwave's wiki, a bug elsewhere in the code meant that the filename was returning as an empty string. I'd overlooked this an assumed that something else was needed
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Upload-from-a-string-in-Rails-3
my code ended up looking like this