基于计算值对 Rails 资源进行排序

611 阅读3分钟

设置

我最近去了一个移动支付黑客马拉松,并与我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", "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

现在回到编码!