基於計算值對 Rails 資源進行排序

設置

我最近去了一個移動支付黑客馬拉松,並與我Flock的co-founder一塊兒工做了reminder app。 您輸入項目的名稱和到期日期。 而後,該應用程序會顯示您須要多長時間才能完成基於Clear啓發的綠色到紅色光譜的給定任務。原文git

做爲一個黑客馬拉松項目,咱們但願保持應用程序簡單。 它包含一個具備2個屬性的Reminder模型:1)名稱和2)您想要完成任務的月份中的某一天。github

問題

我到了一個點,我想按項目到期前剩餘的天數對項目進行排序。數據庫

若是我在數據庫中有一個名爲'days_until_due'的列,我可使用訂單方法:數組

Reminder.order('days_until_due ASC')

複製代碼

但我沒有一個名爲'days_until_due'的數據庫專欄......ruby

這讓我問本身:「基於計算值而不是數據庫中存儲的值對模型進行排序的最佳方法是什麼?"app

解決方案

谷歌搜索後我偶然發現了一個很好的解決方案。 它建議建立一個實例方法來計算值,而後建立一個將查詢與sort_by方法相結合的類方法。ide

Step 1:建立實例方法來計算值

在reminder.rb中,我建立了一個實例方法,根據項目被支持完成的當天和當前日期,找出項目到期前的天數:post

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和上面的實例方法。ui

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", "Adam", "Jen"]:sort_by>

複製代碼

如今你可能會問本身,但'&:days_until_due'如何轉化成塊? 它與procs和blocks有關。 若是你不熟悉這些術語,你應該看看我寫的討論ruby的這些部分的post。 在一個句子中,'&:'語法將實例方法轉換爲proc,而後將proc轉換爲塊,這是Array#sort_by做爲參數。

下面我將使上面的示例看起來像我在提醒應用程序中使用的方法。

array = ["Michael", "Adam", "Jen"]

# Passing in a block directly
array.sort_by{|word| word.length} # => ["Jen", "Adam", "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", "Adam", "Michael"]

# Creating a proc and converting the block to a proc in one step
array.sort_by(&:length) # => ["Jen", "Adam", "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

複製代碼

如今回到編碼!

相關文章
相關標籤/搜索