【问题标题】:Better solution for "one, other or both" cases“一个、另一个或两个”情况的更好解决方案
【发布时间】:2018-10-24 23:08:41
【问题描述】:

我正在检查一些代码,然后出现了类似于以下内容:

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

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

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

【问题讨论】:

  • 这是一个 Rails 问题吗?您是否尝试动态构建查询?
  • 你说得对,Stefan,这是一个动态查询。我会添加标签,谢谢。

标签: ruby-on-rails ruby conditional


【解决方案1】:

Rails 允许您动态构建查询。这是一个使用scopes 和类方法的示例。由于作用域总是返回一个ActiveRecord::Relation 对象(即使块返回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')

【讨论】:

  • 这是一个很好的解决方案。它不完全适合我的情况,但它会作为参考。
【解决方案2】:

我会使用这样的东西:

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

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

【讨论】:

  • 如果我错了,请纠正,但这个解决方案基本上是 Nermin 提出的解决方案的“多行”版本。我个人更喜欢这种方式,但你的更具可读性,我宁愿把这个版本放在 prod 中。
  • @Harry:正确,我们的代码做同样的事情。只有 Nermin 的代码还处理 none 日期不存在的情况(unless
  • [].join (' AND ') 返回"",这是可以接受的。正如我所说,我喜欢更短的代码,但这看起来更好维护。
  • @Harry:我同意你的观点 :)
【解决方案3】:
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

【讨论】:

  • unless date_conditions.empty? 是多余的。
  • 我会去掉除非,但它看起来不错。
  • 在问题的最后一部分不是else 它是elsif 所以当没有date_1 并且没有date_2 提供方法将返回nil 与问题相同。所以我不会说除非是多余的
  • @mudasobwa:取决于conditions 的实现方式。 (这不是内置的 AR 方法,是吗?)要复制原始代码完全正确,需要这个 unless(或类似的东西)
【解决方案4】:

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

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

【讨论】:

  • 我明白为什么有人会认为这很优雅,但就个人而言,我不会在生产代码中编写这样的东西。主要是因为条件结构在代码中嵌入太多。例如,想象一下,您需要为其中一个条件调用一个函数。 MONTH(date) 什么的。现在你优雅的减少分崩离析。 :)
  • @SergioTulentsev 是的,这是真的。此外,添加 更多 条件(例如,一打 :) 将不会显着增加此代码的数量,而您的代码将被 rubocop 拒绝,因为该方法中有 52LOC。
  • @mudasobwa touché.
  • @maxpleaner 我越想这个我认为created_ad 也应该移到输入数组中。
猜你喜欢
  • 2012-10-07
  • 2022-11-30
  • 1970-01-01
  • 2012-03-31
  • 2021-05-20
  • 2011-03-23
  • 1970-01-01
  • 2020-11-18
  • 1970-01-01
相关资源
最近更新 更多