Making a PDF File and Automatically Attaching It
I’m writing a rails site that collects information from an applicant and references from other people that will then be reviewed by a different group of people. For the programmer, it’s easy to present the information from the database on a webpage and then provide links to uploaded documents. However, this is not necessarily the easiest way for the reviewers to read all the information. Based on other sites that do the same type of job, I’ve learned that reviewers want to be able to download a single file with all the information in it. I’ve put together various hacks in the past to do this, but have recently come up with a proper way. The following isn’t complete, but it presents the basics of how to do this.
There are three models that I’m using.
- Applicant – This is where the applicant initially fills out all of their info and uploads a CV.
- Reference_Document – We require each applicant to give us the names of two people who will write references for them. These uploaded documents are stored in reference_document. Each applicant has_many reference_documents.
- Application – This is the single model that just has an uploaded file that is a pdf created by combining the applicant’s info, CV and any reference_documents provided. Each applicant has_one application.
There are three gems that I’m using to create the application. Paperclip is to upload and store files. Prawn is used to create a pdf file with all the information that was stored in the database. And CombinePDF is used to combine all the pdf files into a single one that will get stored in application.
Applicant Model (partial)
class Applicant < ActiveRecord::Base has_one :application, dependent: :destroy has_many :reference_documents, dependent: :destroy has_attached_file :cv, path: "#{Rails.root}/storage/:attachment/:id/:filename" validates_presence_of :cv_file_name validates_format_of :cv_file_name, with: /pdf\Z/i, message: "must be a PDF file" do_not_validate_attachment_file_type :cv
ReferenceDocument Model (partial)
class ReferenceDocument < ActiveRecord::Base belongs_to :applicant has_attached_file :doc, path: "#{Rails.root}/storage/:attachment/:id/:filename" validates_presence_of :doc_file_name validates_format_of :doc_file_name, with: /pdf\Z/i, message: "must be a PDF file" do_not_validate_attachment_file_type :doc
Application Model
class Application < ActiveRecord::Base belongs_to :applicant has_attached_file :info, path: "#{Rails.root}/storage/application/:attachment/:id/:filename" do_not_validate_attachment_file_type :info end
Let’s just look at the applicants_controller because that’s where most things happen. (When references are uploaded, similar methods will be called to update the application.)
The create method does most of the work. (Later, update will repeat most of this as well.)
def create @applicant = Applicant.new(applicant_params) if @applicant.save @applicant.send_thanks_for_applying @applicant.send_request_for_recommendation generate_application(@applicant) redirect_to thanks_path else render :new end end
Let’s look at the generate_application method.
def generate_application(applicant) @applicant = applicant info = make_pdf_of_info(@applicant) pdf = CombinePDF.new pdf << CombinePDF.load("#{info}") pdf << CombinePDF.load("#{Rails.root}/storage/cvs/#{@applicant.id}/#{@applicant.cv_file_name}") unless @applicant.reference_documents.empty? @applicant.reference_documents.each do |ref| pdf << CombinePDF.load("#{Rails.root}/storage/docs/#{ref.id}/#{ref.doc_file_name}") end end pdf.save "#{Rails.root}/tmp/#{@applicant.fullname_no_spaces}_travel.pdf" @application = Application.create!(info: File.open("#{Rails.root}/tmp/#{@applicant.fullname_no_spaces}_travel.pdf"), applicant_id: @applicant.id) end
The first step is to make a pdf with whatever info is stored in the database. Looking at that method:
def make_pdf_of_info(applicant) @applicant = applicant @file = "#{Rails.root}/tmp/#{@applicant.fullname_no_spaces}.pdf" Prawn::Document.generate("#{@file}") do |pdf| pdf.text "#{@applicant.fullname}" ... (more lines like above) end return "#{@file}" end
Along with creating the file, it also returns the name of the file created. The generate_application method can then use this file name, along with the stored CV file and any references to make the final application. Once we have this file, the last line in the method, “uploads” it to the Application model along with the applicant’s id.
That’s basically it. We now have a single file with all the information for an applicant. As references come in, we’ll run the same methods, but will have to change the create! to an update! because each applicant only has one application.