delegate_cached is a Ruby gem to easily cache delegated attributes on an ActiveRecord model, and have the cache updated when the delegated value changes.
One regret I have from my Intridea days is that I was so busy running the business that I rarely had time to contribute in meaningful ways to open-source projects. Sure, I pitched in a commit here or there on various projects, but largely my contributions involved clearing the schedule of our team’s engineers enough so they could work on their own open-source projects.
So, it is with great pleasure that, at long last, I release my first
open-source Ruby gem: delegate_cached
The Problem
As with most open-source contributions, this gem came about in response to a pain point I was having on an existing project. After installing the bullet gem and tracking down a lot of N+1 queries, I found myself frequently having to include a rarely-touched model.
Our Company
model references an IndustryCompanyList
(names have been changed
to protect the innocent), which is a occasionally-updated “official” list of
companies from an external industry source. OfficialCompanyList
has a name
attribute we would like to display as the Company name, so we delegate to
OfficialCompanyList
as below.
This works well. With any instance of Company
, we can call company.name
and
we get the value through the delegated association.
IndustryCompanyList
must then, however, be included every time company.name
is referenced to avoid the N+1 query issue. Storing the value from
IndustryCompanyList
in a column on Company
would prevent this issue, but
there would need to be a process to prevent the data from going stale when
IndustryCompanyList
values are updated. Enter, delegate_cached
Example
We start with two classes in a belongs_to
: has_many
relationship, where
the Hiker
class has a name
attribute.
As in the case above, this works well. We can call thru_hike.name
to
retrieve the delegated name
value from the associated Hiker
. But if the
hiker’s name rarely changes, we could instead cache it on the ThruHike
model
to avoid having to touch the Hiker
model each time we want to access the
name
.
This can now be easily accomplished with delegate_cached
using virtually the
same syntax as delegate
.
Since ThruHike
is now cacheing the value, you would need to create and run a
migration adding a name
column to the thru_hikes
table.
What you now get by using delegate_cached
is:
- When
thru_hike.name
is nil, it updates itself with the delegated value. Subsequent accesses will return the cached value. - When the
thru_hike
updates the value of itshiker
association, abefore_save
callback updates thethru_hike.name
to the new value. - When the associated
Hiker
updates its name, anafter_save
callback updates the value inThruHike
There’s still more work to be done on delegate_cached
to make it backwards
compatible with Rails 4.X, and other features I’m interested in adding. I
welcome bug reports, feedback, suggested improvements, and any other comments
on the Github site below.