I have model named Dish which has an attribute cost of float type. I've set precision to 15 and scale to 2. The problem is in rails console, the value for cost is displayed in scientific notation.
cost: 0.102e2
How to display in decimals?
you can use to_f for simple format, but for more options you can also use NumberHelper module, you can format with many format from this helper module , here is reference
from your console:
include ActionView::Helpers::NumberHelper
number_with_precision(cost, precision: 2)
To prevent ActiveRecord from displaying decimal values in scientific notation, you can set the :float column type when defining your model's attributes. This will cause ActiveRecord to display decimal values in float format rather than scientific notation.
For example, you might define your model's attributes like this:
class MyModel < ApplicationRecord
attribute :my_decimal_value, :float
end
This will cause ActiveRecord to display the my_decimal_value attribute in float format rather than scientific notation.
Related
I am handling a bunch of BigDecimals, which I want to store to my database. Ideally without any loss of accuracy. I don't know the nicest way to achieve that.
This came to my mind:
t.decimal :value, precision: 1000, scale: 30
It does not look like a good way to approach this problem. 1. it still compromises accuracy. 2. It's is getting unnecessarily large.
Is there a way to store the object, e.g.: #<BigDecimal:586a238,'0.563E0',9(36)> to the database (within a text column) and then re-initialize it as a BigDecimal?
You might want to look into composed_of.
But I prefer using custom getter and setter methods because I thing they are easier to read and to understand.
Example: Imagine your attribute is named foo and you want to use BigDecimal in the app but store the value as a string in the database:
def foo
BigDecimal.new(read_attribute(:foo))
end
def foo=(foo)
write_attribute(:foo, foo.to_s)
end
By default a PostgreSQL Decimal has a range of "up to 131072 digits before the decimal point; up to 16383 digits after the decimal point". Is that not enough?
https://www.postgresql.org/docs/9.1/static/datatype-numeric.html
Just use:
t.decimal :value
I have a model that has an attribute named value this is saved in the db as an integer.
I input the number in the view using a text_field, I am also performing a method on it :before_save that takes the value (something like 21.37) and using the money gem convert it to just cents.
However, it seems that before I can even perform the method that converts it from a float to an integer it is being converted to some kind of integer and the decimal is being lost.
I have tested this by outputting value in the method that runs before_save: and it round it to 21
Does anyone know why this might be happening, Im not performing any other changes to it.
I'm guessing you're doing something like Coupon.new(params) in your controller, and since Rails knows Coupon's value should be an integer, it helpfully calls to_i on params[:value] for you under the covers, which turns 21.37 into 21. Here's a rather inelegant but effective fix:
params[:value] ~= /.*\.(\d+)/
params[:value] = params[:value].to_f * (10**$1.length) if $1
Do that before you do Coupon.new(params).
Another option is to simply assume you don't care about anything after the 2nd decimal place (you're dealing with cents after all). Then you can simply do params[:value] = params[:value].to_f.round(2) * 100.
Also you should consider using number_field instead of text_field in your view so you can at least be sure you're getting a number. Something like number_field(:coupon, :value, step: 0.01) should ensure your users can enter decimals.
I agree with #kitkat seems like something that would happen in the model layer. You might be able to implement your conversion to cents logic in 'before_validation' or perhaps a custom setter for 'coupon'.
Ok I have a Stripe charge to which I am applying taxes. Stripe takes in a number as cents, so it leaves you with a number like 10015 instead of 100.15.
In my controller, I am sending the number to ActiveRecord as 10015/100.0
When I retrieve it, it gives me #<BigDecimal:7fca81f71130,'0.1243E3',18(27)>>
Whats going on ?
I tried
rails g migration add_expense_to_user expense:integer
and
rails g migration add_expense_to_user expense:decimal
to whose migration I added
add_column :user, :expense, :decimal, precision: 6, scale: 2
which is the current setup.
How do I store / retrieve the value if it is stored as 10015/100
The BigDecimal is just the type that Rails uses for decimal types in DBs. 0.1243E3 is scientific notation, short for 0.1243 x 10³ - ie. 124.3
You can get a regular float from it by just using the .to_f method in Ruby, or you can pass a BigDecimal into other Rails helpers, like number_to_currency(the_big_decimal) will produce "$124.30"
So, in other words, with the BigDecimal you probably already have what you're asking for in this question.
When you access the data, you need to call .to_f.
In irb:
a = BigDecimal.new(5, 2)
a
=> #<BigDecimal:1ad7c98,'0.5E1',9(27)>
a.to_f
=> 5.0
The value is stored inside the BigDecimal object with arbitrary precision. The "common" representation of non-integer values (i.e. floats) however doesn't provide this precision. Thus, BigDecimal provides various options to convert its value to other types.
You can e.g use expense.to_f to get a floating point representation of the BigDecimal value (and thus loosing the precision along the way). Alternatively, if you just want to print the value, you could use one of the to_s method to format the value as a string:
expense.to_s
# => "124.3"
See the BigDecimal documentation for details.
I have a model with column amount which is a decimal in the database. I'd like to ensure that only a BigDecimal with certain precision is ever given when this model is instantiated. I've written specs to test the scenario when a Float is provided, and then I have a before_create callback that raises an error if it's not a BigDecimal.
However, by the time the value gets to the before_create callback, Rails has already converted it to a BigDecimal. This is nice I supposed, and I can probably still check for precision, but since I don't know precisely how rails goes about converting, it would be nice to check for proper argument type and precision further up the chain.
Is there any way to do this?
From http://api.rubyonrails.org/classes/ActiveRecord/Base.html
Sometimes you want to be able to read the raw attribute data without having the column-determined typecast run its course first. That can be done by using the _before_type_cast accessors that all attributes have. For example, if your Account model has a balance attribute, you can call account.balance_before_type_cast or account.id_before_type_cast.
Try to override amount=
def amount=(val)
# checking / conversion goes here, new_val as result
self[:amount] = new_val # don't use self.amount = new_val to avoid endless loop
end
Is there a way to force Rails to output all decimal numbers with a certain precision? All of the decimal fields in my database are currency amounts, so I'd like decimal numbers to show by default with a precision of 2 (i.e. 2.40). I know I can use a helper function like "number_to_currency" to do this to each individual number, but that seems a bit tedious and unnecessary.
If you're concerned about overriding "to_s" on Float having possible unforeseen side-effects, your next best bet is probably to just create a new method, but still as a core extension. Something like this:
class Float
def c
sprintf('%.2f', self)
end
end
Then can't have any unforeseen consequences, and then anywhere you'd want to display the number with two decimal places, you'd just call .c. For example:
message = "The account balance is $#{amount.c}."
Not automatic, but not much extra typing, and no possible side-effects that overriding to_s could potentially cause.
Well if you don't mind monkey patching ruby, you can add a something like this (put it in an initializer at "/config/initializers/core_extensions.rb"):
class Float
def to_s
sprintf('%.2f', self)
end
end