我有一个非常复杂的Rails(v 3.2)模型,它在多个表上进行手动选择。这非常适合获取显示模型所需的所有数据。但是,当我的数据不存在而且我必须创建一个虚拟对象时,我的模型上不存在这些列。对于我的生活,我无法想出一种方法来支持模型上的虚拟和实际列。
这比这复杂得多,但这是我目前的一般选择:
class MyObject < ActiveRecord::Base
attr_accessible :apples, :bananas, :oranges
def self.get(id)
select("my_objects.*, table1.apples, table2.bananas, table3.oranges")
.joins("left outer join table1 on something
left outer join table2 on something
left outer join table2 on something")
.where(:my_object => id)
end
end
这适用于id
存在时我需要显示的内容。但是,在某些情况下id
不存在,我必须显示虚拟(或默认)对象。
我以为我可以简单地为这个虚拟对象做这样的事情:
@my_object = MyObject.new({:apples => 1,
:bananas => 50,
:oranges => 10})
但是当然,在我@my_object.apples
的视图中,我收到错误,因为MyObject实际上没有这些列:
ActionView::Template::Error (unknown attribute: apples)
我采取的下一步是将attr_accessor
添加到MyObject
模型中:
attr_accessor :apples, :bananas, :oranges
适用于virtual
MyObject
版本的attr_accessor
。但现在,当试图显示对象的真实版本时,我的所有苹果,香蕉和橙子都是零!我假设这是因为method_missing
getter和setter覆盖了select返回的内容。
如何在此模型上同时支持虚拟和实际属性?
P.S。我尝试了多种使用define_method
和{{1}}的方式,但无法获得任何成功的方法。
答案 0 :(得分:1)
有趣的问题。
一种方法是为虚拟属性定义一个setter方法,如此
# MyObject.rb
def set_virtual_attribs=(hsh)
hsh.map{|key,value| self[key] = value}
end
然后你可以像这样创建一个虚拟对象
@my_object = MyObject.new(:set_virtual_attribs => {
:apples => 1,
:bananas => 50,
:oranges => 10
})
虚拟属性现在应该以{{1}}的形式提供,除非调用@my_object.apples
,否则不会覆盖实际对象的任何属性方法。
答案 1 :(得分:1)
我建议调查Null Object Pattern;我认为这些方面的想法对你的情况有帮助。
一般的想法是,不是在任何地方进行零检查,而是创建一个可以代替具有一些合理默认值的原始对象的对象。
我假设class MyObject
定义之外的某些内容决定何时id
不存在,因此您需要进行@my_object = MyObject.new({:apples => 1,...
调用。您可以使用不同的类,而不是在那里创建MyObject
的实例:
class MyNilObject
def apples
1
end
def bananas
50
end
def oranges
10
end
end
然后执行@my_object = MyNilObject.new
。如果要求您能够动态设置这些默认值,则可以使用def initialize
来获取哈希并分配属性并具有attr_readers。
这个类将是一个普通的旧Ruby对象,它没有任何数据库持久性。它需要实现您的视图调用MyObject
。
我不确定在渲染对象后你在这种情况下做了什么,所以你可能需要在这个对象上实现像create
这样的方法,将它转换为MyObject
的实例,可能会将其保存在数据库中。
答案 2 :(得分:0)
您可以覆盖模型中的访问者方法:
class MyObject < ActiveRecord::Base
def apples
read_attribute(:apples) || 1
end
# etc ....
end