我的模型如下:
class Property < ActiveRecord::Base
has_and_belongs_to_many :property_values
end
我想要做的是使用由Property对象的属性确定的模块扩展property_values扩展上的find返回的任何值。我试过这样的事情:
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 :(得分: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返回后,它将比修补每个返回的对象具有更小的影响性能。
我对你想要达到的目标有点好奇:)
答案 1 :(得分:0)
简单地使用类来更改功能要容易得多。您可以使用适当的行为来使用PropertyValues类,并使用STI(单表继承)来实例化相应的实例,或者您可以使用#becomes实例方法覆盖'实例化'ActiveRecord类方法来设置类:
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
我发现使用类和继承提供了更清晰,更简单的代码,并且更容易测试。
答案 2 :(得分: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