-
Cleaning up this blog with AI
It has been several years since I wrote a post on this blog. Part of this is not really feeling like something I have worth articulating here (though that is changing a little lately), but a bigger part is that it felt like a huge amount of work to get this blog into a state where I didn’t feel like it was full of outdated articles and junk metadata. Something a little more current.
Well, we now have a tool that all of us can use to help ease this kind of menial work: AI. So, using ChatGPT, I cleaned house and wrote this article to cover how I did it.
read more... -
Six weeks at Discourse
I meant to make a post about this when I started, but I have now been working at Discourse as a Software Engineer for six weeks!
The best things about working at Discourse are:
- The team (39 people and growing, largest I have ever worked on) is fully remote and distributed across every continent! (well, except Antarctica)
- Asynchronous work culture has been the core of the company since its inception. Discourse bleeds asynchronously!
- The team is full of ridiculously talented people who are generous with their knowledge. Everyone is a stellar written communicator.
- The codebase is fully open-source and is varied. On any day I can be working on the huge core product, a plugin, a theme component, or contributing to an open-source project Discourse uses.
- There is a lot of reading and a lot of writing, which is kind of a thing of mine.
- Everyone is treated like grown-ups, and trusted to do their work on their own schedule.
- How much more time do you have? I can keep listing things!
I am over the moon with this job and still pinch myself each day when I think about where I work and who I work with. 🌟
-
CSV header converters in Ruby
The CSV library in the Ruby stdlib is a really great and easy to use one, and I’ve often used it for data migrations and imports. When importing data I often find it useful to validate the headers of the imported CSV, to ensure that valid columns are provided. Some users may provide columns in different cases to what you expect or with different punctuation (including spaces etc.). To normalize the headers when parsing a CSV, you can use an option passed to
new
(other methods such aparse
,read
, andforeach
accept the same options) calledheader_converters
. Here is a simple example of how you can convert the headers of the parsed CSV to lowercase:### # Source CSV looks like: # # First name,last Name,Email # Abraham,Lincoln,alincoln@gmail.com # George,Washington,gwashington@outlook.com downcase_converter = lambda { |header| header.downcase } parsed_csv = CSV.parse('/path/to/file.csv', headers: true, header_converters: downcase_converter) parsed_csv.each do |row| puts row['first name'] # => Abraham # => George end
Simple as that. You can do anything to the headers here. There are also a couple of built in header converters (
:downcase
and:symbol
) that can be used, and an array can be passed as an argument, not just one converter. Converters can also be used for cells in the CSV rows as well, not just headers. The documentation for the RubyCSV
class is quite clear and helpful, take a look to see all the other myriad options for reading and writing CSVs in Ruby.Originally, I found this solution and tweaked it a bit from this StackOverflow answer - https://stackoverflow.com/questions/48894679/converting-csv-headers-to-be-case-insensitive-in-ruby
-
Per-page background images using prawn and Ruby
Prawn is an excellent PDF generation library for ruby, and we use it for all our PDF needs at work (Webbernet at time of writing). Their manual is some of the best documentation I have read. Recently, I needed to set a different background image on every page of a PDF I was generating. The prawn documentation, while good, only shows how to use a background image for the whole PDF:
img = "some/image/path.jpg" Prawn::Document.generate(filename, background: img, margin: 100) do |pdf| pdf.text 'My report caption', size: 18, align: :right end
So, I decided to dig into their source code to see how they rendered the background image. After a short search I found what I needed. Turns out, this works for rendering multiple different background images! In prawn you can call
pdf.start_new_page
to start a new page, and on each new page I would call the following to set the new background for that page:background_image_path = 'some/path/for/this/page.jpg' pdf.canvas do pdf.image(background_image_path, scale: 1, at: pdf.bounds.top_left) end
I was able to generate the PDF with different background images perfectly with this code.
-
ImageMagick unable to load module error on AWS Lambda
This may no longer be relevant in 2025, but it was critical back in 2019. Leaving it as a historical curiosity.Last Friday (July 2019) we started seeing an elevated error rate in our AWS Lambda function that converted single page PDFs into images using ImageMagick. We had been seeing the same error crop up randomly in around a two week period before Friday, but we were busy with other things and didn’t look too deeply into it. This was a mistake in retrospect. Below is the error in question:
read more...Error: Command failed: identify: unable to load module `/usr/lib64/ImageMagick-6.7.8/modules-Q16/coders/pdf.la': file not found @ error/module.c/OpenModule/1278. identify: no decode delegate for this image format `/tmp/BEQj9G8xj1.pdf' @ error/constitute.c/ReadImage/544.
-
Rails Forms with Virtus and ActiveModel
In 2025 the Virtus project has been discontinued, replaced by dry-types, dry-struct, and dry-schema, and really the general dry-rb ecosystem. Consider using them instead, and see this article as a relic of the time.I absolutely HATED doing forms in Rails, until we came across this method of doing them at work. Our goal was to make forms simple to set up and to have clear logic and separation of concerns. We were using Reform at first, and although it worked well for simple one-to-one form-to-model relationships, it quickly fell apart with more complex model relationships were involved. As well as this, if there were complex validations or different logic paths when saving the forms, things quickly fell apart. And there was no way to control the internal data structure of the form. Enter Virtus and ActiveModel.
read more...
1 / 11 | Next → |