Sigh.
[1] hollandaise_party » blank_string = String.new
=> ""
[2] hollandaise_party » price = blank_string.to_d
=> 0.0
[3] hollandaise_party » price.present?
=> true
[4] hollandaise_party » price = ''
=> ""
[5] hollandaise_party » price.present?
=> false
Although I'm not sure what I expected to happen - when trying to figure out why
validates :price, presence: true
was failing when I did not put a value into the input box on my form, turns out the above example is why.
I try not to rely on front end form validations all that much, and build out ActiveRecord validators first. And during the inital stages of testing, I couldn't figure out why that particular validation was failing when I didn't put anything in the input boxes.
Looking into further (I'm bad at source browsing) it looks to be doing something similar to Ruby's to_f
method (even though I can't confirm it in the Rails/Rails repo).
If there is not a valid number at the start of str, 0.0 is returned.
So now we know if a blank string is passed to price.to_d
method we're going to get 0.0
. Well we could just do a check to see if it's blank? price.to_d if price.present?
which would technically work. But let's say a user passed in text to our input boxes, instead of an Integer. At that point, the previous example is going to fail.
[6] hollandaise_party » price = "loltext"
=> "loltext"
[7] hollandaise_party » price.present?
=> true
[8] hollandaise_party » price.to_d
=> 0.0
Balls.
There were a few ideas floating around on StackOverflow on how to deal with this - most people opted for regex. However, a more clever solution which I think works better than regex was this:
def is_a_number?(value)
true if Float(value) rescue false
end
Which works for just about nearly any value you pass to it.
[9] hollandaise_party » Float('-1000.00')
=> -1000.0
[10] hollandaise_party » Float('1000')
=> 1000.0
The only thing I found an issue with was if a comma was included in the value.
[11] hollandaise_party » Float('1,000.00')
ArgumentError: invalid value for Float(): "1,000.00"
from (pry):11:in `Float'
Easy enough solution for that, just .tr
(source) the string and remove the comma, then pass that number to the Float(value)
. The whole method that I ended up using looks like this.
def is_a_number?(value)
number = value.tr(',','')
true if Float(number) rescue false
end
Now! Back to our validations! Should look something like this.
price.to_d if is_a_number?(price)
Shazam.
If there is a better way to do this let me know!