Category Archives: Rails

Magic sprinkles for Capybara and PDF

One of the frequent things I end up doing these days is generating reports. When you create a start up called Report Hero, it comes with the territory. The two most common outputs that I’m generating are PDF, and word (yeah word, I’m sorry).

I’ve recently being going through some pain getting testing working with PDFs and capybara. It took me way to long to get to the answers, so I’m going to document what I ended up with.

First, it’s worth noting that at the time of writing, poltergeist doesn’t support downloading files:
https://github.com/teampoltergeist/poltergeist/issues/485
http://stackoverflow.com/questions/35585994/downloading-a-csv-with-capybara-poltergeist-phantomjs

This means that you’ll need to use a different driver, WebKit works well for me (https://github.com/thoughtbot/capybara-webkit). As mentioned on that github project page, by adding webkit you’ll probably need to make sure that you run your tests on CI with an xvfb server.

If you are on rails 3 (did I mention I was doing this work on a legacy project), the next thing to consider is that depending on your setup you’ll need to make it possible to have two threads running at the same time to help enable the wkhtmlwrapper gem (wicked pdf or pdfkit) to work. So in you’re test.rb environment you’ll need to set config.threadsafe!

Once you’ve got PDFs being downloaded, there a number of options for performing the tests around them. They essentially boil down to reading the content using something like PDF Reader, and then performing assertions on it.

Pivotal have a decent blog post talking about this: https://content.pivotal.io/blog/how-to-test-pdfs-with-capybara , and prawn have extracted a gem to help: https://github.com/prawnpdf/pdf-inspector . For what it’s worth in this project, I went with the prawn pdf-inspecter gem.

So, the final steps I ended up with are:

  • use the webkit driver so you can download the pdf (with appropriate CI settings)
  • (in rails 3) set config.threadsafe! so the pdf generation can happen
  • use pdf-inspector to extract content from the PDF and perform some assertions.

With this setup you can

Adding rubocop to a legacy project

To add rubocop to a legacy project, first grab a .rubocop.yml that specifies your projects code style and then do:

rubocop -c .rubocop.yml --auto-gen-config --exclude-limit 500

Then you’ll want to include the automatically generated todo file into your .rubocop.yml.

inherit_from: .rubocop_todo.yml

Run rubocop. Any violations that you now see will be caused by your config overriding the todo exclusions.  Find the cops causing problems using rubocop -D

Then fix them by doing things like:

increasing your Metrics/LineLength in  the .rubocop.yml

or perhaps setting the Enabled to false.

By doing this you can relatively quickly add rubocop to a legacy project with settings matching an organisations coding style, ready for you to really start making a codebase better.

Cool stuff you can do with rails has_one

Over the past three months has_one has shown itself to be the answer to questions that I was struggling to get an elegent answer for.

The two cases I found were:

1. Specifying a belongs_to :through
2. Accessing one item of a list of related records

The second case ends up expanding to be anytime you want to have a named scope that returns a single record.

Specifying a belongs_to :through with has_one

Say you have a model of the form:

You can see that we’ve used the standard has_many and belongs_to methods to model the relationships between the entities. We’ve also thrown in a has_many through to help model the fact that an organization has_many employees. Conceptually we also want to be able to specify the inverse of this relationship on the Employee. Something like:

The way to implement the belongs_to :through concept is to use has_one :through

Accessing one item of a list of related records

There can often be a desire to specify a scope that returns a single record. This isn’t possible in rails, as scopes by definition are chainable and will return subclasses of ActiveRecord::Relation, which is a list like object.

The way to do this is to setup a has_one with a block specifying a scope. For example:

has_one :head_of_department, -> { where role: ‘department_head’ }, class_name: ‘Employee’

For bonus points the proc and conditions can also be parameterised, so we can end up with a has_one relationship with a parameter. To see the coolness of this we can extend the model a little bit to include
a many-to-many entity and a has_many through relationship.

With the above model it would be handy to have an easy way of accessing the ProjectDepartment easily when you are working with projects. has_one to the rescue.

So with our collection of has ones all together, we end up with code something like:

 

has_one is a powerful tool to have in your rails modelling. Take advantage of it to give some really nice clean looking code.