Skip to content

Instantly share code, notes, and snippets.

@rkh
Created March 20, 2026 11:17
Show Gist options
  • Select an option

  • Save rkh/e6602f1736ce6c5af0d9fe24f8d8a51a to your computer and use it in GitHub Desktop.

Select an option

Save rkh/e6602f1736ce6c5af0d9fe24f8d8a51a to your computer and use it in GitHub Desktop.
require "active_record"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema[8.1].define(version: 1) do
create_table :sti_classes, id: :integer do |t|
t.string :name, null: false
end
create_table :examples do |t|
t.integer :type
t.timestamps
end
end
class StiType < ActiveRecord::Type::Value
def cast(value)
case value
when Class then value.name
when String, nil then value
when Integer then StiClass.name_for(value)
else
raise ArgumentError, "Invalid value for StiType: #{value.inspect}"
end
end
alias deserialize cast
def serialize(value) = StiClass.id_for(value)
end
class StiClass < ActiveRecord::Base
def self.id_for(name)
return unless name &&= name.to_s
@id_for ||= unscoped.pluck(:name, :id).to_h
@id_for[name] ||= find_or_create_by!(name: name).id
rescue ActiveRecord::RecordNotUnique
retry
end
def self.name_for(id)
@name_for ||= unscoped.pluck(:id, :name).to_h
@name_for[id] ||= find(id).name
rescue ActiveRecord::RecordNotFound
raise ArgumentError, "Invalid id for StiClass: #{id.inspect}"
end
end
class Example < ActiveRecord::Base
attribute :type, StiType.new
end
class SubExample < Example
end
load Gem.bin_path("irb", "irb")
@rkh
Copy link
Copy Markdown
Author

rkh commented Mar 20, 2026

I do realize that this technically isn't single-table inheritance anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment