Picture of the author

GAURAV VARMA

← BACK TO BLOG

Rails 6.1 adds ActiveRecord delegated types for polymorphism


Rails 6.1 introduces delegated_type, a cleaner and more expressive way to handle polymorphic relationships in ActiveRecord. It builds on traditional polymorphism by making it easier to work with multiple types and providing helpful querying and predicate methods.

The Problem with Classic Polymorphism

Using polymorphic belongs_to often leads to awkward queries and boilerplate. Let’s say we manage two types of vehicles: Car and Motorcycle. With polymorphic associations, you'd typically do this:

1class Vehicle < ApplicationRecord
2  belongs_to :vehicleable, polymorphic: true
3end
4
5class Car < ApplicationRecord
6  has_one :vehicle, as: :vehicleable
7end
8
9class Motorcycle < ApplicationRecord
10  has_one :vehicle, as: :vehicleable
11end

You can query attributes via vehicle.vehicleable, but checking the type or fetching records by type requires manual filtering and string comparisons.

Enter delegated_type

Rails 6.1 improves this pattern with delegated_type:

1class Vehicle < ApplicationRecord
2  delegated_type :vehicleable, types: %w[Car Motorcycle]
3end

That one line gives you:

  • vehicle.vehicleable (just like before)
  • Type-specific accessors: vehicle.car, vehicle.motorcycle
  • Type predicate helpers: vehicle.car?, vehicle.motorcycle?
  • Scope helpers: Vehicle.cars, Vehicle.motorcycles

Creating Records

With delegated_type, you can initialize and persist both the delegator and delegatee in one go. Note that vehicleable: expects an instance of the associated type:

1Vehicle.create!(
2  vehicleable: Car.new(interior_color: '#fff', adjustable_roof: true),
3  name: 'TS78Z',
4  mileage: 89
5)

This automatically saves both the Car and associated Vehicle record.

Querying and Type Helpers

You get handy scopes and methods out of the box:

1Vehicle.cars
2# => ActiveRecord::Relation of all Vehicles with vehicleable_type: "Car"
3
4Vehicle.motorcycles
5# => ActiveRecord::Relation of all Vehicles with vehicleable_type: "Motorcycle"
6
7vehicle = Vehicle.first
8vehicle.car?        # => true or false
9vehicle.car         # => Returns the Car object or nil

This removes the need for manual conditionals or type comparisons.

Summary

delegated_type is a modern, ergonomic alternative to polymorphic associations. It gives your models cleaner APIs, less boilerplate, and better type safety — all while keeping your data normalized.

To learn more, check out the pull request that introduced it.