基于计算值对 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

复制代码

如今回到编码!

相关文章
相关标签/搜索