"one, other or both" 个案例的更好解决方案

Better solution for "one, other or both" cases

我检查了一些代码,发现了类似下面的内容:

def between_dates(date_1, date_2)
  if date_1 && date_2
    conditions "created_at >= date_1 AND created_at <= date_2"
  elseif date_1
    conditions "created_at >= date_1"
  elseif date_2
    conditions "created_at <= date_2"
  end
end

看起来是那种可以改进的代码,但是我找不到更优雅的解决方案来处理这样一个琐碎而常见的条件语句。

当我们必须 return 一个、另一个或两者的值时,我正在为这个问题寻找更好的答案。

def between_dates(date_1, date_2)
  date_conditions = []
  date_conditions << 'created_at >= date_1' if date_1
  date_conditions << 'created_at <= date_2' if date_2
  conditions date_conditions.join(' AND ') unless date_conditions.empty?
end

我会使用这样的东西:

def between_dates(date_1, date_2)
  parts = []

  if date_1
    parts << "created_at >= date_1"
  end

  if date_2
    parts << "created_at <= date_2"
  end

  full = parts.join(' AND ')
  conditions(full)
end

这可以通过多种方式进一步美化,但您明白了。

我不确定这是否更优雅,但我总是reduce尽一切努力避免拼写错误:

[[date_1, '>='], [date_2, '<=']].
  select(&:first).
  map { |date, sign| "created_at #{sign} #{date}" }.
  join(' AND ')

Rails 允许您动态构建查询。这是一个使用 scopes 和 class 方法的示例。由于范围总是 return 一个 ActiveRecord::Relation 对象(即使块 returns nil),它们是可链接的:

class Event < ApplicationRecord
  scope :created_before, -> (date) { where('created_at <= ?', date) if date }
  scope :created_after,  -> (date) { where('created_at >= ?', date) if date }

  def self.created_between(date_1, date_2)
    created_after(date_1).created_before(date_2)
  end
end

用法示例:

Event.created_between(nil, Date.today)
# SELECT `events`.* FROM `events` WHERE (created_at <= '2018-05-15')

Event.created_between(Date.yesterday, nil)
# SELECT `events`.* FROM `events` WHERE (created_at >= '2018-05-14')

Event.created_between(Date.yesterday, Date.today)
# SELECT `events`.* FROM `events` WHERE (created_at >= '2018-05-14') AND (created_at <= '2018-05-15')