The document that is attached can be downloaded as well, and I wanted to reuse as much code as possible.
For the views we use prawn and prawnto gems. The recipe consists of 3 steps:
- generate and save the pdf to disk
- email (attach the pdf)
- delete the pdf
The 'view' used to generate the pdf document is located in /app/views/my_controller/show.pdf.prawn and is shared for both emailing and downloading actions.
Generate a pdf file AND save it to disk
The key is to inherit from Prawn::Document, and to provide the @ instance variables that the view expects:class MyGenerator < Prawn::Document
def initialize(options)
options && options.merge!({:inline=>true})
create_instance_variables(options.delete(:variables))
super(options)
end
def render_template(template)
pdf = self
pdf.instance_eval do
eval(template) #this evaluates the template with your variables
end
ensure_path
pdf.render_file(File.join(output_path,filename))
end
private
def create_instance_variables(vars)
return if vars.blank?
vars.each_pair do |k,v|
instance_variable_set("@#{k}", v)
end
end
def output_path
@output_path ||= File.join(Rails.root,'tmp','documents')
end
def ensure_path
FileUtils.mkdir_p(output_path)
end
def filename
@output_file ||= "#{Process.pid}::#{Thread.current.object_id}.pdf"
end
end
For example my view expects some variables as @start, @end and @records, and that the pdf document variable is named 'pdf'
template = File.read("#{Rails.root}/app/views/my_controller/show.pdf.prawn")
writter = HemoPdfReport.new(:page_size => 'A4', :page_layout => :landscape, :variables => {:start => params[:start], :end => params[:end], :records => @results})
attachment = writter.render_template(template)
begin#send
MyPdfMailer.attachment_email(
:user => @user,
:destination => @email,
:message => @text,
:attachment => attachment.path).deliver
@report.save!
ensure
FileUtils.rm_f(attachment.path)
end
Things to note:
- The create_instance_variables method copies the 'variables' parameter used in the initialization, to instance variables.
- As our generator is a PrawnDocument, we can pass it as the 'pdf' variable that the view expects (note the line pdf = self before the eval)
- We automatically provide a filename to the output. We could have used a timestamp but we use one based on the current thread. The key here is to avoid using an static name (we can have several processes / threads generating documents concurrently)
Emailing PDF
Emailing a file is really simple, just follow the http://guides.rubyonrails.org/action_mailer_basics.htmlAn example of my mailer
def attachment_email(options)
attachment = options[:attachment]
@user = options[:user]
@destination = options[:destination]
@text = options[:message]
attachment.present? && attachments['report.pdf'] = {
:mime_type => 'application/pdf',
:content => File.read(attachment)
}
subject = "Your Pdf Document"
mail(:to => @destination, :subject => subject)
end
Delete file
Once the file is emailed, dont forget to delete it. The ensure block is meant for this.Profit!
No comments:
Post a Comment