在Ruby中优雅的日期解析

Ada*_*sek 19 ruby datetime parsing

我在控制器操作中有两个日期参数,如果它们是nil,或者解析失败,我想回退到默认值.

不幸的是,DateTime.strptime如果解析失败,它似乎抛出一个异常,这迫使我写下这个怪物:

starting = if params[:starting].present?
  begin
    DateTime.strptime(params[:starting], "%Y-%m-%d")
  rescue
    @meeting_range.first
  end
else
  @meeting_range.first
end
Run Code Online (Sandbox Code Playgroud)

感觉坏人.有没有办法用Ruby stdlib解析一个不需要begin...rescue块的日期?对于这种情况,慢性感觉有点矫枉过正.

eco*_*gic 21

一般来说,我不能同意其他解决方案,使用rescue这种方式是不好的做法.我认为值得一提的是,有人试图将这个概念应用于不同的实现.

我担心的是,你可能会感兴趣的其他一些例外将被隐藏rescue,打破了早期的错误检测规则.

以下是Date没有,DateTime但你会明白:

Date.parse(home.build_time) # where build_time does not exist or home is nil
Date.parse(calculated_time) # with any exception in calculated_time
Run Code Online (Sandbox Code Playgroud)

不得不面对同样的问题,我最终修补了Ruby如下:

# date.rb
class Date
  def self.safe_parse(value, default = nil)
    Date.parse(value.to_s)
  rescue ArgumentError
    default
  end
end
Run Code Online (Sandbox Code Playgroud)

在进入方法之前,任何值的异常都会上升,并且只会ArgumentError被捕获(尽管我不知道任何其他可能的异常).

内联的唯一正确使用rescue类似于:

f(x) rescue handle($!)
Run Code Online (Sandbox Code Playgroud)

更新

这些天,我宁愿没有猴子补丁红宝石.相反,我将我包装Date在一个Rich模块中,我将其放入lib/rich,然后用以下方法调用它:

Rich::Date.safe_parse(date)
Run Code Online (Sandbox Code Playgroud)

  • 事实上,每次我使用内联救援时,我都会后悔.我很久以前就把我所有的日期/时间解析需要用[timeliness](https://github.com/adzap/timeliness)代替,它的行为就像我想要的那样. (4认同)

Ama*_*dan 19

为什么不简单:

starting = DateTime.strptime(params[:starting], '%Y-%m-%d') rescue @meeting_range.first
Run Code Online (Sandbox Code Playgroud)

  • 完全一致的是,未能解析值会引发异常.同样,如果您期望一个值,那么非值也是一个异常状态.Ruby不会这样做,但是在许多语言中,即使是在数组中搜索特定值的函数,如果值不在数组中,也会引发异常,并且通过错误的键访问哈希元素也会这样做(在Ruby中)返回`nil`).不应将例外视为错误,而应视为一种令人惊讶的状态,与特定情况下的正常状态不同.如果你采取这种观点,而不是反模式. (2认同)
  • 这是行为的不一致,而不是我认为是反模式的异常本身.你是对的,抛出异常在某些情况下是完全合理的.我只是希望这里有一个替代方案,因为在这种情况下,解析失败并不令人意外. (2认同)

Ada*_*sek 3

如今我首选的方法是用于 Dry::Types类型强制和Dry::Monads表示错误。

require "dry/types"
require "dry/monads"
Dry::Types.load_extensions(:monads)
Types = Dry::Types(default: :strict)

Types::Date.try("2021-07-27T12:23:19-05:00")
# => Success(Tue, 27 Jul 2021)

Types::Date.try("foo")
# => Failure(ConstraintError: "foo" violates constraints (type?(Date, "foo"))
Run Code Online (Sandbox Code Playgroud)