simple_from_for with decimal precision doesn't save decimals - ruby-on-rails

I am using simple_form_for for my form template which is not storing decimal numbers into my database table. When I submit the form, it throws away the precision.
Here is what I have:
my schema.rb
create_table "coupons", force: true do |t|
t.decimal "amount", precision: 10, scale: 0
end
new.html.slim
.panel-body.padded
= simple_form_for #coupon, :url => admin_coupons_path do |f|
.form-group
= f.input :amount, as: :string, input_html: { value: number_with_precision(f.object.amount, precision: 8) }, required: true
database image
Am confused as to what else I can do to make that work. Any help will be appreciated.

The missing piece to the puzzle is the precision of the decimal within the database. By default, the code above will create a decimal field in MySQL with 10 digits, all of which are to the left of the decimal point. In other words, saving 15.37 to your model will show 15.37 within the app, but when you save it, it’ll become 15 both in the data store and when you reload it.
The way around this is to specify the precision and the scale in your migration. So correct it with a followup migration:
change_column :table_name_in_plural, :column_name, :decimal, :precision => 10, :scale => 2
In this case, we’ve set the total number of digits (the precision) to 10, with 2 decimal places (the scale.) Note that you can pick your sizes as per your needs – MySQL docs give some guidelines to the storage requirements for different combinations.
Also, when picking your precision and scale, be aware that the scale (max decimal places) can’t be greater than the precisions (total digits.) That might seem obvious when written here, but without those translations in parentheses, it’s easy to get lazy and make the two values the same, which would leave you with a number that has to be less than 1.0, since all the space will be allocated to the right of the decimal place.
Reference: Here

Related

How do i change column type from integer to decimal, with a letter after the decimal?

I just realized my column type needs to be decimal instead of integer
Can i make the change directly in my seeds.rb or there's something else i should do?
Also how do i do this if i intend to add a letter after the decimal?
Example: 2.0L, 2.5L, 5.7L, etc.
If you want to store a letter in your DB, that column type can't be a decimal or integer. A string field would be more appropriate for your case. I'll assume your main question is regarding decimals though.
If you do want to change a column type in the DB, you're supposed to do it using Migrations. From terminal, in your app directory, run: bin/rails g migration changeFieldNameFromIntegerToDecimal.
This will generate a migration file with a timestamp in the filename, in which there is a change method telling rails how you want to change the database. In that change method, put:
def change
change_column :table_name, :column_name, :decimal, precision: :8, scale: :2
end
the precision and scale options do the follwing (from the above link):
precision: Defines the precision for the decimal fields, representing the total number of digits in the number.
scale: Defines the scale for the decimal fields, representing the number of digits after the decimal point.
If you want to convert it to a string, then the migration file would contain:
def change
change_column :table_name, :column_name, :string
end
Finally run bin/rails db:migrate from the terminal in your app directory to execute the migration.
This will change the column type to decimal. To know about the scale and precision check out this Stackoverflow question https://stackoverflow.com/a/2377176/12297707 or the docs https://edgeguides.rubyonrails.org/active_record_migrations.html#column-modifiers
change_column :table_name, :column_name, :decimal, :precision => 8, :scale => 2, :default => 0

Generate a column of type 'double' in migration - Ruby on rails

I need to create a column of type double in my database, I understand that in ruby there is no double type and that to simulate this type of value I have to use :scale and :precision
How can i solve this?
You can specify the precision and scale in your migration.
class AddCostToBookings < ActiveRecord::Migration
def change
add_column :bookings, :cost, :decimal, precision: 10, scale: 2
end
end
Precision defines the precision for the decimal fields, representing
the total number of digits in the number.
Scale defines the scale for the decimal fields, representing the number of digits after the decimal point.

Ruby on Rails Models. Why does a decimal{2,1} with scope 1 allow more than digit after the decimal point?

I'm having an issue with a table accepting too many digits after the decimal, despite defining it's precision and scope.
rails generate model Hotel name:string 'rating:decimal{2,1}'
class CreateHotels < ActiveRecord::Migration
def change
create_table :hotels do |t|
t.string :name
t.decimal :rating, precision: 2, scale: 1
t.timestamps
end
end
end
However, I am able to do the following.
Hotel.create!(name: “The Holiday Inn”, rating: 3.75)
Additionally, I have a rooms table (Room model), with
t.decimal :rate, precision: 5, scale: 2 #this holds the room's nightly rate
I input 99.99 into this column, but it ends up storing it as 99.98999999..
Why do I have these 2 decimal issues? If I have defined my scope, why am I allowed to input more scope than I have defined?
I input 99.99 into this column, but it ends up storing it as 99.98999999...
That suggests that you're using SQLite which doesn't really have a decimal data type, if you ask SQLite to create a decimal(m,n) column it will create a REAL instead; REAL is a floating point type, not a fixed precision type. To solve this problem, stop using SQLite; odds are that you're not going to deploy a real application on top of SQLite anyway so you should be developing with the same database that you're going to deploy on.
Also, if you're using a fixed precision type for rating, you should not say this:
Hotel.create!(name: "The Holiday Inn", rating: 3.75)
as that 3.75 will be a floating point value in Ruby and it could get messed up before the database sees it. You should say one of these instead:
Hotel.create!(name: "The Holiday Inn", rating: '3.75')
Hotel.create!(name: "The Holiday Inn", rating: BigDecimal.new('3.75'))
so that you stay well away from floating point front to back.

How can I compare a BigDecimal with ActiveRecord decimal field?

Assume a schema like this:
create_table "bills", :force => true do |t|
t.decimal "cost", :precision => 10, :scale => 5
end
I want to write a function that writes a new bill to the DB iff it is unique. The following does not work:
def load_bill_unless_exists(candidate)
incumbents = Bill.scoped.where(:cost => candidate.cost)
candidate.save unless incumbents.exists?
end
because the incumbent bills and the candidate bills have different limits in their BigDecimal representation, so the :cost => candidate.cost test fails. That is, it's comparing:
candidate: #<Bill id: nil, cost: #<BigDecimal:105e39850,'0.1670576666 6666666E4',27(27)>>
with
incumbent: #<ServiceBill id: 198449, cost: #<BigDecimal:105e35840,'0.167057667E4',18(18)>>
Notice the candidate's BigDecimal represents the cost with more digits than the incumbent.
So the question is simple: What's the right way to perform this comparison? I contemplated :cost => BigDecimal.new(candidate.cost.to_s, 18), but that doesn't feel right -- for example, where does that number 18 come from?
Try using BigDecimal#round:
def load_bill_unless_exists(candidate)
incumbents = Bill.scoped.where(:cost => candidate.cost.round(5))
candidate.save unless incumbents.exists?
end
From the docs:
Round to the nearest 1 (by default), returning the result as a BigDecimal. If n is specified and positive, the fractional part of the result has no more than that many digits.
Given that you've specified a precision of 5 in your schema, that's what you should be rounding to when doing comparisons.
If casting like you were contemplating works, you probably have to go with that. The query you're using is just building a "WHERE cost = number" and if the database isn't able to compare properly with the number as passed, you need to pass it differently. It looks like it's the database stopping you and not anything within Rails necessarily.
If you just don't like casting within your query, you could always do it in the model:
def cost_with_incumbent_precision
BigDecimal.new(cost.to_s, 18)
end

ruby on rails - Two digit precision

iam working on RoR code, i want to know how to find two digit precision for the numbers
a = 0.7
b = 5
thanks
Always your standard
a = "%.2f" % 0.2
b = "%.2f" % 5
0.20
5.00
Outputs strings though, so you may need ("%.2f" % 0.2).to_i or cast later (a.to_i).
number_with_precision. If you're looking to express them as currency, use number_to_currency.
If you're looking to store those numbers and their precision in the database you should set up decimal fields.
add_column :table_name, :column_name, :decimal, :precision => 5, :scale => 2
Options for a decimal field:
:precision : maximum number of digits in a value
:scale : number of digits after the decimal place
With a decimal field you will never need the helper methods to limit the precision after the decimal place.

Resources