Tricking PaperTrail into saving attributes that didn’t change
Health Information Exchanges (HIE) are pretty exciting innovations in health tech — they allow us to digitally push or pull information about a patient through a network of other healthcare providers. Imagine that you’ve seen your primary care provider and gotten a referral to a specialist, then visited that specialist and completed your treatment. A HIE would let us incorporate your treatment and test results into your Health Record, and then expose that data to you and your care providers.
Patients have to opt-in to the HIE, though. And as regulations evolve, we might have to change the legal agreement. Patients then have to opt-in to the new version, and potentially also opt-out of older versions. This presents a bit of a headache: tracking whether or not a patient has opted-in isn’t too difficult, but tracking it across many versions gets a bit trickier.
PaperTrail persists changes to a model to the database
PaperTrail (https://github.com/paper-trail-gem/paper_trail) is a pretty popular tool for persisting changes to a model, and we already use it to persist changes to a patient’s preferences. Ideally we’d be able to use PaperTrail to track both the hie_opt_in and the hie_version and let you look up which versions a patient has opted-in to or out of.
Here’s where the problem is: PaperTrail works by looking at attributes that have changed. However, if a patient has opted-in to version 1 of the HIE agreement and then opts-in to version 2, the hie_opt_in value hasn’t changed — only the hie_version has changed (line 11 below). PaperTrail will happily log the change to hie_version (line 17), but in order to find out whether or not the patient opted-in to our out of that version you have to do a messy lookup of the previous version (line 24).
ActiveModel::Dirty can manipulate what PaperTrail persists as “changed”
Enter ActiveModel::Dirty. It’s got a bunch of useful methods, but one in particular helps us out here: #[attr_name]_will_change! (see https://api.rubyonrails.org/classes/ActiveModel/Dirty.html). PaperTrail actually relies quite a bit on ActiveModel::Dirty already because it leverages the change functionality ActiveModel::Dirty provides to track versions of a model. By marking both the hie_opt_in and hie_version as attributes that will change (lines 10–11 below), when we actually persist the changes PaperTrail will automatically persist both regardless of whether or not the values actually did change. Then determining what a patient has opted-in to or out of is as simple as looking at the latest PaperTrail entry for a given hie_version.
As a final step, you can set up meta columns (which lets you store metadata as its own SQL column) if you’d like to use simpler SQL queries over parsing the PatientPreferencesVersion#object_changes field (which is a text column). I won’t go into details, but there’s more information in the PaperTrail documentation: https://github.com/paper-trail-gem/paper_trail#4c-storing-metadata.
That’s pretty much it! Just add 2 lines of code and we can easily look up when a patient has opted-in to or out of any version of the Health Information Exchange. Feel free to let me know if you have any further suggestions / questions by commenting below.