4

我有一個模型,如下所示:如何在運行時擴展從ActiveRecord關聯返回的對象?

class Property < ActiveRecord::Base 
    has_and_belongs_to_many :property_values 
end 

我想要做的是延長一個找上了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等)可以委託查找;其次,當我嘗試直接運行查找時,我如何完成別名會導致堆棧溢出。

有沒有辦法做我想做的事情?我可以使用什麼方法進行別名和覆蓋,使得關聯擴展返回的結果無論其檢索方式如何,都可以使用適當的模塊進行擴展?

謝謝,克里斯

+0

有after_find並且可以定義after_initialize回調,但它不是真正的推薦,因爲它會導致一個很大的性能損失。 – jemminger 2011-03-19 03:13:35

回答

0

我結束了對value類的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 

的想法是讓每個「類型」一臺hatbm協會(如果你可以將你的擴展,方式),如果你可以使用你想在一個給定的時間之一,做你想要什麼樣的方式我也很肯定它會比在activerecord返回之後修補每個返回的對象的影響性能更小。

我在你正在努力實現這個:)

+0

感謝您的回覆!目標是提供一種更靈活的單表繼承,其中許多不同的模塊可組合以提供屬性值的行爲和數據語義,而無需修改數據庫以適應新類型。在我的實現中,有一個文本屬性value_str,它存儲JSON文檔;各個模塊在組成時定義文檔的結構以及如何處理其數據。用戶可以以任何所需的組合混合和匹配可用的模塊。 – 2011-03-24 03:56:30

+0

你有沒有看過composed_of?它也可能是解決您的問題的一部分。 – Schmurfy 2011-03-27 11:47:20

0

這是很容易簡單地使用類來改變功能是什麼樣的好奇。您可以使用適當行爲的PropertyValues類,並使用STI(單表繼承)來實例化相應的實例,或者您可以重寫'實例化'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 

我發現使用類和繼承提供了更簡潔,更簡單的代碼,並且更容易測試。

相關問題