biz_record is a Rails engine that stores biz
schedules in Active Record.
It persists weekly hours, date-specific shifts, breaks, and holidays, then
rebuilds a Biz::Schedule for business-time calculations.
Add this line to your application's Gemfile:
gem "biz_record"Then install the migration:
bin/rails generate biz_record:install
bin/rails db:migrateThe engine includes routes and controllers with simple Rails forms for editing schedules. They are a reference implementation: mount them in your app if you want a working base to adapt to your own UI and business rules.
mount BizRecord::Engine => "/biz_record"Schedules use Time.zone and default to Monday-Friday 09:00-17:00.
Change the default weekly hours with an initializer:
BizRecord.configure do |config|
config.default_hours = {
mon: { "08:00" => "12:00", "13:00" => "17:00" },
tue: { "08:00" => "12:00", "13:00" => "17:00" },
wed: { "08:00" => "12:00", "13:00" => "17:00" },
thu: { "08:00" => "12:00", "13:00" => "17:00" },
fri: { "08:00" => "12:00", "13:00" => "16:00" }
}
endWeekdays use biz keys: sun, mon, tue, wed, thu, fri, sat.
Include the BizRecord::HasSchedule module and declare one or more schedules on the model that owns them:
class Account < ApplicationRecord
include BizRecord::HasSchedule
has_schedule
has_schedule :support, time_zone: ->(account) { account.support_time_zone || account.time_zone }
has_schedule :dev
endThis defines regular has_one associations:
account.schedule
account.support_schedule
account.dev_schedule
account.create_schedule!
account.create_support_schedule!key identifies the schedule within its owner. The default key is "default";
named schedules use the name passed to has_schedule.
When the time_zone: option is not provided, schedules use Time.zone, then
the application's configured time zone (Rails.application.config.time_zone),
then BizRecord::DEFAULT_TIME_ZONE. The option accepts a method name or a
proc/lambda that receives the schedulable record. When a resolver is configured,
it must return a time zone.
Edit the persisted parts through associations:
schedule = account.support_schedule
schedule.intervals.create!(weekday: "mon", starts_at: "09:00", ends_at: "17:00")
shift = schedule.shift_days.create!(date: "2026-06-01")
shift.intervals.create!(starts_at: "10:00", ends_at: "14:00")
break_day = schedule.break_days.create!(date: "2026-06-01")
break_day.intervals.create!(starts_at: "12:00", ends_at: "13:00")
schedule.holiday_days.create!(date: "2026-12-25")
schedule.in_hours?(Time.current)BizRecord::Schedulebelongs to a polymorphicschedulable.BizRecord::Intervalstores weekly hours or day-specific intervals.BizRecord::Days::Shift,Break, andHolidaystore date exceptions.- The
configurationJSON column is a cache rebuilt from associations. - The generator uses
jsonbon PostgreSQL andjsonelsewhere.