2013-09-25 211 views
1

我有一个是做一个手动选择多个表一个非常复杂的Rails(V 3.2)模型。这对于获取显示我的模型所需的所有数据非常有用。但是,当我的数据不存在并且我必须创建一个虚拟对象时,这些列在我的模型中不存在。对于我来说,我无法想出一种方法来支持模型上的虚拟列和实际列。Rails的模型属性

这是一个很多比这更复杂,但是这是一般选择我目前有:

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_accessorMyObject型号:

attr_accessor :apples, :bananas, :oranges 

,对于virtual版本的MyObject完美的作品。但是现在,当试图展示真实版本的对象时,我所有的苹果,香蕉和橘子都是零!我认为这是因为attr_accessor获取者和设置者重写了选择返回的内容。

我怎么能支持这种模式的虚拟和实际属性?

p.s.我已经尝试了多种使用method_missing以及define_method的方法,但一直未能取得任何成功。

+0

这可能是因为这是你构建一个例子,但我很困惑,为什么你提到的两个'apple'和'apples' ,“香蕉”和“香蕉”,“杯子”和“桔子”......你希望能够在MyObject实例上调用的实际方法是什么? – carols10cents

+0

@ carolclarinet - 对不起,这是由于我错过了所有更改的编辑。现在已经修复了。核心问题是,有时属性存在,有时他们不会 – lightswitch05

+0

真棒,这清除了很多。谢谢! – carols10cents

回答

1

有趣的问题。

的一种方法是定义虚拟attribs 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 
         }) 

的虚拟属性现在应该可以作为@my_object.apples和除非调用set_virtual_attribs,否则这不会覆盖实际对象的任何属性方法。

+0

This Works!但是,它确实有两个我不太在乎的副作用。首先,我现在得到这个警告'拒绝警告:你正试图创建一个属性'苹果'。在模型上编写任意属性已被弃用。请使用'attr_writer'等。其次,我不得不改变我的视图层来访问像哈希这样的属性,而不是通过一种方法。在将其标记为解决方案之前,我将等待更长时间。 – lightswitch05

+1

他们应该可以通过'@ my_objects.apples'这样的方法访问,我试过了我的控制台。 – tihom

+0

我站好了,可以通过方法访问。关于绕过弃用警告的任何想法? – lightswitch05

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一个实例,并将其保存在数据库中,可能。

+0

我喜欢这个选项。苹果,香蕉和橙子的价值并不像你们的例子那样是静态的,但可以很容易地改变。不过,我觉得@tihom对问题有更好的答案,而不是重新考虑 – lightswitch05

0

您可以覆盖访问方法的模型:

class MyObject < ActiveRecord::Base 

    def apples 
    read_attribute(:apples) || 1 
    end 

    # etc .... 
end 
+0

我认为如果默认值没有变化,那么这个答案是最简单的,并且您可以使所有新旧对象都可访问此属性。 – tihom

+0

这很简单,但我必须能够更改虚拟列上的值。我看到'默认值'如何导致这个结论,我很抱歉。在这个视图中有大约10-25个不同的MyObjects,每个都有不同的苹果,香蕉和橘子值。 – lightswitch05