2013-09-25 116 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