ruby
54 lines · 8 steps
How polymorphic comments work in Rails
A reusable Comment model attaches to any model through a polymorphic association and a shared concern.
Explained by
highlit
1class Comment < ApplicationRecord
2 belongs_to :commentable, polymorphic: true, counter_cache: true
3 belongs_to :author, class_name: "User"
4
5 validates :body, presence: true, length: { maximum: 10_000 }
6
7 scope :recent, -> { order(created_at: :desc) }
8 scope :approved, -> { where(approved: true) }
9
10 after_create_commit :notify_commentable_owner
11
12 private
13
14 def notify_commentable_owner
15 owner = commentable.try(:owner)
16 return if owner.nil? || owner == author
17
18 CommentMailer.with(comment: self).new_comment.deliver_later
19 end
20end
21
22module Commentable
23 extend ActiveSupport::Concern
24
25 included do
26 has_many :comments, as: :commentable, dependent: :destroy
27 end
28
29 def add_comment(body:, author:)
30 comments.create(body: body, author: author)
31 end
32end
33
34class Post < ApplicationRecord
35 include Commentable
36end
37
38class Photo < ApplicationRecord
39 include Commentable
40end
41
42class CreateComments < ActiveRecord::Migration[7.1]
43 def change
44 create_table :comments do |t|
45 t.text :body, null: false
46 t.boolean :approved, null: false, default: false
47 t.references :author, null: false, foreign_key: { to_table: :users }
48 t.references :commentable, polymorphic: true, null: false
49 t.timestamps
50 end
51
52 add_index :comments, [:commentable_type, :commentable_id, :created_at]
53 end
54end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A polymorphic association lets one model belong to many different parent types through a type and id pair.
- 2Concerns package the parent-side wiring so any model becomes commentable with a single include.
- 3A composite index matching your query columns keeps polymorphic lookups and ordering fast.
Related explainers
ruby
require "csv" class SalesReport def initialize(path)
Aggregating CSV sales data in Ruby
data-aggregation
memoization
group_by
Intermediate
6 steps
ruby
module DurationFormatter UNITS = [ ['week', 604_800], ['day', 86_400],
Turning seconds into human-readable durations in Ruby
greedy-decomposition
modular-arithmetic
formatting
Intermediate
7 steps
ruby
class Comment < ApplicationRecord belongs_to :post belongs_to :author, class_name: "User"
Live-updating comments with Turbo in Rails
turbo-streams
callbacks
associations
Intermediate
8 steps
ruby
require 'json' require 'set' class SensitiveScrubber
Recursively scrubbing secrets from JSON
recursion
data-masking
pattern-matching
Intermediate
7 steps
ruby
class ReportBatcher BATCH_SIZE = 500 def initialize(account)
Batching monthly email summaries in Rails
batching
service-object
background-jobs
Intermediate
7 steps
ruby
class SessionsController < ApplicationController MAX_ATTEMPTS = 5 THROTTLE_WINDOW = 15.minutes
Throttling failed logins in Rails
rate-limiting
authentication
caching
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-polymorphic-comments-work-in-rails-explained-ruby-d3a4/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.