1

は、だから私は最近シングルテーブル継承に探してきたが、この一般的な質問/答えを発見した:Rails - 単一テーブル継承 - 欠陥のあるキャストアプローチ?

質問:あなたは、アルファベータ<を想定し、 ベータ版へのAlphaからオブジェクトobjのクラスを変更するにはどうすればよいです、STIで?

答え:ルビーはアヒル型言語なので、キャストは使用しません。しかし、あなたがする必要があるのは、「タイプ」変数に「ベータ版」に設定し、オブジェクトを保存し、次回は、あなたはそれが型ベータ版のものであろうアルファオブジェクトにロードされます。ただし、この

obj = Alpha.new 
obj.save #now obj is of type Alpha 

obj.type = "Beta" 
obj.save #now obj is of type Beta 

をアプローチは私のために働いていないようです。 objは正しく保存されますが、Betaオブジェクトとしてはまったく機能していないようです。ベータ検証を実行せずに保存し、obj.respond_to?(:beta_method) #beta_method being a method in the beta classをチェックするとfalseを返します。このアプローチは機能しませんか?正しいアプローチはありますか?あるいは私は単に何か間違っているのですか? (:beta_method)(:beta_method)(真Alpha.lastとベータ版の両方が返さBeta.last.respond_toは、一方でそれは、falseが返さ

EDIT

私はAlpha.last.respond_toをしたときことがわかりました。最後に同じオブジェクトが返されました)。興味深い開発ですか?それでも誰かがこれを(ルビが継承をどのように処理するかに関して)詳細に説明できればそれはすばらしいでしょう。

答えて

1

あなたが言ったように、Rubyは型キャスティングを許可していません。むしろ、ここで起こっているのは、ActiveRecordがtypeフィールドの値に応じて異なるクラスのインスタンスをインスタンス化しているということです。だから、あなたがそれを「ベータ」に変えているとき、あなたは本当にルビーの観点からは何もしていません。

しかし、ActiveRecordはレコードを取得するためにデータベースを検索すると、新しいtype値を確認し、AlphaではなくBetaのインスタンスをインスタンス化します。このプロセスは、特にActiveRecordを使用するよりもRubyとあまり関係がありません。

場合によっては、より洗練されたアプローチがあります。モデルをあるタイプから別のタイプに「変換」する必要がある場合(たとえば、EmployeeManagerになります)、これは合理的な方法です。他のケースでは、mixinやその他のツールを使用することで、あなたの目標をより良く達成できるかもしれません。

あなたはこのような、する必要がある場合もAlphaの属性からBetaの一時的なインスタンスを作成することができます。

# obj is an instance of Alpha 
obj = Beta.new(obj.attributes) 

しかし、これは同様に、ほとんどの場合に行う必要があるために奇妙なことです。

+0

私はこの答えがかなり正しいとは思わない。 OPでは、 'obj.type =" Beta "; obj.save'はDBを更新しませんが、メモリ内のバージョンはアルファとして型キャストされます。 'obj.reload'を実行すると、RailsはそれがAlphaであることを期待しているので、この場合は動作しません。 obj2 = Beta.find(obj.id) ' –

+0

...でも、@Teoulasが以下で説明するように' obj.becomes(Beta) 'を使用することをお勧めします。 –

+0

私はデータベースが更新されていないとは決して言いませんでした。私が言ったことは、型を "ベータ"に変更することで、Rubyは気にしません(まだAlphaのインスタンスを持っています)。次の段落で述べたように、ActiveRecordが新しいレコードをデータベースから取得すると、ベータインスタンスが作成されます。 (なぜ私は#私の元の記事ではなく、あなたが言うように、他の誰かがそれを持ってきたことを思い出しません。) –

4

はあなたが達成しようとしている内容に応じて、あなたはbecomesを使用するか、またはあなたがやった(または直接update_attribute経由)のようにオブジェクトの種類を変更し、データベース(または多分両方)から、それを再ロードするかのいずれかをお勧めします。

becomesは、前のオブジェクトと同じ属性を使用して、唯一のパラメータとして渡されたクラスの新しいオブジェクトをインスタンス化して返します。これにより、新しいオブジェクトのメソッドを呼び出すことができます。例:

a = Alpha.last 

# if you want to save the new type in the database: 
a.type = 'Beta' 
a.save 

b = a.becomes(Beta) 
b.respond_to?(:beta_method) 
=> true 

私は、あなたがメソッドを呼び出す順序が重要であることを発見しました。最初にbecomesを使用して、タイプを変更して保存しようとすると、データベースのタイプは変更されません。

関連する問題