Ruby: Strip significant digits from number? - ruby-on-rails

Suppose I have the number 1.29999. I want to just have 1.2 without the trailing 9s. Note, I don't want it to round to 1.3. How do I do this? I know there's the number helper, but I can't seem to get this working outside of a view. Any ideas?
For instance, number_with_precision 111.2345, :precision => 2 does not work if I just put it in a normal model function.
Thanks!

Another approach is to multiply by 100, truncate, then divide by 100.0:
$ irb --simple-prompt
>> (1.29999999*100).truncate/100.0
=> 1.29
Making it a method:
>> def truncate_to_two (x)
>> (x * 100).truncate/100.0
>> end
=> nil
>> truncate_to_two 6342.899
=> 6342.89
>> truncate_to_two -322.11892
=> -322.11
>> truncate_to_two 244.9342
=> 244.93

It's rudimentary, but you can do use string manipulation instead of math to do it. Example:
x = 1.29999
truncated = x.to_s.match(/(\d+\.\d{2})/)[0] # assumes the format "n.nn" with 2 or more digits of precision; the regex can be expanded to handle more cases

You can always include ActionView::Helpers::NumberHelper in your model to get access to the helpers.

Related

How to add decimal place to last two positions in string? Rails 4

I am trying to convert the string 1009 to become $10.09. I have been looking at the documentation for NumberHelpers for about 15 minutes now and am a bit surprised that I did not find any methods that can add a decimal point delimiter . before the last two positions of a string or integer.
At first, I tried:
number_to_currency("1009", :unit => "$")
# => $1,009.00
then I tried using:
number_to_currency("1009", :unit => "$", :precision => 0)
# => $1,009
which only removes digits after the decimal place, but does not shift the decimal place itself. As well, adding -2 as the precision argument throws ArgumentError.
I know this is possible to do with a few lines of JavaScript but I was looking for a more a pure Rails approach, if any.
How would someone be able to accomplish this?
You could just divide the number with 100. It's simple and it works. I guess you are converting cents to USD, I don't think there's any other way around it.
number_to_currency("1009".to_f / 100, :unit => "$")
You could also write a helper for this, something like...
def cents_to_usd(amount)
number_to_currency(amount.to_f / 100, :unit => "$")
end
So that it looks nicer in your views.

Overriding the way Integers print in .erb

I want to print numbers in my rails application with commas. As per the answer here, I could wrap every single number I print with
number_with_delimiter(#number, :delimiter => ',')
However, I don't want to go into my views and apply this manually. I'd much rather override the way integers are printed.
In java/jsp, anything inside a <%= %> tag gets a toString() call to evaluate what is printed on the page, so I figured that overriding the to_s method for the Integer class would do:
class Integer
def to_s
number_with_delimiter(self, :delimiter => ',')
end
end
Unfortunately, this doesn't work in that the numbers printed using the <%=%> tag don't appear with commas. (No errors are raised.)
How do I get this right? Does the <%=%> block not automagically call a to_s method on the given object? How does it evaluate what to print?
You're monkey patching the wrong thing, you want to patch Fixnum. For example:
>> class Fixnum
>> def to_s
>> 'pancakes'
>> end
>> end
=> nil
>> 1.to_s
=> "pancakes"
and for ERB:
>> ERB.new('<%= 11 %>').result
=> "pancakes"
That said, your shortcut (like most shortcuts) will probably end up causing you various new and interesting problems elsewhere. You will end up sending '1,000' to something (such as a database or client-side JavaScript or ...) that expects '1000' and you'll get a confusing hissy fit for your efforts. You'll also have to worry about the other numeric classes such as Float and Bignum.
Fix your views, don't kludge around your own laziness.

Formatting a float to a minimum number of decimal places

I'm storing a decimal in rails and I need to have it display as a currency as such:
11.1230 => "$11.123"
11.1000 => "$11.10"
11.0100 => "$11.01"
11.1234 => "$11.1234"
Any easy way to do this?
def pad_number( number, min_decimals=2 )
s = "%g" % number
decimals = (s[/\.(\d+)/,1] || "").length
s << "." if decimals == 0
s << "0"*[0,min_decimals-decimals].max
end
puts [ 11.123, 11.1, 11.01, 11.1234, 11 ].map{ |n| pad_number(n) }
#=> 11.123
#=> 11.10
#=> 11.01
#=> 11.1234
#=> 11.00
Edit: Looks like this is Rails 3 specific, as Rails 2's number_with_precision method doesn't include the strip_insignificant_zeros option:
You can pass some options to number_to_currency (a standard Rails helper):
number_to_currency(11.1230, :precision => 10, :strip_insignificant_zeros => true)
# => "$11.123"
You need to provide a precision in order for the strip_insignificant_zeros option to work, though, otherwise the underlying number_with_precision method never gets called.
If you want to store as a float, you can use the number_to_currency(value) method in yours views for printing something that looks like $.
Correct me if I'm wrong (as I've rarely dealt with currency) but I think the conventional wisdom is to store dollar values as integers. That way you won't have to deal with funky float math.
So, convert it to three decimal fraction digits and then remove the final one if and only if it's a zero.
s.sub(/0$/, '')

Ruby (Rails) unescape a string -- undo Array.to_s

Have been hacking together a couple of libraries, and had an issue where a string was getting 'double escaped'.
for example:
Fixed example
> x = ['a']
=> ["a"]
> x.to_s
=> "[\"a\"]"
>
Then again to
\"\[\\\"s\\\"\]\"
This was happening while dealing with http headers. I have a header which will be an array, but the http library is doing it's own character escaping on the array.to_s value.
The workaround I found, was to convert the array to a string myself, and then 'undo' the to_s. Like so:
formatted_value = value.to_s
if value.instance_of?(Array)
formatted_value = formatted_value.gsub(/\\/,"") #remove backslash
formatted_value = formatted_value.gsub(/"/,"") #remove single quote
formatted_value = formatted_value.gsub(/\[/,"") #remove [
formatted_value = formatted_value.gsub(/\]/,"") #remove ]
end
value = formatted_value
... There's gotta be a better way ... (without needing to monkey-patch the gems I'm using). (yeah, this break's if my string actually contains those strings.)
Suggestions?
** UPDATE 2 **
Okay. Still having troubles in this neighborhood, but now I think I've figured out the core issue. It's serializing my array to json after a to_s call. At least, that seems to be reproducing what I'm seeing.
['a'].to_s.to_json
I'm calling a method in a gem that is returning the results of a to_s, and then I'm calling to_json on it.
I've edited my answer due to your edited question:
I still can't duplicate your results!
>> x = ['a']
=> ["a"]
>> x.to_s
=> "a"
But when I change the last call to this:
>> x.inspect
=> "[\"a\"]"
So I'll assume that's what you're doing?
it's not necessarily escaping the values - per se. It's storing the string like this:
%{["a"]}
or rather:
'["a"]'
In any case. This should work to un-stringify it:
>> x = ['a']
=> ["a"]
>> y = x.inspect
=> "[\"a\"]"
>> z = Array.class_eval(y)
=> ["a"]
>> x == z
=> true
I'm skeptical about the safe-ness of using class_eval though, be wary of user inputs because it may produce un-intended side effects (and by that I mean code injection attacks) unless you're very sure you know where the original data came from, or what was allowed through to it.

Converting filesize string to kilobyte equivalent in Rails

My objective is to convert form input, like "100 megabytes" or "1 gigabyte", and converts it to a filesize in kilobytes I can store in the database. Currently, I have this:
def quota_convert
#regex = /([0-9]+) (.*)s/
#sizes = %w{kilobyte megabyte gigabyte}
m = self.quota.match(#regex)
if #sizes.include? m[2]
eval("self.quota = #{m[1]}.#{m[2]}")
end
end
This works, but only if the input is a multiple ("gigabytes", but not "gigabyte") and seems insanely unsafe due to the use of eval. So, functional, but I won't sleep well tonight.
Any guidance?
EDIT: ------
All right. For some reason, the regex with (.*?) isn't working correctly on my setup, but I've worked around it with Rails stuff. Also, I've realized that bytes would work better for me.
def quota_convert
#regex = /^([0-9]+\.?[0-9]*?) (.*)/
#sizes = { 'kilobyte' => 1024, 'megabyte' => 1048576, 'gigabyte' => 1073741824}
m = self.quota.match(#regex)
if #sizes.include? m[2].singularize
self.quota = m[1].to_f*#sizes[m[2].singularize]
end
end
This catches "1 megabyte", "1.5 megabytes", and most other things (I hope). It then makes it the singular version regardless. Then it does the multiplication and spits out magic answers.
Is this legit?
EDIT AGAIN: See answer below. Much cleaner than my nonsense.
You can use Rails ActiveHelper number_to_human_size.
def quota_convert
#regex = /([0-9]+) (.*)s?/
#sizes = "kilobytes megabytes gigabytes"
m = self.quota.match(#regex)
if #sizes.include? m[2]
m[1].to_f.send(m[2])
end
end
Added ? for optional plural in the regex.
Changed #sizes to a string of plurals.
Convert m[1] (the number to a float).
Send the message m[2] directly
why don't you simply create a hash that contains various spellings of the multiplier as the key and the numerical value as the value? No eval necessary and no regexs either!
First of all, changing your regex to #regex = /([0-9]+) (.*?)s?/ will fix the plural issue. The ? says match either 0 or 1 characters for the 's' and it causes .* to match in a non-greedy manner (as few characters as possible).
As for the size, you could have a hash like this:
#hash = { 'kilobyte' => 1, 'megabyte' => 1024, 'gigabyte' => 1024*1024}
and then your calculation is just self.quota = m[1].to_i*#hash[m2]
EDIT: Changed values to base 2

Resources