我在控制器动作中有两个日期参数,如果它们为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
感觉不好的人。是否有任何方法来解析日期与Ruby标准库,不需要begin...rescue
块?在这种情况下,慢性感觉有点过头了。
总的来说,我不同意其他解决方案,以这种方式使用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
不得不面对同样的问题,我结束了猴子修补Ruby如下:
# date.rb
class Date
def self.safe_parse(value, default = nil)
Date.parse(value.to_s)
rescue ArgumentError
default
end
end
任何异常值将在进入方法之前上升,只有ArgumentError
被捕获(尽管我不知道任何其他可能的)。
内联rescue
的唯一正确用法类似于:
f(x) rescue handle($!)
这些天我更喜欢而不是 monkey patch Ruby。相反,我把
Date
包装在Rich
模块中,我把它放在lib/rich
中,然后用:
调用它:Rich::Date.safe_parse(date)
为什么不直接:
starting = DateTime.strptime(params[:starting], '%Y-%m-%d') rescue @meeting_range.first
目前我比较喜欢的方法是使用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"))
所有现有的答案都有rescue
。然而,我们可以使用一些"丑陋的"。
这个方法很难看,因为它以下划线开头。然而,它符合目的。
这样,问题中的方法调用可以写成starting = if params[:starting].present?
parsed = DateTime._strptime(params[:starting], "%Y-%m-%d") || {}
if parsed.count==3 && Date.valid_date?(parsed[:year], parsed[:month], parsed[:mday])
@meeting_range.first
end
else
@meeting_range.first
end
- 如果日期字符串与输入格式匹配,
_strptime
将返回包含所有3个日期部分的散列。所以parsed.count==3
意味着这三个部分都存在。 - 然而,进一步检查三个部分在日历中形成有效日期仍然是必要的,因为
_strptime
不会告诉你它们是无效的。
当你想获得日期作为对象,从字符串变量解析,有时传递的字符串值可能是nil,或空,或无效的日期字符串。我想写安全的方法,简称:
def safe_date(string_date)
::Date.parse(string_date)
rescue TypeError, ::Date::Error
::Date.today
end
例如-检查irb控制台:
3.0.2 :001 > safe_date
=> #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)>
3.0.2 :001 > safe_date('')
=> #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)>
3.0.2 :002 > safe_date('29.12.2022')
=> #<Date: 2022-12-29 ((2459943j,0s,0n),+0s,2299161j)>
3.0.2 :003 > safe_date('29.13.2022')
=> #<Date: 2022-08-29 ((2459821j,0s,0n),+0s,2299161j)>