Skip to main content

Export PDFs in a Ruby on Rails project

Today's blog post is about exporting PDFs in a Ruby on Rails project. We will discuss how to achieve the following tasks:

  1. Generate content as PDF
  2. Handle the PDF generation as a background job
  3. Make the link available in an email for download purposes
Generate content as PDF

For this section, the following Gem is used out of many libraries which does the same thing.

gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'

If you want to take a look inside this library here is the link.

This awesome library makes PDF generation easy. These are the steps that were followed:

1. Add the Gem into the file and install it.

bundle install

2Change the required method with respond_to block.

    respond_to do |format|
      format.html
      format.pdf do
        html = render_to_string template: "pdf/diary.pdf"
        folder = "#{@diary.id}_#{@diary.child_name}_#{@year}"
        file_name = "#{@diary.child_name}"
        
        pdf = WickedPdf.new.pdf_from_string(html)
        
        file_name = "#{file_name}-#{year}"
        save_path = Rails.root.join("public/export/#{folder}","#{file_name}.pdf")
        
        File.open(save_path, 'wb') do |file|
          file << pdf
        end
        
        redirect_to finish_download_content_index_path, notice: t('download_link_will_be_sent_to_your_email')
       end
     end

 

For this, a separate template for the PDF generation had to be created and it is referenced in, 

html = render_to_string template: "PDF template file"

 

Those files are added in a folder named “PDF” under the view directory as below:

pdf template under view
             pdf template under view

 

Wicked PDF library generates the PDF from the HTML string. So we can parse the string into the wicked PDF library as follows.

pdf = WickedPdf.new.pdf_from_string(html)

html refers to the string that is rendered from the template. 

That’s all here; but there is a slight issue when this string is huge. It will hold the server until it completes the PDF generation. Therefore this process needs to be in the background, so that it’ll generate the PDF without interrupting the main thread.

How to handle PDF generation as a background job? 

For this, the Rails Active Job facility can be used. First, create a class to perform this operation.

To create a new job:

rails generate job pdfs_generate

Those jobs will be saved in a new folder named "Jobs”.

2

 

For the Job execution a queuing library is required. There are many such libraries. Some examples are below.

Let's use the “Delayed Job” library:

gem 'delayed_job_active_record'

 

It is time to embed the wicked PDF generation process inside the Job class now.

Note:

There are two main things in a JOB.

1. Perform method

2. Call backs → after_perform, before_perform etc.

Perform method will carry out the process in the background. Once it is completed, it will call the "callback method" named as “after_perform”, which will be used to send the email after the PDF generation process.

Here is the PDF generate Job class file.

class PdfsGenerateJob < ActiveJob::Base
  queue_as :default

  after_perform do |job|
    puts "****************************************"
    puts "************** DONE !!!!!!! *************"
    puts "****************************************"
    puts "Email should sent to :  #{@user_email}"
    puts "Diary Id:  #{@diary_id}"
    puts "****************************************"
    
    # Sending the email
    DownloadMailer.send_download_pdf_link(@user_email, @folder, @file_name).deliver_now
  end

  def perform(html, folder, file_name, user_email, year, diary, year_start_date, year_end_date)
    pdf = WickedPdf.new.pdf_from_string(html)
    file_name = "#{file_name}-#{year}"
    save_path = Rails.root.join("public/export/#{folder}","#{file_name}.pdf")
    File.open(save_path, 'wb') do |file|
      file << pdf
    end
    @user_email = user_email
    @diary_id = diary.id
    @folder = folder
    @file_name = file_name
  end
end

Show method was changed by getting the perform function in a new thread in order to call the Job process and to call the perform function using the later postfix.

    respond_to do |format|
      format.html
      format.pdf do
        Thread.new do
          html = render_to_string template: "pdf/diary.pdf"
          folder = "#{@diary.id}_#{@diary.child_name}_#{@year}"
          file_name = "#{@diary.child_name}"

          PdfsGenerateJob.perform_later(html, folder, file_name, @current_user.email, @year, @diary, @year_start_date, @year_end_date)
        end
        redirect_to finish_download_content_index_path, notice: t('download_link_will_be_sent_to_your_email')
       end
     end

 

Wicked PDF generation will happen in a new thread in the background. Therefore, it won't affect the main rails process. User is free to do any other work.

rails generate job guests_cleanup

According to this code, once the PDF work is completed, it will send the email to the current user as described above. (remind → after_perform callback).

Hope the process is clear to you. Please feel free to comment below if you have any questions. 


LEAVE A COMMENT







POST COMMENTS


© 2020 Creative Software. All Rights Reserved | Privacy | Terms of Use