I’m building a simple JSON API using Rails 3.2.1 and Jbuilder on Ruby 1.8.7 (1.9.x might help me here, but my hosting provider only has 1.8.7).
Since the API consumer expects timestamps as floats, I’m currently just doing a simple to_f on the time attributes:
json.updated_at record.updated_at.to_f #=> 1328242368.02242
But to_f incurs a precision loss. This causes some issues when the client requests records that have been modified since a given point in time, as the SQL query finds the same record that the client uses for reference. I.e. when trying to find “newer” records than the example above, the SQL query (e.g. updated_at > Time.at(1328242368.02242)) returns that same record, since the actual value of updated_at is more precise and fractionally larger than the given timestamp.
In fact, record.updated_at.usec #=> 22425 or 0.022425seconds. Notice the extra decimal.
So optimally, the timestamp should be JSON’ified with 1 extra decimal, e.g. 1328242368.022425, but I can’t find a way to make that happen.
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!
I’ve looked around for ways to set the default float precision, but I’m stumped. Any ideas?
Edit: I should add that the API consumer isn’t running JavaScript, so the float can have higher precision. It would break JS-compatibility (and thus the JSON spec) to add another digit (decimal or otherwise), since JS floats can’t handle that, I believe. So perhaps I need an entirely different approach…
After the deliberation in the comments, the best option seems to be monkeypatching
ActiveSupport::JSONto make it handleBigDecimals the same asNumerics:This overrides the Rails team’s decision to prevent serialised
BigDecimals from being parsed as floats (and losing precision) in JSON deserialisers with no support for decimal numbers.