Building a Unified Timeline with Rails Delegated Types and Turbo Broadcasting
بِسْمِ ٱللَّٰهِ ٱلرَّحْمَٰنِ ٱلرَّحِيمِ
A teacher's dashboard needs to show assignments, notes, and grades in a single timeline — but these are fundamentally different models. In this post, I'll walk through how we unified them using Rails' delegated_type, built a real-time broadcasting system with Turbo Streams, and kept it all under 210 lines of core code.
The Problem
QuranPortal has two types of content that appear on the teacher dashboard:
- Assignments — with due dates, grades, verse ranges, mistake counts, and assignment types
- Student Notes — with text content, dates, and visibility (public or internal)
Both need to appear in the same spreadsheet cell (organized by student and date), the same timeline view, and the same notification tray. We needed a single queryable model that could represent both without sacrificing type-specific behavior.
Why Not STI?
Single Table Inheritance would force both types into one table with many nullable columns. Assignments have 15+ columns (quality_rating, completed_at, mistake_count, hesitation_count, etc.) that make no sense for notes, and vice versa. The table would be sparse and confusing.
Delegated Types
Rails 6.1 introduced delegated_type — a pattern where a shared table stores common attributes and delegates type-specific behavior to separate tables via polymorphic association. Think of it as composition instead of inheritance.
The Entry Model: 16 Lines
# app/models/entry.rb
class Entry