Rails 中的两点运算符是什么?

Dér*_*tti 1 ruby ruby-on-rails

这两个点在 Rails 中意味着什么

有一个这样的函数:

def period
   start_date..end_date
end
Run Code Online (Sandbox Code Playgroud)

它是什么?这会生成一个数组吗?我看到这个课程中使用了这个

class Booking < ApplicationRecord
   # ... some code is skipped here for simplicity's sake
   validate :validate_other_booking_overlap

   def period
     start_date..end_date
   end

   private

   def validate_other_booking_overlap
     other_bookings = Booking.all
     is_overlapping = other_bookings.any? from |other_booking|
       period.overlaps?(other_booking.period)
     end
     errors.add(:overlaps_with_other) if is_overlapping
   end
end
Run Code Online (Sandbox Code Playgroud)

eng*_*nky 11

这是 ruby​​ 中包含有限的字面语法Range

\n
\n

Range 表示一个区间\xe2\x80\x94 一组具有开始和结束的值...当用作迭代器时,Range 返回序列中的每个值。

\n
\n

范围可以表示为包含两个点 ( ..) 或排除三个点 ( ...)。独占范围不包括最后一个元素。

\n

在 2.6 之前,所有范围都是有限的(语法上需要开始和结束;尽管结束可以是Float::INFINITY[与 -Float::INFINITY开始相反],所以从技术上讲,并非所有范围都是“有限的”)。

\n

在 2.6 中,“无限范围”是使用语法实现的x..

\n

从 2.7 开始,范围可以使用语法“无开头”..x

\n

根据文档,无限范围和无限范围都是“半无限”,可以表示为包含或排除。(说实话,我不确定排他的无限范围是什么;尽管语法具有创造性的用途[见下文])

\n

作为旁注:( TL;DR )

\n

Booking从性能的角度来看,循环遍历每个都是一个坏主意。

\n

更好的想法是使用以下方法将其卸载到数据库:

\n
def validate_other_booking_overlap\n  overlaps_bookings = Booking\n    .where(start_date: period)\n    .or(Booking.where(end_date: period))\n    .or(\n      Booking.where(\n        Booking.arel_table[:start_date].gt(start_date)\n          .and(Booking.arel_table[:end_date].lt(end_date))))\n    .where.not(id: id)\n    .exists?\n  errors.add(:overlaps_with_other) if overlaps_bookings\nend\n
Run Code Online (Sandbox Code Playgroud)\n

Rails (Arel) 将 WHERE 条件中的有限范围转换为 SQL BETWEEN 子句,因此此查询条件将是:

\n
WHERE\n((bookings.start_date BETWEEN \'####-##-##\' AND \'####-##-##\' \n  OR bookings.end_date BETWEEN \'####-##-##\' AND \'####-##-##\')\n  OR (\n    bookings.start_date > \'####-##-##\' \n    AND bookings.end_date < \'####-##-##\'\n  ))\n  AND bookings.id != # -- IS NOT NULL on create\n
Run Code Online (Sandbox Code Playgroud)\n

在较新版本的 Rails(>=6.0.3 和 ruby​​>=2.7)中:

\n
Booking.arel_table[:start_date].gt(start_date)\n  .and(Booking.arel_table[:end_date].lt(end_date))\n
Run Code Online (Sandbox Code Playgroud)\n

可以用这个代替

\n
Booking.where(start_date: start_date..., end_date:...end_date))\n
Run Code Online (Sandbox Code Playgroud)\n

因为现在Rails(Arel)对待范围如下:

\n
    \n
  • x..y属性介于 x 和 y 之间(例如属性 >= x AND 属性 <= y )
  • \n
  • x...y属性 >= x AND 属性 < y
  • \n
  • x..属性 >= x
  • \n
  • x...attribute >= x (这是 Rails 团队根据无限独占范围的 Ruby 实现/解释做出的决定)
  • \n
  • where.not(attribute: ..x)属性 > x
  • \n
  • ..x属性 <= x
  • \n
  • ...x属性 < x
  • \n
\n

...现在我相信我们已经回到原点了。

\n

更新

\n

由于@max\'s 的评论,您可以使用该OVERLAPS函数(在数据库支持的情况下),如下所示

\n
def validate_other_booking_overlap\n  left = Arel::Nodes::Grouping.new(\n    [Booking.arel_table[:start_date],\n    Booking.arel_table[:end_date]])\n  right = Arel::Nodes::Grouping.new(\n    [Arel::Nodes::UnaryOperation.new(\n      \'DATE\', \n      [Arel::Nodes.build_quoted(start_date - 1)]),\n    Arel::Nodes::UnaryOperation.new(\n      \'DATE\', \n      [Arel::Nodes.build_quoted(end_date + 1)])])\n  condition = Arel::Nodes::InfixOperation.new(\'OVERLAPS\', left, right)\n  errors.add(:overlaps_with_other) if Booking.where.not(id: id).where(condition).exists?\nend\n
Run Code Online (Sandbox Code Playgroud)\n