I have a page where the user can select multiple costprojects
to create a single pdf (including S3 attachments). It works fine - except if the user selects quite a few, then app will time out (30 sec).
So, I would like to create the pdf and email it in the background using gem 'delayed_job_active_record'
.
This works without delayed job:
def pdfemail
@costprojects = Costproject.find(params[:costproject_ids])
pdf = CombinePDF.new
@costprojects.each do |costproject|
@costproject = costproject
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf << CombinePDF.parse( Net::HTTP.get( URI.parse( attachment.attach.url ) ) )
end
end
useremail = current_user.email
CostpdfMailer.costpdf_email(useremail,pdf).deliver
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
This was my first try that didn't work - I added:
handle_asynchronously :pdfemail
My second try:
def pdfemail
@costprojects = Costproject.find(params[:costproject_ids])
CostprojectsController.delay.pdfemail2(@costprojects)
end
def self.pdfemail2(costprojects)
@costprojects = costprojects
pdf = CombinePDF.new
@costprojects.each do |costproject|
@costproject = costproject
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf << CombinePDF.parse( Net::HTTP.get( URI.parse( attachment.attach.url ) ) )
end
end
useremail = current_user.email
CostpdfMailer.costpdf_email(useremail,pdf).deliver
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
With the 2nd try, I get:
undefined method `render_to_string' for CostprojectsController:Class
The same render_to_string
worked when it was just pdfemail
.
3rd try:
pdf2 = CostprojectsController.new.render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
With the 3rd try, @costproject
isn't getting passed to costprojects/viewproject.pdf.erb
You can't handle a controller action asynchronously.
And you can't call controller methods like render
, render_to_string
and flash[:notice]
from within a class method self.pdfemail
since those methods are instance methods on the controller.
You'll have to separate out the pdf email logic from the controller response logic, like this:
def pdfemail
pdf_template = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
PdfMailer.new.pdf_email params[:costproject_ids], current_user.email, pdf_template
redirect_to :back
flash[:notice] = 'An Email containing a PDF has been sent to you!'
end
Then in another file:
class PdfMailer
def pdf_email(costproject_ids, useremail, pdf_template)
@costprojects = Costproject.find(costproject_ids)
pdf = CombinePDF.new
@costprojects.each do |costproject|
@costproject = costproject
# This part needs work, you can't use render_to_string here.
# You'll have to generate this pdf some other way.
pdf2 = render_to_string pdf: "SLCO Captital Projects.pdf", template: "costprojects/viewproject", encoding: "UTF-8"
pdf << CombinePDF.parse(pdf2)
costproject.attachments.each do |attachment|
pdf_response = Net::HTTP.get URI.parse(attachment.attach.url)
pdf << CombinePDF.parse(pdf_response)
end
end
CostpdfMailer.costpdf_email(useremail, pdf).deliver
end
handle_asynchronously :pdf_email
end
The PdfMailer
class does not use any controller instance methods such as render
, render_to_string
, or flash
, that has to be taken care of in the controller since you can't handle controller action asynchronously. Consequently you have to pass in the data you need, in this case the costproject_ids
, useremail
from current_user.email
.
Inside the PdfMailer
class you'll have to generate the pdf in that loop with some other method than the controller's render_to_string
. There's plenty of other tools for this here: https://www.ruby-toolbox.com/categories/pdf_generation.
On your 3rd try: it isn't working because you're instantiating a new controller which doesn't have the @costproject instance variable set in it.