Trouble converting money value to cents in Rails - ruby

I story money values as integers like, price_in_cents or fee_in_cents. So, $1.00 would be stored in the database as 100.
Internally, I've always just typed 100 in the form when I meant 1.00, but I thought it would be simple to convert a form input of 1.00 to 100 in the model, but I'm having trouble.
No matter what I enter, I'm getting 0 for both values. Here's the model:
before_create :convert_money_to_cents
attr_accessor :price, :fee
private
def convert_money_to_cents
self.price_in_cents = price.to_i * 100
self.fee_in_cents = fee.to_i * 100
end
Any thoughts on why it's always coming up zero? I tried converting the string to various things (floats) and I'm still getting zeros.

Try this:
def convert_money_to_cents
self.price_in_cents = (self.price.to_d * 100).to_i
self.fee_in_cents = (self.fee.to_d * 100).to_i
end

I would probably do something like:
class Numeric
def to_cents
(self.to_f * 100).to_i
end
end

Related

Float not equal after division followed by multiplication

I'm testing a small and simple library I made in Ruby. The goal is to convert from EUR to CNY and vice versa. Simple.
I tested it to be sure everything works but I got an unexpected issue. When I use to_euro followed by to_yuan it should go back to the original amount ; it doesn't happen. I tried to .to_f or round(2) the amount variable which fix some tests, raise new ones, but it's never equal to what I expect globally ; I'm running out of idea to fix this :(
class Currency
attr_reader :amount, :currency
def initialize(amount, currency='EUR')
#amount = amount
#currency = currency
end
def to_yuan
update_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)
end
def to_euro
update_currency!('EUR', amount / Settings.instance.exchange_rate_to_yuan)
end
def display
"%.2f #{current_symbol}" % amount
end
private
def current_symbol
if currency == 'EUR'
symbol = Settings.instance.supplier_currency.symbol
elsif currency == 'CNY'
symbol = Settings.instance.platform_currency.symbol
end
end
def update_currency!(new_currency, new_amount)
unless new_currency == currency
#currency = new_currency
#amount = new_amount
end
self
end
end
Tests
describe Currency do
let(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }
context "#to_yuan" do
it "should return Currency object" do
expect(Currency.new(20).to_yuan).to be_a(Currency)
end
it "should convert to yuan" do
expect(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)
end
it "should convert to euro and back to yuan" do
# state data test
currency = Currency.new(150, 'CNY')
expect(currency.to_euro).to be_a(Currency)
expect(currency.to_yuan).to be_a(Currency)
expect(currency.amount).to eql(150.00)
end
end
context "#to_euro" do
it "should convert to euro" do
expect(Currency.new(150, 'CNY').to_euro.amount).to eql(150 / rate)
end
end
context "#display" do
it "should display euros" do
expect(Currency.new(10, 'EUR').display).to eql("10.00 €")
end
it "should display yuan" do
expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")
end
end
end
And here's my RSpec result
I'm pretty sure this problem is very common, any idea how to solve it easily ?
Float isn't an exact number representation, as stated in the ruby docs:
Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.
This not ruby fault, as floats can only be represented by a fixed number of bytes and therefor cannot store decimal numbers correctly.
Alternatively, you can use ruby Rational or BigDecimal
Its is also fairly common to use the money gem when dealing with currency and money conversion.

(Ruby) Padding single digits when it comes to time

I have 2 methods in a Timer class I'm creating. One method is where hours, minutes, and seconds are calculated from any given amount of seconds and the other method will pad any single digits with a "0". Every things I've look up so far isn't work for me. Below is my code:
class Timer
attr_accessor :seconds=(time), :time_string
def initialize(seconds = 00)
#seconds = seconds
end
def time_string
hours = padded((#seconds/3600)
minutes = padded(#seconds/60 - hours * 60)
seconds = padded(#seconds%60)
puts '#{hours):#{minutes}:#{seconds}'
end
def padded(x)
if x.length == 1
"0"+x
end
end
end
so if I put in 7683, the output I want to get is "02:08:03". but when I execute it, I get the following error message:
(eval):6: (eval):6:in `-': String can't be coerced into Fixnum (TypeError)
from (eval):6:in `time'
from (eval):19
I'm confused where this is erroring out.
To answer your question as to why your code is not working, you have got couple of conversion issues between FixNum and String throughout your code, you can fix it as follows:
def time_string(seconds)
hours = seconds/3600
minutes = seconds/60 - (hours * 60)
seconds = seconds%60
puts padded(hours)+':'+padded(minutes)+':'+padded(seconds)
end
You use the hours variable in the second statement, but because its already converted to string, it crashes, therefore its better to do all the calculations first, and only later use the padded method which returns the padded digits in string format. The padded method must also be modified to be consistent:
def padded(x)
if x.to_s.length == 1
return "0"+x.to_s
else
return x.to_s
end
end
Just keep in mind that the combination of the two methods will work only for numbers up to 86399, which will return 23:59:59. Any number passed to time_string bigger than that will pass the 24 hour mark and will return something like: 26:00:00
There is a brilliant method for padding, why not use it?
3.to_s.rjust(10,"*") #=> "*********3"
4.to_s.rjust(2,"0") #=> "04"
44.to_s.rjust(2,"0") #=> "44"
If you want a better solution than writing your own class, use at
Time.at(7683).strftime("%H:%M:%S") #=> "02:08:03"
There's no need to reinvent the wheel.
t = 7683 # seconds
Time.at(t).strftime("%H:%M:%S")
Time.at(seconds) converts your seconds into a time object, which then you can format with strftime. From the strftime documentation you can see you can get the parameters you want non padded, white padded or zero padded.
I tend to use something like this
"%02d" % 2 #=> 02
"%02d" % 13 #=> 13
It's part of the Kernel module: http://ruby-doc.org/core-2.1.3/Kernel.html#M001433

Find percentage of two small numbers in ruby

I have two small numbers that I'd like to find the percentage of.
First number: 0.683789473684211
Second number: 0.678958333333333
I want to find out what percentage of the number is bigger or smaller. These happen to be small numbers, but they could be bigger. The first number COULD be 250, and the second number could be 0.3443435. What I'm TRYING to do is detect whether the first number is 25% bigger than the second number.
I tried using this:
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
But it kept saying I was dividing by zero
How would you do it?
Why not shoot straight for what you say you want to do?
class Numeric
def sufficiently_bigger?(n, proportion = 1.25)
self >= proportion * n
end
end
p 5.sufficiently_bigger? 4 # => true
p 5.sufficiently_bigger? 4.00001 # => false
This will default to a 25% larger check, but you can override the proportionality by supplying a different value as the second argument.
It's generally easier and avoids the need for an explicit zero-denominator check if you express ratios in product form rather than using division.
The basic implementation of your code looks correct to me. Can you provide the specific example and expected output that is producing that error?
Just because I was curious I took your code and executed it with a small test suite and had 3 passing tests.
require 'rubygems'
require 'test/unit'
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.00
end
end
class PercentageTeset < Test::Unit::TestCase
def test_25_is_50_percent_of_50
assert_equal (25.percent_of(50)), 50.0
end
def test_50_is_100_percent_of_50
assert_equal (50.percent_of(50)), 100.0
end
def test_75_is_150_percent_of_50
assert_equal (75.percent_of(50)), 150.0
end
end
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
p 0.683789473684211.percent_of(0.678958333333333)
--output:--
100.71155181602376
p 250.percent_of(0.3443435)
--output:--
72601.9222084924
p 0.000_001.percent_of(0.000_000_5)
--output:--
200.0
p 0.000_000_000_01.percent_of(0.000_000_000_01)
--output:--
100.0
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
numbers = [ 0.683789473684211, 0.678958333333333 ]
min_max = {min: numbers.min, max: numbers.max}
puts "%<min>f is #{min_max[:min].percent_of(min_max[:max])} of %<max>f" % min_max
This program has opinions in that it shows what percentage the minimal number is of the maximal number, and shows the numbers.
If you use %d for the String#format method, you will show 0's. Perhaps that was what you were referring to, not sure.
Edit: Using minmax as suggested.
class Numeric
def percent_of(n)
self.to_f / n.to_f * 100.0
end
end
numbers = [ 0.683789473684211, 0.678958333333333 ]
min_max = Hash.new
min_max[:min], min_max[:max] = numbers.minmax
puts "%<min>f is #{min_max[:min].percent_of(min_max[:max])} of %<max>f" % min_max
I like the first version as the hash is built as it is needed, rather than initalized and then built.

Ruby, how to take a specific percentage of elements of an array

In a testing context I need to take a specific percentage of the elements of an array.
The specifications of my request can be described in this test:
def test_percent_elements
array = [1,2,3,4,5,6,7,8,9,10]
assert_equal([], array.percent_elements(0))
assert_equal([1], array.percent_elements(1))
assert_equal([1], array.percent_elements(10))
assert_equal([1,2], array.percent_elements(11))
assert_equal([1,2,3,4,5], array.percent_elements(50))
assert_equal([1,2,3,4,5,6,7,8,9,10], array.percent_elements(100))
end
Which is the best way to solve this in Ruby?
I'd write:
class Array
def percent_elements(percent)
take((size * percent / 100.0).ceil)
end
end
My actual approach is this:
class Array
def percent_elements(percent)
total = self.length
elements = ((total * percent) / 100.to_f).ceil
self[0, elements]
end
end

Why does Ruby give this large precision decimal result?

I want to convert a subtitle time code:
begin="00:00:07.71" dur="00:00:03.67
to pure seconds:
begin=7.1 end=11.38
I wrote a Ruby code:
def to_sec(value)
a = value.split(':')
a[0].to_i*3600+a[1].to_i*60+a[2].to_f
end
which resulted in 11.379999999999999.
Can anybody tell me why this happens?
Is there any Time library that can do this conversion?
It'll probably be easiest for you to represent your underlying datatype as integer hundredths of a second (centiseconds):
def to_csec(value) #if you had CSec < Integer this would be `def self.[](value)`
a = value.split(':')
#tacking on a couple zeros to each
a[0].to_i*360000+a[1].to_i*6000+(a[2].to_f * 100).to_i
end
You could add some helpers for dealing with the durations and pretty printing them as well:
def csec_to_s(csec) #if you had CSec < Integer, this would be `def to_sec`
"%.2f" % (csec.to_f / 100)
end
class SubtitleDuration < Range
def initialize(a,b)
centi_a = to_csec(a)
super(centi_a,to_csec(b) + centi_a)
end
def to_s
"begin=#{csec_to_s(self.begin)} end=#{csec_to_s(self.end) }"
end
end
Then your answer is just:
puts SubtitleDuration.new("00:00:07.71", "00:00:03.67").to_s
#=> begin=7.71 end=11.38
This sort of thing can happen in just about any programming language. It's because of how floating point numbers are represented. They're not stored as decimals under the hood, so sometimes you get odd rounding errors like this.

Resources