Изящный синтаксический анализ даты в Ruby

У меня есть два параметра даты в действии контроллера, которые я хотел бы вернуть обратно к значению по умолчанию, если они равны нулю или разбор не выполняется.

К сожалению, кажется, что 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 stdlib, которая не требует блока begin...rescue? Chronic чувствует себя излишним для этой ситуации.

Ответы

Ответ 1

В общем, я не могу согласиться с другим решением, используя rescue таким образом, это плохая практика. Я думаю, стоит упомянуть, если кто-то другой попытается применить концепцию к другой реализации.

Меня беспокоит, что какое-то другое исключение, которое может вас заинтересовать, будет скрыто этим rescue, нарушив правило раннего обнаружения ошибок.

Ниже приведено Date not 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($!)

Обновление

В эти дни я предпочитаю использовать не патч для обезьян Ruby. Вместо этого я обертываю свой Date в модуль Rich, который я помещаю в lib/rich, затем вызываю его с помощью:

Rich::Date.safe_parse(date)

Ответ 2

Почему бы просто:

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