Ruby
Rails
RubyOnRails

ActiveRecord では STI をどう実装しているかを調べたメモ

More than 1 year has passed since last update.

コード読むときのメモです。すみません


仕組み


モデルのインスタンスを new するところ

メソッド: ActiveRecord::Inheritance#new

ActiveRecord::Inheritance モジュールにて、レコードに対応するクラス名を取得していて subclass があれば subclass として new を、それ以外だったら普通に ActiveRecord::Base の new をしている


lib/active_record/inheritance.rb#L64-L69

        if subclass && subclass != self

subclass.new(*args, &block)
else
super
end


クラス名の決定

subclass_from_attributes メソッド内で、レコードの inheritance_column で指定される attributes の値を find_sti_class に渡している

メソッド: ActiveRecord::Inheritance#subclass_from_attributes


lib/active_record/inheritance.rb#L221-L225

            subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym]

if subclass_name.present?
find_sti_class(subclass_name)
end


inheritance_column は ModelSchema で定義していて (ActiveRecord::Base が include している)、 デフォルトでは "type" である

find_sti_class メソッドでは、所謂 type を使って constantize までする。

メソッド: ActiveRecord::Inheritance


lib/active_record/inheritance.rb#L188-L194

        def find_sti_class(type_name)

type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
subclass = begin
if store_full_sti_class
ActiveSupport::Dependencies.constantize(type_name)
else
compute_type(type_name)

type_name を type_for_attribute で構築し直しているが、type_for_attribute が string じゃないことも一応あるのだろうか...


改造したいときは


カラムを変える

inheritance_column メソッドをオーバーライドすればよいはず


マッピングルールを変える

たとえば一般的なクラス名を string で入れておく使い方じゃなく、何か別の文字列を入れてクラス名にマッピングさせたいときは、 find_sti_class をオーバーライドすることで実現できるらしいです

https://qiita.com/suusan2go/items/6da23826523744a74ba3