12 November 2020
Transform small tables with ActiveHash in Ruby on Rails
In a recent Ruby on Rails project, we needed to work with days of the week. We considered making a small table so we could fetch it, sort it, and join it to other tables as needed. But knowing that the days of the week do not change, we ruled out making a table for this data. We went with ActiveHash. With an ActiveHash class we can handle data in a manner similar to working with ActiveRecord classes, and since ActiveHash classes reside in memory, we get faster access than what is possible with a table.
We created the following ActiveHash class and placed it in our model’s folder (app/models). We gave it an instance method #tomorrow, and optionally, you can include other ActiveRecord-like things such as ‘alias’ and ‘scope’, as well.
class WeekDay < ActiveHash::Base
include ActiveHash::Associations
has_many :operating_hours
self.data = [
{
id: 1,
day: 'Monday',
rails_day_index: 1
},
{
id: 2,
day: 'Tuesday',
rails_day_index: 2
},
{
id: 3,
day: 'Wednesday',
rails_day_index: 3
},
{
id: 4,
day: 'Thursday',
rails_day_index: 4
},
{
id: 5,
day: 'Friday',
rails_day_index: 5
},
{
id: 6,
day: 'Saturday',
rails_day_index: 6
},
{
id: 7,
day: 'Sunday',
rails_day_index: 0
}
]
def tomorrow
self.class.find((id % 7) + 1)
end
end
Using our WeekDay class we have access to many ActiveRecord-like methods including the following:
WeekDay.all # returns all objects
WeekDay.find_by(rails_day_index: 0) # returns the object for 'Sunday'
Once created, we can wire this to an ActiveRecord model with the following lines.
class OperatingHour < ApplicationRecord
extend ActiveHash::Associations::ActiveRecordExtensions
belongs_to :week_day
validates :week_day_id, presence: true
end
Using the ActiveHash::Associations allows access to the attributes of either the parent or child models, just as in ActiveRecord models.
In WeekDay we can access the related operating hours as follows:
week_day = WeekDay.find_by(rails_day_index: Date.today.wday)
hours_of_operation = week_day.operating_hours
Or from our OperatingHour we can access the related WeekDay as follows:
operating_hour = OperatingHour.first
day_of_week = operating_hour.week_day
We can create tests for ActiveHash classes similar to those we create for our ActiveRecord models. Here is an example:
require 'rails_helper'
RSpec.describe WeekDay, type: :model do
describe '#tomorrow' do
it 'returns Monday on Sunday' do
expect(WeekDay.find(7).tomorrow.day).to eq('Monday')
end
end
end
If later we decide to change our WeekDay class to be an ActiveRecord class with a database table, we can do so without having to rewrite the code we already have. Such flexibility and in-memory speed make ActiveHash a good choice for small data sets. One sacrifice is that it does not support all the features of ActiveRecord. This makes ActiveHash a good candidate for any project when the conditions are simple, as in this example.