Rails: точность времени # to_f в JSON? - PullRequest
2 голосов
/ 04 февраля 2012

Я создаю простой JSON API с использованием Rails 3.2.1 и Jbuilder на Ruby 1.8.7 (1.9.x может помочь мне в этом, но у моего хостинг-провайдера только 1.8.7).

Поскольку потребитель API ожидает метки времени как числа с плавающей точкой, в настоящее время я просто делаю простые to_f для атрибутов времени:

json.updated_at record.updated_at.to_f #=> 1328242368.02242

Но to_f несет потерю точности. Это вызывает некоторые проблемы, когда клиент запрашивает записи, которые были изменены с данного момента времени, поскольку запрос SQL находит ту же запись, которую клиент использует для справки. То есть при попытке найти «более новые» записи, чем в приведенном выше примере, SQL-запрос (например, updated_at > Time.at(1328242368.02242)) возвращает эту же запись, поскольку фактическое значение updated_at является более точным и незначительно большим, чем заданная временная метка.

Фактически, record.updated_at.usec #=> 22425 или 0.022425 секунд. Обратите внимание на дополнительный десятичный.

Таким образом, оптимально, метка времени должна быть JSON'-указана с одним дополнительным десятичным знаком, например, 1328242368.022425, но я не могу найти способ, чтобы это произошло.

updated_at.to_i #=> 1328242368       # seconds
updated_at.usec #=> 22425            # microseconds
updated_at.to_f #=> 1328242368.02242 # precision loss

# Hacking around `to_f` doesn't help
decimals = updated_at.usec / 1000000.0 #=> 0.022425         # yay, decimals!
updated_at.to_i + decimals             #=> 1328242368.02242 # dammit!

Я искал способы установить точность по умолчанию, но я в тупике. Есть идеи?

Редактировать: я должен добавить, что потребитель API не использует JavaScript, поэтому float может иметь более высокую точность. Это нарушило бы JS-совместимость (и, следовательно, спецификацию JSON), добавив еще одну цифру (десятичную или иную), поскольку, как я полагаю, поплавки JS не могут справиться с этим. Так что, возможно, мне нужен совершенно другой подход ...

1 Ответ

1 голос
/ 04 февраля 2012

После обсуждения в комментариях, лучший вариант, кажется, monkeypatching ActiveSupport::JSON, чтобы он обрабатывал BigDecimal s так же, как Numeric s:

class BigDecimal
  def as_json(options = nil) self end #:nodoc:
  def encode_json(encoder) to_s end #:nodoc:
end

Это отменяет решение команды Rails о том, чтобы предотвратить анализ сериализованных BigDecimal s как числа с плавающей точкой (и потерю точности) в десериализаторах JSON без поддержки десятичных чисел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...