Railsでモデルデータのクラスをサブクラスに変更する

Single Table Inheritanceで親子関係にある二つのクラスHoge, Fugaがあるとする。

class Hoge < ActiveRecord::Base
end

class Fuga < Hoge
end

このときHogeクラスのオブジェクトaがあったとしてそれをFugaクラスに変換したいとする。 そもそもそういう状況がオブジェクト指向的にどうなのかというのは置いておく。

このとき、

a = Hoge.find(1)
b = a.becomes(Fuga)
b.save!

とすればいいのかと思って試してみるも、ロードしなおしたオブジェクトはHogeクラスのままだった。

オブジェクトを保存するときのクエリーを見ると次のようになっていた。

UPDATE `hoges` SET `type` = 'Fuga', `updated_at` = '2013-04-08 14:04:33' 
 WHERE `hoges`.`type` IN ('Fuga') AND `hoges`.`id` = 1

type In ('Fuga')の制約のために、更新したいレコードが引っかからなかったのだ。 親クラスから子クラスに変更するには、次のように元のオブジェクトのtypeカラムを直接変更するのが無難か。

a = Hoge.find(1)
a.update_attribute(:type, Fuga.name)

becomesというメソッドの存在を知って、これを使えばクラスの変換もナチュラルな感じに書けるのかと期待したけど、結局typeカラムを修正するという、STIの実装に依存した書き方になった。

ちなみに子クラスから親クラスへの変換はbecomesとsave!を使ったやり方でもうまくいく。