Created
March 20, 2026 11:17
-
-
Save rkh/e6602f1736ce6c5af0d9fe24f8d8a51a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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") |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I do realize that this technically isn't single-table inheritance anymore.