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.

  1. Applicant – This is where the applicant initially fills out all of their info and uploads a CV.
  2. 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.
  3. 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

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
      redirect_to thanks_path
      render :new

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}")
    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)

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)
    return "#{@file}"

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.