Reputation: 18773
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.022425
seconds. 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...
Upvotes: 2
Views: 1237
Reputation: 39970
After the deliberation in the comments, the best option seems to be monkeypatching ActiveSupport::JSON
to make it handle BigDecimal
s the same as Numeric
s:
class BigDecimal
def as_json(options = nil) self end #:nodoc:
def encode_json(encoder) to_s end #:nodoc:
end
This overrides the Rails team's decision to prevent serialised BigDecimal
s from being parsed as floats (and losing precision) in JSON deserialisers with no support for decimal numbers.
Upvotes: 1