5

我有一個包含多個日期屬性的模型。我希望能夠設置和獲取字符串的值。我過騎的方法(bill_date)像這樣一個:在Rails中缺少使用方法

def bill_date_human 
    date = self.bill_date || Date.today 
    date.strftime('%b %d, %Y') 
    end 
    def bill_date_human=(date_string) 
    self.bill_date = Date.strptime(date_string, '%b %d, %Y') 
    end 

這將執行非常適合我的需要,但我想這樣做同樣的事情,其他幾個屬性日期...我將如何乘虛而入方法缺失,以便任何日期屬性可以設置/獲取像這樣?

+1

'method_missing'是你應該採取的最後一根稻草。實際上,定義方法更清晰,導致更好的代碼設計和明確的問題分離,更容易理解,也更快捷。所以如果你可以定義你的方法,你應該總是這樣做。 – 2012-01-16 20:43:04

+0

從KL-7獲知有更好的方法比method_missing更好,但考慮到我有4個不同的日期屬性爲這個模型,手動定義每個不是解決方案。 DRY – tybro0103 2012-01-16 20:57:22

+0

那麼,KL-7的方法實際上是這裏的首選方法。因爲他提出了我的意思:定義方法。 – 2012-01-16 21:18:22

回答

10

正如您已經知道所需方法的簽名,最好定義它們而不是使用method_missing。你可以那樣做(你類定義):

[:bill_date, :registration_date, :some_other_date].each do |attr| 
    define_method("#{attr}_human") do 
    (send(attr) || Date.today).strftime('%b %d, %Y') 
    end 

    define_method("#{attr}_human=") do |date_string| 
    self.send "#{attr}=", Date.strptime(date_string, '%b %d, %Y') 
    end 
end 

如果列出所有日期的屬性是沒有問題的這種做法是更好,因爲你面對的常規方法,而不是內部method_missing一些魔術。

如果要適用於該有_date結尾的名稱的所有屬性,你可以檢索它們像(你的類定義):

column_names.grep(/_date$/) 

而這裏的method_missing溶液(沒有測試過,雖然以前一個也不會被測試):

def method_missing(method_name, *args, &block) 
    # delegate to superclass if you're not handling that method_name 
    return super unless /^(.*)_date(=?)/ =~ method_name 

    # after match we have attribute name in $1 captured group and '' or '=' in $2 
    if $2.blank? 
    (send($1) || Date.today).strftime('%b %d, %Y') 
    else 
    self.send "#{$1}=", Date.strptime(args[0], '%b %d, %Y') 
    end 
end 

此外,它是很好的覆蓋respond_to?方法和方法名返回true,你處理內部method_missing(在1.9中,你應該改寫respond_to_missing?)。

+0

oooo很好!我之前沒有遇到過define_method :)我仍然希望看到該方法缺少實現,但+1爲更好的解決方案 – tybro0103 2012-01-16 20:34:05

+1

添加了'method_missing'版本,但如果您可以定義這些方法,請不要使用它。 – 2012-01-16 20:50:18

+1

@ KL-7在您的'method_missing'中,如果不處理方法,您應該直接返回super。現在,下面的屬性處理總是執行。 – 2012-01-16 21:20:16

5

您可能會感興趣的ActiveModel的AttributeMethods模塊(活動記錄已經用於一堆東西),這幾乎(但不完全)你所需要的。

簡而言之,你應該能夠做到

class MyModel < ActiveRecord::Base 

    attribute_method_suffix '_human' 

    def attribute_human(attr_name) 
    date = self.send(attr_name) || Date.today 
    date.strftime('%b %d, %Y') 
    end 
end 

這樣做之後,my_instance.bill_date_human會叫attribute_human與attr_name設置爲「bill_date」。 ActiveModel將爲您處理諸如method_missing,respond_to之類的內容。唯一的缺點是這些人類方法將存在於所有列中。