ruby
47 lines · 8 steps
How STI models share behavior in Rails
A single Employee table backs four classes that inherit validations and override pay logic through single-table inheritance.
Explained by
highlit
1class Employee < ApplicationRecord
2 validates :name, presence: true
3 validates :salary, numericality: { greater_than: 0 }
4
5 scope :managers, -> { where(type: "Manager") }
6 scope :engineers, -> { where(type: "Engineer") }
7
8 def weekly_pay
9 (salary / 52.0).round(2)
10 end
11
12 def role
13 type&.underscore&.humanize || "Employee"
14 end
15end
16
17class Manager < Employee
18 has_many :reports, class_name: "Employee", foreign_key: :manager_id
19
20 def weekly_pay
21 super + bonus_share
22 end
23
24 def bonus_share
25 (annual_bonus.to_f / 52.0).round(2)
26 end
27end
28
29class Engineer < Employee
30 validates :primary_language, presence: true
31
32 def weekly_pay
33 super + on_call_stipend
34 end
35
36 def on_call_stipend
37 on_call? ? 200.0 : 0.0
38 end
39end
40
41class Contractor < Employee
42 validates :hourly_rate, numericality: { greater_than: 0 }
43
44 def weekly_pay
45 (hourly_rate * hours_per_week).round(2)
46 end
47end
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Single-table inheritance lets subclasses share one table while specializing behavior through Ruby's normal class hierarchy.
- 2Calling super in an overridden method composes subclass-specific additions on top of the base implementation instead of replacing it.
- 3Validations and scopes defined on the base model apply to every subclass, while subclasses can add their own constraints.
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 Comment < ApplicationRecord belongs_to :commentable, polymorphic: true, counter_cache: true belongs_to :author, class_name: "User"
How polymorphic comments work in Rails
polymorphic-association
concerns
counter-cache
Intermediate
8 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-sti-models-share-behavior-in-rails-explained-ruby-2d08/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.