4

次のように私はモデルを持っている:私がやりたい実行時にActiveRecordアソシエーションから返されたオブジェクトをどのように拡張できますか?

class Property < ActiveRecord::Base 
    has_and_belongs_to_many :property_values 
end 

何がPropertyオブジェクトの属性によって決定されたモジュールとproperty_values拡張子に検索で返される任意の値を拡張することです。私はこのような何かを試みました:

class Property < ActiveRecord::Base 
    has_and_belongs_to_many :property_values, :extend => PropertyUtil::Extensible 

    def enrich(to_extend) 
    modules.split(/\s*,\s*/).each do |mod| 
     to_extend.extend(Properties.const_get(mod.to_sym)) 
    end 
    end 
end 

module PropertyUtil 
    module Extensible 
    def self.extended(mod) 
     mod.module_eval do 
     alias old_find find 
     end 
    end 

    def find(*args) 
     old_find(*args).map{|prop| proxy_owner.enrich(prop)} 
    end 
    end 
end 

ここで、選択できるすべてのモジュールは、[プロパティ]モジュールで定義されています。しかし、このコードで実行しようとすると、いくつかの問題があります。まず、私の驚いたことに、ダイナミックファインダ(property_values.find_by_nameなど)のどれもfindに代理するように見えません。次に、エイリアシングを行った方法で、findを直接実行しようとすると、スタックのオーバーフローが発生します。

私は何をしようとしていますか?どのような方法でエイリアスとオーバーライドできますか?すべての結果は、関連付け拡張子によって返されますが、それらの取得方法に関係なく、適切なモジュールで拡張されますか? (私は別名が行われる方法を変更)

おかげで、クリス

+0

ありafter_findだとあなたが定義することができafter_initializeコールバックが、それは大きなパフォーマンスの低下を招くことになりますので、それは本当にお勧めできません。 – jemminger

答えて

0

この問題を解決するために、値クラスのafter_find呼び出しを使用しました。これはモジュール情報がプロパティ参照と値との間で複製される必要があることを意味するので、かなり最適ではない解決策ですが、正確に実行可能でない場合は実行可能です。パフォーマンスが低下した結果、大量のデータをデータベースにキャッシュしなければならなかったのですが、多数のプロパティの計算結果が表示されましたが、これはすべてが悪くないことが判明しました。かなりのデータを報告する。

は最後に、ここで私がなってしまったものをいくつかのビットです:

module Properties::NamedModules 
    def modules 
    (module_names || '').split(/\s*,\s*/).map do |mod_name| 
     Property.const_get(mod_name.demodulize.to_sym) 
    end 
    end 
end 

module Properties::ModularProperty 
    def value_structure 
    modules.inject([]){|m, mod| m + mod.value_structure}.uniq 
    end 
end 

module Properties::Polymorphic 
    include NamedModules, ModularProperty 

    def morph 
    modules.each {|mod| self.extend(mod) unless self.kind_of?(mod)} 
    end 
end 

class Property < ActiveRecord::Base 
    include Properties::NamedModules, Properties::ModularProperty 

    has_and_belongs_to_many :property_values, :join_table => 'property_value_selection' 

    def create_value(name, value_data = {}) 
    property_values.create( 
     :name => name, 
     :module_names => module_names, 
     :value_str => JSON.generate(value_data) 
    ) 
    end 
end 

class PropertyValue < ActiveRecord::Base 
    include Properties::Polymorphic 
    has_and_belongs_to_many :properties, :join_table => 'property_value_selection' 
    after_find :morph 
end 
0

は、私はこれを行うには試したことがないが、あなたは次のことを試してみたいことがあります。

class Property < ActiveRecord::Base 
    has_and_belongs_to_many :property_values, :extend => PropertyUtil::Extensible 

    def enrich(to_extend) 
    modules.split(/\s*,\s*/).each do |mod| 
     to_extend.extend(Properties.const_get(mod.to_sym)) 
    end 
    end 
end 

module PropertyUtil 
    module Extensible 
    def self.extended(mod) 
     mod.module_eval do 
     alias_method :old_find, :find 
     alias_method :find, :new_find 
     end 
    end 

    def new_find(*args) 
     old_find(*args).map{|prop| proxy_owner.enrich(prop)} 
    end 
    end 
end 

それはここでは動作しない場合であります(あなたはグループの方法あなたの拡張可能な場合)

class Value < ActiveRecord::Base 
    self.abstract_class = true 

end 


class ExtendedValue < Value 

end 

class ExtendedValue2 < Value 

end 


class Property < ActiveRecord::Base 
    has_and_belongs_to_many :property_values, :class_name => 'ExtendedValue' 
    has_and_belongs_to_many :property_values_extended, :class_name => 'ExtendedValue' 
    has_and_belongs_to_many :property_values_extended2, :class_name => 'ExtendedValue2' 


end 

アイデアは、「タイプ」ごとに1つのhatbm関連を持つことであり、あなたができれば、あなたが特定の時間にしたいいずれかを使用します。あなたがしたいことがあり、別のアイデアが試します行うそのようにしたいのですが、私は、activerecordがそれらを返した後で、返されたすべてのオブジェクトにパッチを適用するよりも、影響のパフォーマンスが小さくなることを確信しています。

私はあなたがこれを達成しようとしているもので、ちょっと興味があります:)

+0

お返事ありがとうございます!目的は、新しいタイプに対応するためにデータベースを変更することなく、プロパティ値の振る舞いとデータセマンティクスを提供するために、いくつかの異なるモジュールが構成できる単一テーブル継承のより柔軟なソートを提供することです。私の実装では、JSONドキュメントを格納する単一のテキストプロパティvalue_strがあります。様々なモジュールは、構成されると、文書の構造とそのデータを処理する方法の両方を定義する。ユーザーは、使用可能なモジュールを任意の組み合わせで組み合わせて対応させることができます。 –

+0

あなたはcompos_ofを見ましたか?また、問題の解決策の一部になる場合もあります。 – Schmurfy

+0

compos_ofは近いですが、クラス定義の一部であり、レコードがデータベースから取得された後にモジュール名を解決する機会を与えるようには見えません。 –

0

単に機能を変更するクラスを使用する方がはるかに簡単です。 PropertyValuesのクラスを適切な動作で使用し、STI(Single Table Inheritance)を使用して適切なインスタンスをインスタンス化するか、インスタンス化ActiveRecordクラスメソッドをオーバーライドして#becomesインスタンスメソッドを使用してクラスを設定できます。

class PropertyValue < AR:Base 
    def self.instantiate(record) 
    property_value = super 
    case property_value.sub # criteria for sub_class 
     when 'type1' then property_value.becomes(Type1) 
     when 'type2' then property_value.becomes(Type2) 
    end 
    end 
end 

class Type1 < PropertyValue 
    def some_method 
    # do Type1 behavior 
    end 
end 

class Type2 < PropertyValue 
    def some_method 
    # do Type2 behavior 
    end 
end 

クラスと継承を使用すると、はるかにクリーンでシンプルなコードが提供され、テストが簡単になることが分かりました。

+0

残念ながら、これは私のユースケースを満足させるものではありません。私は15のモジュールを持っていますが、プロパティ値の振る舞いとデータ構造を指定するために任意の数を構成できます。これらの組み合わせのデカルト積のクラスを構築することは賢明ではありません。 –

関連する問題