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



这两个点是什么意思.. 在轨道中

有一个这样的函数:

def period
start_date..end_date
end

这是什么?这会生成一个数组吗? 我看到这门课用了这个

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

这是 ruby 中包含有限Range的文字语法。

范围表示一个间隔 - 一组具有开始和结束的值...用作迭代器时,范围返回序列中的每个值。

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

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

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

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

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

作为旁注:(TL;DR)

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

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

def validate_other_booking_overlap
overlaps_bookings = Booking
.where(start_date: period)
.or(Booking.where(end_date: period))
.or(
Booking.where(
Booking.arel_table[:start_date].gt(start_date)
.and(Booking.arel_table[:end_date].lt(end_date))))
.where.not(id: id)
.exists?
errors.add(:overlaps_with_other) if overlaps_bookings
end

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

WHERE
((bookings.start_date BETWEEN '####-##-##' AND '####-##-##' 
OR bookings.end_date BETWEEN '####-##-##' AND '####-##-##')
OR (
bookings.start_date > '####-##-##' 
AND bookings.end_date < '####-##-##'
))
AND bookings.id != # -- IS NOT NULL on create

在较新版本的rails(>=6.0.3和ruby>= 2.7)中,这是:

Booking.arel_table[:start_date].gt(start_date)
.and(Booking.arel_table[:end_date].lt(end_date))

可以用这个代替

Booking.where(start_date: start_date..., end_date:...end_date))

因为现在 Rails (Arel) 将范围处理如下:

x..yx 和 y
  • 之间的属性(例如属性>= x 和属性 <= y )
  • x...y属性>= x 和属性
  • x..属性>= x
  • x...属性> x
  • ..x属性 <= x
  • ...x属性

。现在我相信我们已经走了一个完整的圈子。

更新

由于@max的评论,您可以使用OVERLAPS函数(数据库支持它),如下所示

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

最新更新