Reputation: 2198
I have a field called price in my db which is a decimal field and in my JSON API response it is returned as a String.
I wanted to apply some validations to the field that would allow max 2 digits after period and found out that if I use decimal field I can then apply the precision on DB level.
t.decimal "price", precision: 10, scale: 2
Then I want to calculate total price in a service object:
class CurrentMonthRidesGenerator
def initialize(current_user)
@current_user = current_user
end
def call
user_current_month_rides_by_day.map do |date, rides_on_day|
{
day: formatted_date(date),
total_distance: total_distance(rides_on_day),
avg_ride: avg_ride(rides_on_day),
avg_price: avg_price(rides_on_day),
total_price: total_price(rides_on_day)
}
end
end
...
def total_price(rides)
rides.map(&:price).sum
end
end
get '/current_month' do
Statistics::CurrentMonthRidesGenerator.new(current_user).call
end
but in API response this field is a String.
{
"day": "November, 8th",
"total_distance": "9km",
"avg_ride": "9km",
"avg_price": "100.0PLN",
"total_price": "100.0"
}
I want this field to be returned as it was saved because I need a float/decimal number in the front end to then do other calculations.
Why is it returning a String when it is a decimal field? How can I fix it?
Upvotes: 1
Views: 3197
Reputation: 2198
I decided to solve this problem by changing the column type to Float
, renaming it to price_cents
, adding money-rails
and monetizing the column. Then I just call to_f
or round the value right before displaying it or sending it to front-end in API response.
Upvotes: 0
Reputation: 3255
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:
If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
Upvotes: 6
Reputation: 3121
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
Upvotes: 2