How to generate PDF and Excel reports on Rails using Wicked PDF and AXLSX.
If you are involved in the web development world, you might notice your clients ask you for a way to export information from the web app to a file in a format that they are more familiar with, for instance, Excel or PDF. This is very common in web development, so common that there are in fact several libraries or gems whose only purpose is to make this process easier.
For this tutorial, we will be using Ruby as our programming language, and Ruby On Rails. On top of this, we will use Heroku to store our web application.
For the libraries, we will use axlsx_rails gem to export to excel and wicked_pdf for PDF exporting. First, I will show you how to use the Wicked PDF. For this, you would need to have a working application in Ruby on Rails.
Not only we will get into how to install and start while using these libraries, but we will also mention some issues or problems we encounter when first using these gems and how we can be able to solve this issues and continue using the gems with no more problems. Some of those issues are mainly about compatibility between the computer being used in the project, and the server which is storing our web application (Heroku).
Before we go into any further details, let’s get some context from the official documentation: “Wicked PDF uses the shell utility wkhtmltopdf to serve a PDF file to a user from HTML. In other words, rather than dealing with a PDF generation DSL of some sort, you simply write an HTML view as you would normally, then let Wicked PDF take care of the hard stuff.”
The first step to include Wicked PDF in your project is to have the gem and all its dependencies declared in the Gemfile of our project. So go ahead and open the Gemfile in your project and add the following lines.
gem ‘wicked_pdf’ gem ‘wkhtmltopdf-binary
The first one is the Gem itself, and the second gem being declared is a binary file or dependency which the gem needs to install so the main gem can work properly.
Now we need to install the gem; do a bundle install. The next step now is to declare a new MIME Type in our project. For that go to config/initializers/mime_types.rb
Mime::Type.register “application/pdf”, :pdf
MIME Type is a method that you call on the block object format or the symbols that you pass to respond_to in your controllers. It Is basically adding a new format to the application so you can pass it to the respond_to method.
Like most gems, once everything is set up we need to load or install the initial configurations, which are often done by using the rails command and the generated statement along with the name of the gem to install; something like this...
rails generate wicked_pdf
This will create a new file under the config/initializers path with the name wicked_pdf.rb, we might actually use this file later, feel free to take a look at it.
Now we have pretty much everything we needed for the gem to work. Let's start using it then. Keeping in mind the definition of Wicked PDF previously saw on the official documentation, we know that Wicked PDF works with a template or view that includes HTML content and then transforms that content to pdf format.
So the first thing to do now it’s to determine which the controller is the one that handles the date that you need for the PDF, once you know this, you can make a specific action of that controller to respond to a new format, in our case PDF.
Let me give you an example of this. Let's say you have an application which stores the information of a series of car dealerships. Naturally, your application would have to show the action that displays the information for a given dealer, so what a better way to send this information to someone who does not have access to this web application than exporting that information to a PDF, and then send it to that person.
Following the previous example, we can then modify the show action to respond to the desired format (as long as this format exists in the application).
With the previous statement, we are letting Rails know that from now on, the show action will respond to both HTML and pdf formats; for the first one, it will look for the html.erb view and for the second one, it will look for a show view under reports_results directory that has a pdf.erb format. If you do not specify anything in the template tag, then it will look by default in the views that belong to the controller, the action’s view and in the specified format.
The second helper
Always remember to name your pdf views as follows pdf.erb, or if you are using haml then use the following extension html.haml.
These two helpers used in the previous examples are just a few of the ones available with wicked pdf. So far, those are pretty much the only ones that I have needed in order to work with the gem. I will leave you a brief list of some other helpers that are included with the gem which you might actually find useful.
disposition: the disposition helper allows you to either downland directly the pdf as a file, or just open it in a different tab without downloading the file.
Now that we already told our controller and its action to respond to the pdf format, we need to create a new view file with the right extension, which is the pdf.erb extension. For our example and in order to keep it consistent, we will create a new file named ‘show.pdf.erb’ since we told the controller that the view or template that we want to use was in the views/reports_results/ path under the name of ‘show’ and with the pdf object or helper, Rails assumes that the file will have a pdf.erb extension.
If you want to try if the setup is actually working, what you can do is add maybe an h1 or a simple HTML tag to see if the view is actually being called by the controller and the action. If it is, it will open a new tab with the pdf file and show you the HTML tag you used. You can test this by adding the URL directly with the right extension which for this test would be (.pdf) so it can be something more or less like this ‘localhost/reports_results/1.pdf’, this way it will know the controller that we are referring to the action and the format that we need.
Another way to test this would be to create a simple button_to or link_to and then specify the route and the format that you want to access.
link_to ‘Export to PDF’, reports_result_path(format: ‘pdf’, dealer)
Here we are declaring a link to the reports_results controller, but because we are using singular word instead of the plural one, and because of the Rails routes the framework, it assumes that we are referring to the show action and it asks us for a reference to look the right information. In our case we want to obtain the report for a specific dealer, so we pass the dealer object which contains the dealer’s information, that way the controller will only return the information for the report of that specific dealer that we want.
Now that we know how to obtain the data from the database by using the controller and the rails conventions, we can start declaring some styles for our pdf template, for that we need to use some of the wicked pdf helpers to include css stylesheets in our view. The necessary helper is the:
This helper allows us to specify a stylesheet file in which we want to include the styles for our pdf template, the easiest way is to declare the application file, that way the wicked pdf will look in the assets path for all the files and styles that belong to the pdf template.
Or you can also include the absolute path of specific files you want to use.
It is also possible to use CSS libraries like Bootstrap. The only thing you need to consider is that you need to include all the necessary dependencies. It would look something like this:
= stylesheet_link_tag "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" = wicked_pdf_stylesheet_link_tag 'dashboard', media: 'all', 'data-turbolinks-track' => true = wicked_pdf_stylesheet_link_tag('bootstrap')
The example above is only for Bootstrap. In case you want to use different libraries, you would need to do a little research but in essence, it is very similar to the bootstrap example.
In here I would like to do a brief parenthesis to tell you some problems that I have faced while using the gem in a project for a big client. It turns out that the default binary that wicked pdf uses (in this case the latest binary) is not fully compatible with all computers. There are some computers that have different binaries for pdf, and there the functionality in those devices (like my MacBook Air) is not as it should be. The main issue that I experimented is that when I was showing some content in the pdf file, it looked good in my computer, but when that same view rendered in a different computer it was not the same model as mine. The content looked completely different from mine. After looking around and doing some research I found an additional gem that extends the binary compatibility with more computers or devices and makes it more ‘stable’ for all of them.
Adding additionally this gem in my gem file to the initial ones solved my issues with the way that the content was rendered locally and in my colleague's computers.
Another issue that I encountered while developing the reports for our client is that when we did the staging deployment, we realized that the content was actually not being displayed in the way it should, or in the way that it looked locally. For that, we needed to include a different binary that was compatible with the Ubuntu version being used in Heroku (ubuntu 16.0). Basically, this gem takes care of making Heroku fully compatible with the Wicked PDF gem. The gem in question is:
This helped us to make the content generated by Heroku in the pdf’s consistent with the ones that we had locally. Just keep in mind that this gem will only help you if you are also using Heroku as your server. If you are using a different one and you encounter similar issues, then do some research on whether you need a special binary for your server or if there is a specific way to fix the issue you are facing.
In the end, these are the only tips that might be helpful to you in case you happen to deal with similar issues when using these gems.
Another tip I want to give you for this gem refers to when you want to use flex box in your pdf view. Unfortunately, the gem does not include flex box styles by default, you need to import them in the stylesheet you are using for the pdf view. Something like this:
Flexbox is a very convenient way of applying styles to our website, it is also very helpful when creating reports; it makes the process easier and more reliable. In the last example, we wanted to be able to use the
display: flex; statement, this way all the elements that belong to this parent will be displayed in a horizontal line one after the other. As you noticed we needed to import some webkit elements, this already gave us a lot from flex-box, but if you encounter something that we do not mention in here, such as a specific tag from flex box that does not seem to work even after exporting the webkit tags, then you might need to google it, and I am pretty confident you will be able to solve it.
Now let’s talk a little bit about the Excel reports. In order to use the xlsx format in our web app, the procedure is very similar to the one explained for the wicked pdf gem. Basically, we need to declare the libraries along with all its dependencies, and also declare the mime type and make the controller and action also respond to this format.
The gem that we will use for this is the AXLSX gem, but the one that is specific for Rails is axlsx_rails. This gem has a couple of dependencies. This is what we will need:
gem 'axlsx', git: 'https://github.com/randym/axlsx.git', ref: 'c8ac844' gem 'axlsx_rails', '>= 0.5.1' gem 'rubyzip', '>= 1.2.1'
If you noticed, we are requesting a specific version of the axlsx gem, this is because the axlsx_rails gem requires this specific version of the axlsx gem. Feel free to try different options of the axlsx gem. I am just letting you know which one was the one that worked for me.
Ok, now we have all the gems and dependencies that we need to use the gem. The next step is to declare the new Mime Type. It Is going to be exactly as it was with the PDF mime type, just add the next line under config/initializers/mime_types.rb
Mime::Type.register 'application/xlsx', :xlsx
This will allow Rails to respond to a new format, in this case, it will be for Excel (xlsx). So, having this in mind I guess you already have an idea of what the next step is.
Respond to the new format in the controller that you require, you can do something similar as we did with the pdf format. At the end it will look something like this (if we leave the other formats as well):
It pretty much follows the same principles or logic as the wicked pdf gem, if you do not declare any templates Rails, it will look for one in the views folder and it will select the one that matches the action’s name and the format. So let’s pretend that the response to the declaration was in a show action or method, in that scenario, Rails would look for a template in the views folder with the name ‘show’ and the extension .pdf.erb (show.pdf.erb).
It is also possible to change the name of the generated file, you only need to use the xlsx: tag instead of pdf: this way we are telling Rails to generate an xlsx file with the specified name as a string.
render xlsx: ‘name_you_want_to_use_for_generated_file’
And if you want to change the default template you can use the template helper:
No need to pass the format, Rails already knows which format to look for with the response to the method.
Now that we have the necessary structure, let’s talk about how to print information in the excel sheet.
In the template that you created for your action whether it was the actions name or a different name for the template, be sure to include the following lines...
These are the libraries from the gems that we need to use in order to display the content in the workbook or spreadsheet (however you want to see it). The add_worksheet method includes a lot of actions that we can use in order to show information in the cells.
There are several methods you are going to use when working with this gem.
To print or display a row, there is a method called add_row that accepts an array as a parameter; each element of the array will be displayed in each horizontal cell in the order that the array has. For example, if you use the following statement:
sheet.add_row [1, 2, 3, 4, 5]
Let's say that you have a database table named ‘products’ and you want to print all the products you have along with the prices and the quantity for each one, in this case, you would need to have a controller action that retrieves the data for you from the right place, like this:
This will give us all the products we have in that table with all its attributes. So in order to print them, you would need to first construct the array for each row and then pass the array with all the data to the
add_row  method. Here is a quick example:
In here we are iterating on every product that we obtained from the controller and creating an array so the add_rowmethod can use it. When we pass the object to this method, the gem will take care of printing each value in each cell according to the order you already gave to the object.
There are a lot of methods you can use for multiple purposes, for example. if you need to include an image in the excel or multiple images, you can use this other method:
Another thing you might use is styles. The styles need to be declared first, and whenever you want to apply them to a specific row or to the whole sheet, you just need to ‘call them’.
Here you have an example of how to declare the styles:
And here is how you apply it to a row:
With all this in mind, we come to the conclusion that the process of generating reports with Ruby on Rails ends up being a very simple process. It is just a matter of obtaining the right data, sort it in the way that you need to display it, and finally start using the existing methods from the gem to do what you need to do. Remember, the best way to use a new gem is always to read its official documentation, and explore its code to see the available methods which compose it. Hope you have an easy time using these gems and find this blog post helpful.