/activerecord-cached_at

Allows ActiveRecord and Rails to use a `cached_at` column for the `cache_key` if available

Primary LanguageRubyMIT LicenseMIT

ActiveRecord - CachedAt

This gem causes ActiveRecord to update a cached_at column if present, like the updated_at column.

When calculating a cache_key for a model it will also consider the cached_at column to determine the key of a model.

Any ActiveRecord::Migration that calls timestamps will include a cached_at column.

Call to ActiveRecord::Persistence::touch will also touch the cached_at column.

Installation

Add the following line to your Gemfile:

gem 'activerecord-cached_at', require: 'cached_at'

If you just need the ActiveRecord::Base#cache_key and associated helpers and aren't updating the models you can just require the helpers:

gem 'activerecord-cached_at', require: 'cached_at/helpers'

Configuration

By default updates to the cached_at, updated_at, and created_at columns will not trigger and update to the cached_at column. You can add aditional fields to ignore:

class User
  cached_at ignore: :my_column
end

class Photo
  cached_at ignore: :column_a, :column_b
end

Relationship Cache Keys

CachedAt also allows you to keep cache keys for relationships. This allows you to use the record to determine if a cache is valid for a relationship instead of doing another database query.

For example:

class User < ActiveRecord::Base
  has_many :photos
end

class Photo
  belongs_to :user, cached_at: true
end

bob_ross = User.create(name: 'Bob Ross')
# => INSERT INTO "users"
#    ("name", "cached_at", "updated_at_", "created_at")
#    VALUES
#    ("Bob Ross", "2020-07-19 20:22:03", "2020-07-19 20:22:03", "2020-07-19 20:22:03")

photo = Photo.create(user: bob_ross, file: ...)
# =>INSERT INTO "photos" ("user_id", "cached_at", "updated_at_", "created_at") VALUES (1, "Bob Ross", "2020-07-19 20:22:04", "2020-07-19 20:22:04", "2020-07-19 20:22:04")
# => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:04" WHERE "users"."id" = 1

photo.update(file: ...)
# =>UPDATE "photos" (..., "cached_at", "updated_at_") VALUES (..., "2020-07-19 20:22:05", "2020-07-19 20:22:05", "2020-07-19 20:22:05")
# => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:05" WHERE "users"."id" = 1

photo.update(user: not_bob_ross)
# =>UPDATE "photos" ("user_id", "cached_at", "updated_at_") VALUES (2, "2020-07-19 20:22:06", "2020-07-19 20:22:06", "2020-07-19 20:22:06")
# => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:06" WHERE "users"."id" IN (1, 2)

photo.destroy
# => UPDATE "users" SET "photos_cached_at" = "2020-07-19 20:22:07" WHERE "users"."id" = 2
# => DELETE FROM "users" WHERE WHERE "users"."id" = 2

Usage

cached_at will automatically be used for determining the cache key in Rails.

However if you need to calculate the cache key based on relationship cache keys you will need to manually compute the cache key. Examples are below:

The cache key here is the maxium of the following keys: cached_at, listings_cached_at, and photos_cached_at

<%= render partial: 'row', collection: @properties, as: :property, cached: Proc.new { |item|
  [item.cache_key_with_version(:listings, :photos), current_account.id ]
} %>

<% cache @property.cache_key_with_version(:listings, :photos) do %>
  <b>All the info on this property</b>
  <%= @property.name %>
  <% @property.listings.each do |listing| %>
    <%= listing.info %>
  <% end %>
  <% @property.photos.each do |photo| %>
    <%= image_tag(photo.url) %>
  <% end %>
<% end %>

TODO:

  • Document going more than one level with cached_at keys

  • Add a cache_key method to the Model class that gets MAX(cached_at)

  • change option to cache: true

  • add cache_association helper