基於計算值對 Rails 資源進行排序
設定
我最近去了一個移動支付黑客馬拉鬆,並與我Flock的co-founder一起工作了reminder app。 您輸入專案的名稱和到期日期。 然後,該應用程式會顯示您需要多長時間才能完成基於Clear啟發的綠色到紅色光譜的給定任務。原文
作為一個黑客馬拉鬆專案,我們希望保持應用程式簡單。 它包含一個具有2個屬性的Reminder模型:1)名稱和2)您想要完成任務的月份中的某一天。
問題
我到了一個點,我想按專案到期前剩餘的天數對專案進行排序。
如果我在資料庫中有一個名為'days_until_due'的列,我可以使用訂單方法:
Reminder.order('days_until_due ASC' )
複製程式碼
但我沒有一個名為'days_until_due'的資料庫專欄......
這讓我問自己:“基於計算值而不是資料庫中儲存的值對模型進行排序的最佳方法是什麼?"
解決方案
谷歌搜尋後我偶然發現了一個很好的解決方案。 它建議建立一個例項方法來計算值,然後建立一個將查詢與sort_by方法相結合的類方法。
Step 1:建立例項方法來計算值
在reminder.rb中,我建立了一個例項方法,根據專案被支援完成的當天和當前日期,找出專案到期前的天數:
def days_until_due
today = Time.now
simple_today = Time.new(today.year,today.month,today.day)
if day >= today.day
month_due = today.month
else
month_due = today.month + 1
end
day_due = Time.new(today.year,month_due,day)
return ((day_due - simple_today)/(60*60*24)).to_i
end
複製程式碼
Step 2:建立一個類方法來查詢和排序模型
再次在reminder.rb中,我建立了一個類方法,它結合了一個查詢,sort_by和上面的例項方法。
def self.sorted_by_days_until_due
Reminder.all.sort_by(&:days_until_due)
end
複製程式碼
您可能會問自己這一行方法中發生了什麼? 讓我們一步一步地分解它。 首先,Reminder.all返回所有提醒的陣列(未排序)。
Reminder.all.class # => Array
複製程式碼
接下來,我在這個陣列上呼叫sort_by方法。 此方法將塊作為引數,並通過將值對映到給定塊來生成排序陣列。 如果沒有給出塊,則返回列舉器。 以下是一個例子:
array = ["Michael","Adam","Jen"]
array.sort_by{|word| word.length} # => ["Jen","Adam","Michael"]
array.sort_by # => #<Enumerator: ["Michael","Jen"]:sort_by>
複製程式碼
現在你可能會問自己,但'&:days_until_due'如何轉化成塊? 它與procs和blocks有關。 如果你不熟悉這些術語,你應該看看我寫的討論ruby的這些部分的post。 在一個句子中,'&:'語法將例項方法轉換為proc,然後將proc轉換為塊,這是Array#sort_by作為引數。
下面我將使上面的示例看起來像我在提醒應用程式中使用的方法。
array = ["Michael","Jen"]
# Passing in a block directly
array.sort_by{|word| word.length} # => ["Jen","Michael"]
# Creating a proc and converting the block to a proc using the '&' syntax
proc = :length.to_proc # => #<Proc:0x007f98a225f700>
array.sort_by(&proc) # => ["Jen","Michael"]
# Creating a proc and converting the block to a proc in one step
array.sort_by(&:length) # => ["Jen","Michael"]
複製程式碼
上面的語法通過將隱式型別轉換與'&'運運算元組合起來。 '&'運運算元用於引數列表,以將Proc例項轉換為塊。 如果將運運算元與Proc例項之外的其他內容組合在一起,則隱式型別轉換將嘗試使用to_proc方法將其轉換為Proc例項。 由於Symbol#to_proc存在,當我們在'&'運運算元之後傳遞符號時,它會轉換為proc,然後轉換為塊。
回到我原來的問題,所有這些關於procs和塊和型別轉換的討論都讓我建立了以下一行方法:
def self.sorted_by_days_until_due
Reminder.all.sort_by(&:days_until_due)
end
複製程式碼
這個方法允許我在我的檢視中簡潔地包含一個排序的提醒陣列,其中包括:
@reminders = Reminder.sorted_by_days_until_due
複製程式碼
現在回到編碼!